Welcome to Sming Framework

Let’s do smart things!!!

Sming is an asynchronous embedded C/C++ framework with superb performance and multiple network features. Sming is open source, modular and supports multiple architectures including ESP8266 and ESP32.

samples

Summary

  • Superb performance and memory usage (Sming compiles to native firmware!)

  • Fast and user friendly development

  • Integrated host emulator to assist with developing, testing and debugging libraries and applications on a PC before uploading them to an actual microcontroller. Try it out online here.

  • Built-in powerful wireless modules

  • Compatible with standard Libraries - use popular hardware in few lines of code

  • Simple yet powerful hardware API wrappers

    • Standard Arduino-style wrappers for simplicity and flexibility

    • HardwareSPI library provides advanced asynchronous SPI device master capability including use of ESP8266 ‘overlapped’ mode for reduced pin usage and dual/quad I/O support.

    • Sming Graphics Library implements asynchronous display control model with low memory usage, transparency (alpha-blending) and flexible scripted resource management.

  • Modular C++ installable file system interface

    • Supports popular SPIFFS and LittleFS filing systems

    • New FWFS read-only filesystem can be used as base file system, with read/write filesystems mounted in sub-directories

    • Integrated metadata support (file times, security descriptors, user metadata)

    • Access to Host (linux / Windows / MacOS) filing system via same API

    • Integrated streaming archival system to support backups or file system compaction operations

  • Powerful asynchronous (async) network stack including:

    • Async TCP and UDP stack based on LWIP.

    • With clients supporting: HTTP, MQTT, WebSockets and SMTP.

    • And servers for: DNS, FTP, HTTP(+ WebSockets), Telnet.

    • With SSL support for all network clients and servers. Based on axTLS and BearSSL.

    • Over-The-Air (OTA) firmware upgrades via HTTP(S) and MQTT(S).

  • Universal Plug and Play (UPnP) framework supports both standard and custom devices with full ControlPoint support.

    • Generates C++ code from standard UPnP XML schema.

    • Companion SSDP library independently supports discovery on local network.

    • GoogleCast library allows control of ChromeCast devices or smart TVs supporting the GoogleCast protocol.

    • Hue Emulator provides simple way to implement devices compatible with Amazon Alexa.

  • ESP8266 features

  • ESP32 features

Getting Started

Development System Installation

Choose your preferred development environment for how to install the needed development software and toolchain(s):

Linux Installation
Quick Install

Debian (Ubuntu) and Fedora systems can use the scripted installer.

  1. Prepare installation directory

    Let’s use /opt as the main directory for tools and Sming.

    Regular users may not have access to /opt, so do this:

    sudo chown $USER:$USER /opt
    

    (alternatively, use a different directory).

  2. Install GIT

    Debian:

    sudo apt-get install -y git
    

    Fedora:

    dnf install -y git
    
  3. Fetch the Sming repository

    git clone https://github.com/SmingHub/Sming /opt/sming
    
  4. Run the installer

    source /opt/sming/Tools/install.sh all
    

If you want to save disk space then you can select which tools to install. Get a list of available options like this:

/opt/sming/Tools/install.sh

Install locations can be customised by setting environment variables before running the install. Certain variables should also be set globally. See Configuration for details.

If you want to use the stable (release) branch:

cd /opt/sming
git checkout master
git pull
Next steps

Proceed to Configuration.

Mac-OS Installation
Pre-requisites

(You might already have it)

Xcode command line tools
xcode-select --install
Homebrew
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Eclipse

(Optional):

brew install Caskroom/cask/eclipse-cpp
Build tools

Required for the makefile build system:

brew install binutils coreutils automake wget gawk libtool gettext gperf grep
export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"

Install gnu-sed and make sure that it is the default sed command line application:

brew install gnu-sed --with-default-names

If you have already installed gnu-sed but it is not the default one, then make sure to uninstall it and install it again with the correct options:

brew uninstall gnu-sed
brew install gnu-sed --with-default-names
ESP8266 Toolchain

We pull this in from the SmingTools repository:

cd ~/
export ESP_HOME=/opt/esp-quick-toolchain
curl -LO https://github.com/SmingHub/SmingTools/releases/download/1.0/x86_64-apple-darwin14.xtensa-lx106-elf-e6a192b.201211.tar.gz
sudo mkdir -p $ESP_HOME
sudo tar -zxf x86_64-apple-darwin14.xtensa-lx106-elf-e6a192b.201211.tar.gz -C $ESP_HOME
sudo chmod -R 775 $ESP_HOME

You can also build it yourself with Homebrew or with MacPorts.

Get Sming Core

Clone from the Sming repository:

cd <your-favourite-development-folder>/
git clone https://github.com/SmingHub/Sming.git
cd Sming

Warning

Do NOT use the –recursive option for the command above. Our build mechanism will take care to get the third-party sources and patch them, if needed.

You will get a copy of our develop branch which is intended for developers. It is the one where all new cool (unstable) features are landing.

If you want to use our stable branch then use the master branch:

git checkout origin/master

Finally, set the SMING_HOME environment variable to point to <your-favourite-development-folder>/Sming/Sming:

export SMING_HOME=`pwd`
Environment Variables

Open with a text editor the .profile file in your home directory, and add these lines:

export ESP_HOME=/opt/esp-quick-toolchain
export SMING_HOME=<your-favourite-development-folder>/Sming/Sming

Make sure to replace <your-favourite-development-folder> in the command above with the actual directory on your local disk.

(Optional step)

(used by Make and Eclipse - make sure to quit Eclipse first)

If you installed Eclipse manually, substitute /opt/homebrew-cask/Caskroom/eclipse-cpp/4.5.1/Eclipse.app to /Applications/Eclipse/eclipse.app:

sudo /usr/libexec/PlistBuddy -c "Add :LSEnvironment:ESP_HOME string '/opt/esp-open-sdk'" /opt/homebrew-cask/Caskroom/eclipse-cpp/4.5.1/Eclipse.app/Contents/Info.plist
sudo /usr/libexec/PlistBuddy -c "Add :LSEnvironment:SMING_HOME string '/opt/sming/Sming'" /opt/homebrew-cask/Caskroom/eclipse-cpp/4.5.1/Eclipse.app/Contents/Info.plist
sudo /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -v -f /opt/homebrew-cask/Caskroom/eclipse-cpp/4.5.1/Eclipse.app
Using Eclipse on Mac

If you are using Eclipse to build the samples you need to make sure the path is set correctly for the make process.

Your project must also be configured to use the correct serial port for your ESP8266. You can change it like this:

make COM_PORT=/dev/tty.usbserial

Next, ensure that you can build the Basic Blink from a terminal window:

cd $SMING_HOME/../samples/Basic_Blink
make

This will also build the required framework components, so may take a few minutes. If this works without errors then type echo $PATH and copy the resulting path to the clipboard.

Now fire up Eclipse and go to

Eclipse ==> Preferences ==> C/C++ ==> Build ==> Environment

and add a new variable PATH. Paste in the path saved from the terminal session above. You can also add SMING_HOME and ESP_HOME variables here the same way as you set in the export commands above which will then be set for all the projects.

The standard make files use miniterm.py to provide a serial Terminal for debugging the ESP8266. Miniterm does not work inside Eclipse so you should disable it like this:

make KILL_TERM= TERMINAL=

This will prevent Eclipse from trying to launch miniterm and throwing an error about Inappropriate ioctl for device.

You can use the built in terminal in Eclipse Oxygen by adding it using

Window ==> Show View ==> Terminal

then setting terminal type to Serial and setting the port to the port the ESP8266 is connected to. Remember to disconnect before tying to re-flash the device though.

Compile Sming Examples

See Sample Projects for a list of all examples provided with Sming:

cd $SMING_HOME/../samples/

If you want to test some of the examples, try this:

cd $SMING_HOME/../samples
cd Basic_Blink
make
# The command below will upload the sample code to your ESP8266 device
make flash
Next steps

Proceed to Configuration.

Windows Installation

This page describes how to install the required tools and obtain the current release version of Sming using the Chocolatey package manager.

See also:

Manual Windows Installation
MinGW

Code built for the ESP8266 uses a separate Espressif compiler, but Components such as SPIFFS require additional tools which are built as Windows executable applications.

MinGW provides a (mostly) POSIX-compliant development environment for Windows, including GNU Make and various other command-line tools.

Note

There are two versions of MinGW.

MinGW is the original, and the version recommended for use with Sming.

MinGW-w64 was forked from MinGW in 2007 in order to provide support for 64 bits and new APIs. (Which we don’t need!)

To find out if you already have GCC on your system, type:

where gcc

If it shows C:\MinGW\bin\gcc.exe then you have a standard MinGW installation. Check the gcc version:

gcc --version

The current version is 8.2.0. You can upgrade by renaming or removing your existing installation then following these instructions.

Fast install
  1. Download MinGW.7z from the SmingTools repository.

  2. Unzip to default location:

    7z -oC:\ x MinGW.7z
    

    Note

    You can obtain 7-zip from https://www.7-zip.org/.

  3. Update PATH environment variable:

    SETX PATH "C:\mingw\bin;C:\mingw\msys\1.0\bin;%PATH%"
    

    Make sure it is in this exact order. If you have Cygwin installed make sure the above entries appear first.

    You will need to restart your command prompt (and Eclipse, if already running) for these changes to take effect.

Alternative install

To install from the original MinGW source repository:

  1. Get the MingGW Setup and run it. This will create the C:\MinGW directory with minimal content.

  2. Set PATH environment variable as above.

  3. Install required MinGW packages:

    mingw-get install mingw32-base-bin mingw-developer-toolkit-bin mingw32-gcc-g++-bin mingw32-pthreads-w32-dev mingw32-libmingwex
    

    Note that you can upgrade packages using:

    mingw-get update
    mingw-get upgrade
    

    However this will not upgrade a 6.3.0 installation to 8.2.0.

Install ESP8266 Toolchain
  1. Download toolchain ESP Quick Toolchain.

  2. Unzip to default location:

    7z -oC:\tools\esp-quick-toolchain x x86_64-w64-mingw32.xtensa-lx106-elf-e6a192b.201211.zip
    
  3. Set ESP_HOME environment variable:

    SETX ESP_HOME C:\tools\esp-quick-toolchain
    

Note

There is NO trailing slash on the path!

If you want to set environment variables system-wide, append /M to the command. You’ll need to do this from an administrative command prompt.

Install Python

Get the current version from https://www.python.org/.

By default, python gets installed here:

C:\Users\xxx\AppData\Local\Programs\Python\Python38\python.exe

Wherever it ends up will either need to be in the path:

setx PATH "C:\Users\xxx\AppData\Local\Programs\Python\Python38;%PATH%"

or located using the PYTHON environment variable:

setx PYTHON "C:\Users\xxx\AppData\Local\Programs\Python\Python38"

Important

The PYTHON variable may not contain spaces. This is a MinGW restriction.

Install GIT

This is required to fetch and update Sming code from its repository.

  1. Install command-line GIT client.

These steps are optional, but highly recommended:

  1. Install Graphical client Git Extensions.

  2. Create an account at https://github.com. This will allow you to participate in discussions, raise issues and, if you like, Contributing to the framework!

Download Sming
  1. You can put Sming anywhere convenient, provided there are no spaces in the path! For example, C:\tools\sming:

    mkdir C:\tools
    cd /d C:\tools
    
  2. To obtain the latest develop code with all the latest features and fixes:

    git clone https://github.com/SmingHub/Sming
    

    To obtain the latest release:

    git clone https://github.com/SmingHub/Sming --branch master
    
  3. Set SMING_HOME environment variable:

    SETX SMING_HOME C:\tools\Sming\Sming
    

    Note: there is NO trailing slash on the path!

Note

Whilst Windows filenames are not (by default) case-sensitive, the compiler tools are.

Please take care to type paths exactly as shown.

At this stage you should be able to build a sample:

cd samples\Basic_Blink
make -j

If you want to try out the Host emulator, do this:

make -j SMING_ARCH=Host

For build options:

make help
Install Eclipse IDE

Whilst building and configuring your application is generally easier and faster using the command prompt, developing and debugging code is greatly simplified using an Integrated Development Environment (IDE).

  1. Install Java Runtime Environment.

  2. Install Eclipse IDE for C++ Developers.

  3. Start Eclipse IDE. When prompted, enter C:\tools\sming as the workspace path.

  4. Select File -> Import -> General -> Existing Project into Workspace. In the line Select root directory, select the directory C:\tools\sming\Sming and import everything.

  5. Go have a cup of coffee while Eclipse scans all the source code. It can take a while!

  6. To build a project, right-click and select Build project. Alternatively, select the project and press F9.

Eclipse IDE variables

The only variable you should need to set within Eclipse is SMING_HOME. You can set this within the Eclipse IDE via Window > Preferences -> C/C++ > Build > Environment.

If you set this via global environment variable before starting Eclipse then this step is not necessary.

Note

Variables set within the IDE won’t be accessible in other Eclipse sessions or the command prompt.

All other configuration should be done either in your project’s component.mk file or via command line.

For example, to switch to a Host emulator build, do this:

make SMING_ARCH=Host list-config

This also displays the current configuration settings. Whether you build from command line or Eclipse, the same settings will be used.

Next steps

Proceed to Configuration.

WSL

Building under Windows is generally slower than in Linux. This is because the current build system requires a Posix emulation layer (MinGW). However, it does offer the simplest way to use Sming on a Windows PC and does not affect the quality or functionality of your applications.

There are situations where it is highly desirable to build Sming in a Linux environment:

  • Making use of linux-only development tools, e.g. valgrind (dynamic bug detection system)

  • Integration building/testing prior to submitting a PR to the Sming repository

  • Need/want faster builds

Whilst a Windows computer can be configured to dual-boot with Linux, this is generally inconvenient for day-to-day use. A better solution is to run Linux inside a virtual machine environment such as VirtualBox, VmWare or Hyper-V.

Note that Docker is not a virtual environment but can in fact be run inside a virtual machine to take advantage of the process isolation and security benefits.

Windows Subsystem for Linux

https://docs.microsoft.com/en-us/windows/wsl/

“The Windows Subsystem for Linux lets developers run a GNU/Linux environment – including most command-line tools, utilities, and applications – directly on Windows, unmodified, without the overhead of a traditional virtual machine or dual-boot setup.”

There are currently two versions of WSL: this documentation relates to WSL2.

Note

WSL2 uses Hyper-V so may conflict with other virtual machines you may be using.

Installing WSL

See instructions here https://docs.microsoft.com/en-us/windows/wsl/install.

Install an up-to-date Linux distribution from the Microsoft store, currently Ubuntu-20.04.

Note

You may encounter an error message similar to this during installation:

WslRegisterDistribution failed with error: 0x80370102
Error: 0x80370102 The virtual machine could not be started because a required feature is not installed.

One thing not mentioned in the instructions is to check that the hypervisor is set to auto-start at system boot. This is the default but for various reasons it can get disabled.

To check, type:

bcdedit

At an administrative command prompt. Under the Windows Boot Loader entry you should see an entry like this:

hypervisorlaunchtype    Auto

If it’s missing or set to another value (e.g. off) then change it as follows:

bcdedit /set {current} hypervisorlaunchtype auto

After a system reboot you should be able to continue with the installation.

Installing Sming

Open a WSL command prompt and follow the instructions in Linux Installation.

Please note:

  • A native Windows python3 distribution is required to enable access to serial ports.

  • Ensure that python is available in the system path for both WSL2 and Windows.

  • Do not set the PYTHON environment variable.

This will ensure that the build system can run python scripts either in WSL2 or in Windows as necessary.

Flashing devices

WSL2 does not currently support access to USB serial devices, so the Sming build system runs the appropriate application directly under Windows using powershell.exe.

Therefore, use the normal Windows COM port name rather than the linux ones (such as /dev/ttyUSB0):

make flash COM_PORT=COM4
Serial debugging

Again, as we have no direct access to USB COM ports a workaround is required. A small python application can be run on Windows to act as a simple bridge between the serial port and a TCP port. See Tools/tcp_serial_redirect.py - run without arguments to see available options.

You can start the server like this:

make tcp-serial-redirect

A new console will be created (minimised) showing something like this:

--- TCP/IP to Serial redirect on COM4  115200,8,N,1 ---
--- type Ctrl-C / BREAK to quit
Waiting for connection on 192.168.1.101:7780...

This uses the current COM_PORT and COM_SPEED_SERIAL settings.

Now we can start the debugger:

make gdb COM_PORT_GDB=192.168.1.101:7780
Valgrind

You may get an error running make valgrind advising that libc6-dbg:i386 be installed. Here’s how:

sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libc6-dbg:i386
Quick Install

Open an administrative cmd.exe command prompt and paste the text from the box below and press enter:

curl -LO https://raw.githubusercontent.com/SmingHub/Sming/develop/Tools/choco-install.cmd && choco-install.cmd

At the moment the Esp32 toolchain is not installed by default. If you want to install it run the following command:

choco install -y sming.esp32

Important

After installation, please close the administrative command prompt and open a new, regular command shell.

This ensures that environment variables are set correctly.

It is also inadvisable to continue running with elevated privileges.

If you followed and executed carefully the steps above Sming should be installed and configured. You can scroll down to Build Basic_Blink to check the installation.

Optional step: Switch to stable version

The installer uses the latest develop branch. This one contains great new features and bugfixes but can be unstable at times.

Switching to our stable release will guarantee you that Sming’s code will not change so often. On the downside you will have to wait for all new shiny features and bugfixes.

If you really want to use the latest stable release you can type the command below:

cd %SMING_HOME%
git checkout master
git pull
Optional step: Re-installation

In case something is broken, this will perform a forced re-install of all packages:

rmdir /s /q c:\tools\sming
choco install sming -y -f -x
Packages

You can find the installer sources at https://github.com/slaff/chocolatey-packages. Packages are as follows:

git

GIT CLI client.

Please configure after installation to leave line-endings intact or else patching will fail:

git config --global core.autocrlf input
python

Python version 3.

cmake

CMake.

Required to build some Components, also for Host mode.

mingw

MinGW 32-bit.

The installer updates the system PATH but please check by running:

where make.exe

The output should show only one result:

"C:\MinGW\msys\1.0\bin\make.exe"
esp8266-eqt

ESP Quick Toolchain.

Sets a system-wide ESP_HOME variable.

sming.esp32

Sming-compatible version of ESP-IDF and tools.

sming.core

Latest stable version of Sming.

Sets a system-wide SMING_HOME environment variable.

Note that setting SMING_HOME and ESP_HOME as system-wide variables means they do not need to be set every time a command prompt is opened, and will be seen by eclipse without any further configuration.

Next steps

Proceed to Configuration.

Docker

Docker is a useful tool if you want to experiment with Sming in an isolated environment.

If you’re unfamiliar with Docker, you can find a good overview in the article, What is a Container?.

This page shows how to create docker containers with all the necessary tools to build Sming applications.

Install Docker

Visit the official Docker Installation Page and follow the instructions tailored for your operating system.

Install docker-compose

Docker Compose makes dealing with the orchestration processes of Docker containers (such as starting up, shutting down, and setting up intra-container linking and volumes) really easy.

With docker compose we can define the entire multi-container application in a single file and spin it up using one command.

Visit the official Docker Compose Installation Page and follow the instructions tailored for your operating system.

Building images

You can find the related Docker scripts in $SMING_HOME/Tools/Docker.

To build your own images, do this:

cd $SMING_HOME/Tools/Docker/cli
docker-compose build
cd $SMING_HOME/Tools/Docker/ide
docker-compose build
Adjust your settings
sming-ide:

  build: .

  volumes:
    - ../../Sming/:/opt/sming/

 ports:
 #choose a free port to connect to the web C9 editor
 - "10080:80"

 devices:
   # uncomment to map your serial USB adaptor
   #- "/dev/ttyUSB0:/dev/ttyUSB0"

 privileged: true
Start your container
cd /opt/sming/Tools/Docker/ide && docker-compose up -d
Open your browser

http://localhost:10080

_images/c9-1.png
cd /opt/sming/samples/Basic_Blink
make
make flash

You can also try Sming without installing anything locally. We have an interactive tutorial that can be run directly from your browser.

Configuration
Environment variables

Certain environment variables should be set globally so that command prompts and integrated development environments (IDEs) work correctly.

You can find a list of these in Tools/export.sh.

For Linux and WSL2, append values to your ~/.bashrc file:

# All architectures
export SMING_HOME=/opt/sming/Sming

# Esp8266
export ESP_HOME=/opt/esp-quick-toolchain

# Esp32
export IDF_PATH=/opt/esp-idf
export IDF_TOOLS_PATH=/opt/esp32
export ESP32_PYTHON_PATH=/usr/bin

# Rp2040
export PICO_TOOLCHAIN_PATH=/opt/rp2040

Another approach is to place these in a separate file, then add source ~/.smingrc.

For Windows, you can either edit the global variables graphically via control panel, or use the setx command:

REM All architectures
setx SMING_HOME "c:\tools\sming\Sming"

REM Esp8266
setx ESP_HOME "c:\tools\esp-quick-toolchain"

REM Esp32
setx IDF_PATH "c:\tools\esp-idf"
setx IDF_TOOLS_PATH "c:\tools\esp32"
setx ESP32_PYTHON_PATH "c:\Python39"

REM Rp2040
setx PICO_TOOLCHAIN_PATH "c:\tools\rp2040"

In both cases values will only take effect in new sessions so close/reopen command prompts or IDEs.

Note that project-specific settings should NOT be configured globally. Please use the project’s component.mk file for that purpose.

Configuring your device

You may need to configure your project to support the specific device being programmed:

  • COM_PORT If you haven’t set this already, it will need to match the port you’re using to talk to the target device.

  • COM_SPEED_ESPTOOL The default value should work fine but you can usually set a much faster speed.

You can change these on the command line:

make SMING_ARCH=Esp8266 COM_PORT=/dev/ttyUSB3 COM_SPEED_ESPTOOL=921600

For Windows or WSL expect to use COM2, COM3, etc.

Once you’re happy with the settings, you can add them to your project’s component.mk file. You may need to do this to reset the cached values:

make config-clean

The current set of configuration variables can be seen thus:

make list-config

Other hardware-specific settings are stored in the hardware configuration file. You can examine the current configuration like this:

make hwconfig

The standard config should work with all ESP8266 variants. If you want to use SPIFFS then you should add this line to your component.mk file:

HWCONFIG = spiffs

This expects your device to have at least 4MBytes of flash.

Documentation

In addition to our online documentation, you can also generate a complete documentation locally by following these instructions.

Examples

The examples are a great way to learn the API and brush up your C/C++ knowledge.

Further documentation about the Sample Projects is available too.

Simple GPIO input/output
#define LED_PIN 2 // GPIO2
...
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);

For a complete example take a look at the Basic Blink sample.

Start Serial communication
Serial.begin(9600);
Serial.println("Hello Sming! Let's do smart things.");
Connect to WiFi
WifiStation.enable(true);
WifiStation.config("LOCAL-NETWORK", "123456789087"); // Put your SSID and password here
Read DHT22 sensor
#include <Libraries/DHTesp/DHTesp.h> // This is just a popular Arduino library!

#define DHT_PIN 0 // GPIO0
DHTesp dht;

void init()
{
  dht.setup(DHT_PIN, DHTesp::DHT22);

  float h = dht.getHumidity();
  float t = dht.getTemperature();
}

Take a look at the code of the DHT22 Humidity Sensor sample.

HTTP client
HttpClient thingSpeak;
...
thingSpeak.downloadString("http://api.thingspeak.com/update?key=XXXXXXX&field1=" + String(sensorValue), onDataSent);

void onDataSent(HttpClient& client, bool successful)
{
  if (successful) {
    Serial.println("Successful!");
  }
  else {
    Serial.println("Failed");
  }
}

For more examples take a look at the HTTP Client, HttpClient Instapush and ThingSpeak Http Client samples.

OTA application update
void doUpgrade()
{
  // need a clean object, otherwise if run before and failed will not run again
  if(otaUpdater) {
      delete otaUpdater;
  }
  otaUpdater = new Ota::Network::HttpUpgrader();

  // select rom partition to flash
  auto part = ota.getNextBootPartition();

  // The content located on ROM_0_URL will be stored to the new partition
  otaUpdater->addItem(ROM_0_URL, part);

  // and/or set a callback (called on failure or success without switching requested)
  otaUpdater->setCallback(upgradeCallback);

  // start update
  otaUpdater->start();
}

For a complete example take a look at the Basic Ota sample.

Embedded HTTP Web Server
server.listen(80);
server.paths.set("/", onIndex);
server.paths.set("/hello", onHello);
server.paths.setDefault(onFile);

Serial.println("=== WEB SERVER STARTED ===");
Serial.println(WifiStation.getIP());

...

void onIndex(HttpRequest &request, HttpResponse &response)
{
  TemplateFileStream *tmpl = new TemplateFileStream("index.html");
  auto &vars = tmpl->variables();
  vars["counter"] = String(counter);
  vars["IP"] = WifiStation.getIP().toString();
  vars["MAC"] = WifiStation.getMacAddress().toString();
  response.sendTemplate(tmpl);
}

void onFile(HttpRequest &request, HttpResponse &response)
{
  String file = request.getPath();
  if (file[0] == '/')
    file = file.substring(1);

  response.setCache(86400, true);
  response.sendFile(file);
}

For more examples take a look at the HttpServer Config Network, Bootstrap Http Server, HttpServer Websockets and AJAX Http Server samples.

Email client
SmtpClient emailClient;

emailClient.connect(Url("smtp://user:password@domain.com"));

MailMessage* mail = new MailMessage();
mail->from = "developers@sming";
mail->to = "iot-developers@world";
mail->subject = "Greetings from Sming";
mail->setBody("Hello");

FileStream* file= new FileStream("image.png");
mail->addAttachment(file);

emailClient.onMessageSent(onMailSent);
emailClient.send(mail);

...

int onMailSent(SmtpClient& client, int code, char* status)
{
    MailMessage* mail = client.getCurrentMessage();

    ...

    if(client.countPending() == 0) {
        client.quit();
    }

    return 0;
}

See the SMTP Client sample for details.

Live Debugging

Applications based on Sming Framework that are flashed and running on an ESP8266 device can be debugged using interactive debuggers. In order to debug an application it has to be re-compiled with the ENABLE_GDB=1 directive. And then flashed on the device. As shown below:

cd $SMING_HOME/../samples/LiveDebug
make clean
make ENABLE_GDB=1
make flashapp # <-- this will update only the application firmware.

Once the debuggable application is flashed on the device the developers have to run GDB. The easiest way to run the command-line GDB is to execute the following command:

make gdb

Developers using Eclipse CDT can have debugging sessions like the one below:

See Live Debug sample for details.

Features

There are multiple custom features that can be enabled by default. For example: SSL support, custom LWIP, open PWM, custom heap allocation, more verbose debugging, etc.

The features available are dependent upon the architecture for which Sming is being built.

Sming (main)

This is the main Sming Component containing all architecture-independent code. All architecture-specific stuff is in either Sming (Esp8266) or Sming (Host).

Configuration variables
Serial Communications
COM_SPEED

Default baud rate for serial port.

This will recompile your application to use the revised baud rate. Note that this will change the default speed used for both flashing and serial comms. See also Esptool and Terminal for further details.

The default rate for serial ports is 115200 baud. You can change it like this:

make COM_SPEED=921600
Debug information log level and format
DEBUG_VERBOSE_LEVEL

When compiled in debug mode (:envvar:SMING_RELEASE undefined) there are four debug levels in increasing level of verbosity:

  • 0: errors

  • 1: warnings

  • 2: information (default)

  • 3: debug

Change it like this:

make DEBUG_VERBOSE_LEVEL=3
DEBUG_PRINT_FILENAME_AND_LINE

Set this to 1 to include the filename and line number in every line of debug output. This will require extra space on flash.

Note

If you change these settings and want them applied to Sming, not just your project, then you’ll need to recompile all components like this:

make components-clean
make DEBUG_VERBOSE_LEVEL=3
Release builds
SMING_RELEASE

By default, this value is undefined to produce a build with debug output. To build for release, do this:

make SMING_RELEASE=1

This remains in force until you change it back:

make SMING_RELEASE=
Localisation
LOCALE

Sming can format dates/time values based on a country code identified by this value. This is provided as a #define symbol for your application to use. See Sming/Core/SmingLocale.h for further details.

Networking
DISABLE_NETWORK

0 (Default) 1 - Remove core networking support

Applications which do not require networking can set this flag to avoid building or linking the core Networking Support library.

This will reduce build times, application size and RAM usage. Builds will not succeeded if network code has been inadvertently included.

DISABLE_WIFI

Note

EXPERIMENTAL

0 (Default) 1 - Exclude WiFi initialisation code

Keeps the core Networking Support library but excludes WiFi code. Applications using ethernet can use this to reduce code size. See Basic Ethernet.

Components
FlashString
Introduction

This is a C++ library to simplify the definition and use of data structures stored in program (flash) memory in an embedded microcontroller.

It was developed for use with Sming and the ESP8266 but could be ported to other platforms relatively easily.

Perhaps the most common use for PROGMEM data is passing strings around, usually as a pointer to a ‘C’ NUL-terminated char array. There are a couple of problems with this:

  1. If we need to know how long the string is, we need to call strlen_P(), which is really expensive computationally.

  2. We can’t include NUL characters in the string.

Both of these are easily solved by passing the length along with the string, like this:

char myString[] PROGMEM = "hello, this is a string";
Serial.println(FPSTR(myString), sizeof(myString) - 1);

Of course, passing two parameters instead of one gets tiresome and is not very C++, is it?

This library implements C++ objects stored in flash memory, using macros to create the data structures. The object interfaces are implemented using class templates for performance and flexibility.

The classes are all in the FSTR namespace.

Objects
Introduction

An FSTR::Object is a class template with array-like behaviour, though it is not used directly.

Instead, use one of the four classes in the library:

Each type has its own set of macros for easy data construction, and creation of the appropriate Object class which may then be used directly.

Macros follow the same pattern:

DEFINE_FSTR_*

Creates a static data structure with an associated Object reference. The _LOCAL variant makes the reference static constexpr.

DECLARE_FSTR_*

Use this in a header to declare an Object reference so it can be used across translation units.

Created symbols are C++ and adopt any enclosing namespaced.

Reading Object content

To read parts of an Object, use the FSTR::Object::read() method.

If the data isn’t used very often, use the FSTR::Object::readFlash() method instead as it avoids disrupting the cache. The FSTR::Stream class (alias FlashMemoryStream) does this by default.

Object Internals

This section provides some examples of how structures are created, but in normal use you should use the provided macros as they simplify the task and include structure validity checks.

FSTR::ObjectBase is a non-template POD base class, and looks like this (methods omitted):

class ObjectBase {
   uint32_t flashLength_;
   // uint8_t data[];
};

Attention

flashLength_ must not be accessed directly; use the length() method instead.

Data structures are created like this:

constexpr const struct {
   ObjectBase object;
   char data[8];
} flashHelloData PROGMEM = {
   {5},
   "hello"
};

The object field may then be cast to a reference of the required type, like this:

auto& str = flashHelloData.object.as<FSTR::String>();

If you want to access it as an array, do this:

auto& arr = str.as<FSTR::Array<char>>();

References are an efficient and convenient way to access an Object, and should not consume any memory themselves as the compiler/linker resolve them to the actual object.

However, in practice the Espressif compiler stores a full pointer to most things to support relative addressing, and if the references aren’t declared PROGMEM they’ll consume RAM.

Copy behaviour

Whilst references are the preferred way to access flash Objects, they can also be created dynamically:

FSTR::String emptyString;
FSTR::String stringCopy(FS("Inline string"));

Such instances are stored in RAM but only consume 4 bytes as they simply keep a pointer to the real flash Object.

Note

Don’t try to copy ObjectBase!

Here’s a somewhat contrived example to demonstrate:

DEFINE_FSTR_DATA_LOCAL(flashHelloData, "Hello");
auto myCopy = flashHelloData.object;
Serial.print("myCopy.length() = ");
Serial.println(myCopy.length());

In debug builds, this will throw an assertion. In release builds, you’ll get a zero-length object.

Aggregate initialization

We use aggregate initialization to set up the structures so the data is fixed at link time without any constructor or initialiser functions.

This means classes cannot have:

  • user-provided constructors

  • brace-or-equal-initializers for non-static data members

  • private or protected non-static data members

  • virtual functions

  • base classes (until C++17)

This is why FSTR::ObjectBase is used to define data structures.

Classes created using the FSTR::Object template ensures the necessary constructors are available to do this:

auto myCopy = flashHelloData.object.as<FSTR::String>();
Serial.print("myCopy.length() = ");
Serial.println(myCopy.length());

The macros create an appropriate Object& reference for you.

Structure checks

The construction macros include a sanity check to ensure the initialization is truly just Plain Old Data, without any hidden initialisers.

You may encounter one of the following errors during compilation:

  • The value of ‘X’ is not usable in a constant expression

  • FSTR structure not POD

This generally means one or more of the arguments in the initialisation data is not constexpr. Most compilers are quite relaxed about this but GCC 4.8.5 is particularly thick.

In testing, this happens with references for global Objects, which of course cannot be constexpr. To fix it, the offending Object either needs to be redefined LOCAL, or if the Object data is in scope (i.e. defined in the same source file) then you can get a direct pointer to it using the FSTR_PTR() macro.

Macros
DECLARE_FSTR_OBJECT(name, ObjectType)

Declare a global Object reference.

Parameters:
  • name

  • ObjectType

DEFINE_FSTR_REF(name, ObjectType, object)

Define a reference to an object.

Parameters:
  • name – Name for reference

  • ObjectType – Fully qualified typename of object required, e.g. FSTR::String, FlashString, FSTR::Vector<int>, etc.

  • object – Object instance to cast

DEFINE_FSTR_REF_NAMED(name, ObjectType)
FSTR_DATA_NAME(name)

Provide internal name for generated flash string structures.

FSTR_PTR(objref)

Given an Object& reference, return a pointer to the actual object.

However, some older compilers such as GCC 4.8.5 requires such references to be declared constexpr. For example, this fails with FSTR structure not POD:

    DEFINE_FSTR(globalStringRef, "This creates a global reference");
    DEFINE_VECTOR(myVector, FSTR::String, &globalStringRef);
                                       ^^^
Global references cannot be declared constexpr, so changing DEFINE_FSTR to DEFINE_FSTR_LOCAL will fix the problem.

Another solution is to get a direct pointer to the actual data structure:

    DEFINE_VECTOR(myVector, FSTR::String, FSTR_PTR(globalStringRef));
We can only do this of course if the data structure is in scope.

Parameters:
  • objref – When an Object pointer is required, such when defining entries for a Vector or Map, it is usually sufficient to use &objref.

FSTR_CHECK_STRUCT(name)

Check structure is POD-compliant and correctly aligned.

IMPORT_FSTR_OBJECT(name, ObjectType, file)

Import an object from an external file with reference.

See also

See also IMPORT_FSTR_DATA

Note

Can only be used at file scope

Parameters:
  • name – Name for the object

  • ObjectType – Object type for reference

  • file – Absolute path to the file containing the content

IMPORT_FSTR_OBJECT_LOCAL(name, ObjectType, file)

Like IMPORT_FSTR_OBJECT except reference is declared static constexpr.

Class Template
template<class ObjectType, typename ElementType>
class Object : public FSTR::ObjectBase

Base class template for all types.

Template Parameters:
  • ObjectType – The object type actually being instantiated

  • ElementType

Public Functions

inline Object()

Creates a null object.

inline Object(const Object &obj)

Copy constructor.

Note

Objects are usually passed around by reference or as a pointer, but for ease of use we need a working copy constructor.

inline size_t length() const

Get the length of the content in elements.

inline ElementType operator[](unsigned index) const

Array operator[].

inline size_t read(size_t index, ElementType *buffer, size_t count) const

Read content into RAM.

Parameters:
  • index – First element to read

  • buffer – Where to store data

  • count – How many elements to read

Return values:

size_t – Number of elements actually read

inline size_t readFlash(size_t index, ElementType *buffer, size_t count) const

Read content into RAM,using flashmem_read()

Parameters:
  • index – First element to read

  • buffer – Where to store data

  • count – How many elements to read

Return values:

size_t – Number of elements actually read

Public Static Functions

static inline const ObjectType &empty()

Return an empty object which evaluates to null.

Flash Strings
Introduction

Strings are basically just arrays of char, but have additional methods to allow them to be used more easily. These methods are consistent with Wiring String, so should be reasonably familiar.

  • length() returns the number of characters in the String, excluding the NUL terminator

  • size() returns the number of bytes of storage used

For example, “123” is actually stored as { '1', '2', '3', '\0' } so the length is 3 and the size is 4. However, “1234” is stored as { '1', '2', '3', '4', '\0' } so the length is 4 and the size is 8.

Using Strings

Note

You can use FSTR::String or the Sming-provided FlashString alias to work with Strings.

Within a function:

DEFINE_FSTR_LOCAL(myFlashString, "This is my flash string");

Serial.println(myFlashString);
Serial.printf("myFlashString has %u chars and occupies %u bytes\n", myFlashString.length(), myFlashString.size());

To use Strings across translation units, we do this in the header:

DECLARE_FSTR(myFlashString);

And in a source file:

DEFINE_FSTR(myFlashString, "I am a flash string\0I've got a Naughty NUL.");

You can generally use a Flash String anywhere you can use a regular Wiring String as it has an implicit ::String() operator. Note that WString is used within the library for disambiguation.

Inline Strings

Use the FS() macro to create Flash Strings inline:

Serial.println(FS("A Flash String"));

Note

The macro makes use of FS_PTR() which creates the structure and returns a pointer to it. It behaves like a function call, although the compiler inlines the code.

Therefore FS() may only be used within functions. At file scope you’ll get this error:

statement-expressions are not allowed outside functions nor in template-argument lists

The example above doesn’t provide any improvement over F as there are no Flash String overloads available, so is equivalent to this:

String s = FS("A Flash String");
Serial.println(s);

However, it’s rather different if you pass it to a function which recognises Flash Strings, like this:

FSTR::println(Serial, FS("A Flash String"));

This is equivalent to:

FS("A Flash String").printTo(Serial);
Serial.println();

FSTR::String::printTo() uses no heap and imposes no restriction on the string length.

Nested Inline Strings

It would be really useful to be able to use inline Strings this within nested structures, and this can be done provided those structures are in RAM.

Important

Inline Strings cannot be used when defining Vectors or Maps.

Here’s is a simplified structure we will attempt to initialize:

static const struct {
   FlashString* string;
} flashData PROGMEM = { FS_PTR("Inline Flash String") };
Serial.println(*flashData.string);

The static flashData structure gets initialised at runtime on first use, as per C++ rules. This attempts to copy our pointer into the flashData structure which clearly it cannot do as it’s in PROGMEM, so we get a LOAD/STORE error. We must remove PROGMEM.

Avoiding the heap

Instead of using a temporary Wiring String, you can use LOAD_FSTR() to load the content into a temporary stack buffer:

DEFINE_FSTR(globalTest, "This is a testing string");

void func()
{
   LOAD_FSTR(local, globalTest);
   printf("%s, %u characters, buffer is %u bytes\n", local, globalTest.length(), sizeof(local));
}

You can do this with inline Flash Strings using FSTR_ARRAY():

FSTR_ARRAY(buffer, "text");

Is roughly equivalent to:

char name[] = "text";

Except the buffer is word aligned, so sizeof(name) may differ.

Macros
FS_PTR(str)

Define an inline String and return a pointer to it.

Note

The rather obscure asm statement is required to prevent the compiler from discarding the symbol at link time, which leads to an ‘undefined reference’ error

FS(str)

Define an inline FSTR::String and return it as a copy.

Example:

    Serial.println(FS("This is a Flash String"));

DECLARE_FSTR(name)

Declare a global FSTR::String& reference.

Note

Define the FSTR::String object using DEFINE_STR()

Parameters:
  • name

DEFINE_FSTR(name, str)

Define a FSTR::String object with global reference.

Example:

DEFINE_FSTR(test, “This is a test\0Another test\0hello”)

The data includes the nul terminator but the length does not.

Parameters:
DEFINE_FSTR_LOCAL(name, str)

Like DEFINE_FSTR except reference is declared static constexpr.

DEFINE_FSTR_DATA(name, str)

Define a FSTR::String data structure.

Parameters:
  • name – Name of data structure

  • str – Quoted string content

LOAD_FSTR(name, fstr)

Load a FSTR::String object into a named local (stack) buffer.

Example:

DEFINE_FSTR(globalTest, "This is a testing string")
...
LOAD_FSTR(local, globalTest)
printf("%s, %u characters, buffer is %u bytes\n", local, globalTest.length(), sizeof(local));

FSTR_ARRAY(name, str)

Define a flash FSTR::String and load it into a named char[] buffer on the stack.

Note

Equivalent to char name[] = "text" except the buffer is word aligned. Faster than using a temporary Wiring String and avoids using the heap.

Parameters:
  • name – Name of char[] buffer

  • str – Content of FSTR::String

IMPORT_FSTR(name, file)

Define a FSTR::String containing data from an external file.

See also

See also IMPORT_FSTR_DATA

Parameters:
  • name – Name for the FSTR::String object

  • file – Absolute path to the file containing the content

IMPORT_FSTR_LOCAL(name, file)

Like IMPORT_FSTR except reference is declared static constexpr.

FSTR_TABLE(name)

declare a table of FlashStrings

Deprecated:

Use a Vector or Map

Declares a simple table. Example:

DEFINE_FSTR(fstr1, "Test string #1");
DEFINE_FSTR(fstr2, "Test string #2");

FSTR_TABLE(table) = {
    &fstr1,
    &fstr2,
};
Table entries may be accessed directly as they are word-aligned. Examples:
debugf("fstr1 = '%s'", FSTR::String(*table[0]).c_str());
debugf("fstr2.length() = %u", table[1]->length());

Parameters:
  • name – name of the table

String Class
class String : public FSTR::Object<String, char>

describes a counted string stored in flash memory

Public Functions

inline size_t size() const

Get the number of bytes used to store the String.

Note

Always an integer multiple of 4 bytes

inline flash_string_t data() const

Get a WString-compatible pointer to the flash data.

bool equals(const char *cstr, size_t len = 0) const

Check for equality with a C-string.

Note

loads string into a stack buffer for the comparison, no heap required

Parameters:
  • cstr

  • len – Length of cstr (optional)

Return values:

bool – true if strings are identical

bool equals(const String &str) const

Check for equality with another String.

Parameters:

str

Return values:

bool – true if strings are identical

inline StringPrinter printer() const

Supports printing of large String objects.

Avoids implicit String() cast when working with large FlashStrings:

IMPORT_FSTR(largeString, PROJECT_DIR "/files/large-text.txt");
    Serial.println(largeString.printer());

Arrays
Introduction

Supports arrays of simple types, such as char, int, double, or POD structures (i.e. basic C structures).

FSTR::Array is a class template, so requires an additional ElementType parameter:

#include <FlashString/Array.hpp>

DEFINE_FSTR_ARRAY(myDoubleArray, double,
   PI, 53.0, 100, 1e8, 47
);
Serial.print("My double array: ");
myDoubleArray.printTo(Serial);
Serial.println();

Note

Objects do not inherit from Printable because it is a virtual base class.

Therefore, statements like Serial.println(myDoubleArray) are not supported.

This also avoids ambiguity between implicit WString conversions.

There are some Print helper functions in the library you can use:

FSTR::println(Serial, myDoubleArray);

These are templated so will handle both simple data types and Objects.

You can share Arrays between translation units by declaring it in a header:

DECLARE_FSTR_ARRAY(table);
Macros
DECLARE_FSTR_ARRAY(name, ElementType)

Declare a global Array& reference.

Note

Use DEFINE_FSTR_ARRAY to instantiate the global Object

Parameters:
  • name

  • ElementType

DEFINE_FSTR_ARRAY(name, ElementType, ...)

Define an Array Object with global reference.

Note

Unlike String, array is not NUL-terminated

Parameters:
  • name – Name of Array& reference to define

  • ElementType

  • ... – List of ElementType items

DEFINE_FSTR_ARRAY_LOCAL(name, ElementType, ...)

Like DEFINE_FSTR_ARRAY except reference is declared static constexpr.

DEFINE_FSTR_ARRAY_DATA(name, ElementType, ...)

Define an Array data structure.

Parameters:
  • name – Name of data structure

  • ElementType

  • ... – List of ElementType items

LOAD_FSTR_ARRAY(name, array)

Load an Array object into a named local (stack) buffer.

Note

Example:

DEFINE_FSTR_ARRAY(fsArray, double, 5.33, PI)
...
LOAD_FSTR_ARRAY(arr, fsArray)
printf("arr[0] = %f, %u elements, buffer is %u bytes\n", arr[0], fsArray.length(), sizeof(arr));

FSTR_ARRAY_ARRAY(name, ElementType, ...)

Define an Array and load it into a named buffer on the stack.

Note

Equivalent to ElementType name[] = {a, b, c} except the buffer is word-aligned

IMPORT_FSTR_ARRAY(name, ElementType, file)

Define an Array containing data from an external file.

See also

See also IMPORT_FSTR_DATA

Parameters:
  • name – Name for the Array object

  • ElementType – Array element type

  • file – Absolute path to the file containing the content

IMPORT_FSTR_ARRAY_LOCAL(name, ElementType, file)

Like IMPORT_FSTR_ARRAY except reference is declared static constexpr.

Classes
template<typename ElementType>
class Array : public FSTR::Object<Array<ElementType>, ElementType>

Class to access an array of integral values stored in flash.

Template Parameters:

ElementType

Public Functions

inline ArrayPrinter<Array> printer() const

Returns a printer object for this array.

Note

ElementType must be supported by Print

Tables
Introduction

Simple tables can be implemented using Arrays, like this:

struct TableRow {
   float columns[3];

   int operator[](size_t index) const
   {
      return columns[index];
   }
};
DEFINE_FSTR_ARRAY(table, TableRow,
   {0.1, 0.2, 0.3},
   {0.6, 0.7, 0.8}
);
for(auto row: table) {
   Serial.printf("%f, %f, %f\n", row[0], row[1], row[2]);
}

Each row is a fixed size. The FSTR::TableRow class template is provided to simplify this:

#include <FlashString/Table.hpp>

using FloatRow = FSTR::TableRow<float, 3>;
DEFINE_FSTR_ARRAY(table, FloatRow,
   {0.1, 0.2, 0.3},
   {0.6, 0.7, 0.8}
);
table.printTo(Serial);
table.println();

If you want to create a table with rows of different sizes or types, use a Vector.

Class Template
template<typename ElementType, size_t Columns>
class TableRow

Class template to define the row of a table.

Use with an Array Object to construct simple tables. Methods provide Object-like access.

Template Parameters:
  • ElementType

  • Columns – Number of columns in the table

Public Functions

inline ElementType operator[](size_t index) const

Array operator.

Parameters:

index

Return values:

ElementType

inline size_t length() const

Get number of columns.

Return values:

size_t

inline size_t printTo(Print &p) const

Print a row using Array Printer.

Public Static Functions

static inline TableRow empty()

Return a TableRow instance to be used for invalid or empty values.

Vectors
Introduction

A FSTR::Vector is an array of Object pointers:

struct Vector<ObjectType> {
   FSTR::Object object;
   ObjectType* entries[];
};

A key use for this is the construction of string tables.

Defining Vectors

Inline Strings are not supported, so the content has to be defined first:

DEFINE_FSTR_LOCAL(str1, "Test string #1");
DEFINE_FSTR_LOCAL(str2, "Test string #2");
IMPORT_FSTR_LOCAL(str3, PROJECT_DIR "/files/somedata.json");

Now we can define the Vector:

#include <FlashString/Vector.hpp>

DEFINE_FSTR_VECTOR(myTable, FlashString,
   &str1,
   &str2,
   nullptr,
   &str3
);

Note the use of nullptr to indicate an invalid vector entry, as distinct from an empty String.

Using Vectors

Now we can access the data using Vector methods:

debugf("table.length() = %u", table.length());
debugf("fstr1 = '%s'", String(table[0]).c_str());
debugf("fstr2.length() = %u", table[1].length());
debugf("fstr3.length() = %u", table[2].length());

You can share Vectors between translation units by declaring it in a header:

DECLARE_FSTR_VECTOR(table);

To search a Vector:

int i = table.indexOf("TEST STRING #1");

Note

By default, searches in Vector<String> are not case-sensitive.

The indexOf method has an extra ignoreCase parameter, which defaults to true.

Structure

The above example generates a structure like this:

const struct {
   ObjectBase object;
   String* entries[4];
} __fstr__myTable PROGMEM = {
   {16},
   &str1,
   &str2,
   nullptr,
   &str3,
};
const Vector<String>& myTable PROGMEM = __fstr__myTable.as<Vector<String>>();

Note: FSTR:: namespace qualifier omitted for clarity.

Macros
DECLARE_FSTR_VECTOR(name, ObjectType)

Declare a global Vector& reference.

Note

Use DEFINE_VECTOR to instantiate the global Object

Parameters:
  • name

  • ObjectType

DEFINE_FSTR_VECTOR(name, ObjectType, ...)

Define a Vector Object with global reference.

Note

Size will be calculated

Parameters:
  • name – Name of Vector& reference to define

  • ObjectType

  • ... – List of ObjectType* pointers

DEFINE_FSTR_VECTOR_LOCAL(name, ObjectType, ...)

Like DEFINE_FSTR_VECTOR except reference is declared static constexpr.

DEFINE_FSTR_VECTOR_SIZED(name, ObjectType, size, ...)

Define a Vector Object with global reference, specifying the number of elements.

Note

Use in situations where the array size cannot be automatically calculated

Parameters:
  • name – Name of Vector& reference to define

  • ObjectType

  • size – Number of elements

  • ... – List of ObjectType* pointers

DEFINE_FSTR_VECTOR_SIZED_LOCAL(name, ObjectType, size, ...)

Like DEFINE_FSTR_VECTOR_SIZED except reference is declared static constexpr.

DEFINE_FSTR_VECTOR_DATA(name, ObjectType, ...)

Define a Vector data structure.

Note

Size will be calculated

Parameters:
  • name – Name of data structure

  • ObjectType

  • ... – List of ObjectType* pointers

DEFINE_FSTR_VECTOR_DATA_SIZED(name, ObjectType, size, ...)

Define a Vector data structure and specify the number of elements.

Note

Use in situations where the array size cannot be automatically calculated

Parameters:
  • name – Name of data structure

  • ObjectType

  • size – Number of elements

  • ... – List of ObjectType* pointers

Class Template
template<class ObjectType>
class Vector : public FSTR::Object<Vector<ObjectType>, ObjectType*>

Class to access a Vector of objects stored in flash.

Template Parameters:

ObjectType

Maps
Introduction

A FSTR::Map is analogous to the Wiring HashMap class, allowing content to be indexed using a key value.

The Map contains an array of FSTR::MapPair structures:

struct MapPair<KeyType, ContentType> {
   KeyType key_;
   ContentType* content_;
};

KeyType can be any simple type such as char, int, float, enum etc. It may also be a String Object (or, more precisely, String*).

ContentType can be any Object type (String, Array, Vector or Map). This allows hierarchical structures to be created.

Example: int ⇒ String

Here’s a basic example using integer keys:

#include <FlashString/Map.hpp>

IMPORT_FSTR_LOCAL(content1, PROJECT_DIR "/files/index.html");
IMPORT_FSTR_LOCAL(content2, PROJECT_DIR "/files/favicon.html");

DEFINE_FSTR_MAP(intmap, int, FSTR::String,
   {35, &content1},
   {180, &content2}
);

You should generally use IMPORT_FSTR_LOCAL() when referencing imported objects. If you need global access to imported data as well, then use IMPORT_FSTR().

Note

Older toolchains (generally GCC earlier than version 6) will fail to compile with error: the value of ‘FS_content1’ is not usable in a constant expression.

You can work around this as follows:

IMPORT_FSTR(content1, PROJECT_DIR "/files/index.html"); // Make this a global reference
IMPORT_FSTR_LOCAL(content2, PROJECT_DIR "/files/favicon.html");

DEFINE_FSTR_MAP(intmap, int, FSTR::String,
   {35, &FSTR_DATA_NAME(FS_content1).as<FSTR::String>()}, // Cast the actual content
   {180, &content2}
);

We can now do this:

void printValue(int key)
{
   auto value = intmap[key];
   if(value) {
      Serial.printf("Found '%u' in map, containing %u chars\n", value.key(), value.content().length());
      Serial.println(value.printer());
   } else {
      Serial.printf("Couldn't find '%u' in map\n", key);
   }
}
Example: String ⇒ String

Both the key and the content are stored as Strings:

#include <FlashString/Map.hpp>

DEFINE_FSTR_LOCAL(key1, "index.html");
DEFINE_FSTR_LOCAL(key2, "favicon.ico");
IMPORT_FSTR_LOCAL(content1, PROJECT_DIR "/files/index.html");
IMPORT_FSTR_LOCAL(content2, PROJECT_DIR "/files/favicon.html");

DEFINE_FSTR_MAP(fileMap, FlashString, FlashString,
   {&key1, &content1},
   {&key2, &content2},
);

We can now do this:

void onFile(HttpRequest& request, HttpResponse& response)
{
   String fileName = request.uri.getRelativePath();
   auto& value = fileMap[fileName];
   if(value) {
      // Found
      Serial.printf("Found '%s' in fileMap\n", String(value.key()).c_str());
      auto stream = new FlashMemoryStream(value);
      response.sendDataStream(stream, ContentType::fromFullFileName(fileName));
   } else {
      Serial.printf("File '%s' not found\n", fileName.c_str());
   }
}

Note

As with Vector<String>, Map<String, ...> lookups are by default case-insensitive.

If you require a case-sensitive lookup, use the indexOf method with ignoreCase = false.

Structure

The macro in the first example above produces a structure like this:

constexpr const struct {
   ObjectBase object;
   MapPair<int, String> data[2];
} __fstr__intmap PROGMEM = {
   {16},
   {35, &content1},
   {180, &content2},
};
const Map<int, String>& intmap = __fstr__intmap.object.as<Map<int, String>>();

Note: FSTR:: namespace qualifier omitted for clarity.

Usually, each MapPair is 8 bytes, but if the key is a double or int64 it would be 12 bytes.

Macros
DECLARE_FSTR_MAP(name, KeyType, ContentType)

Declare a global Map& reference.

Note

Use DEFINE_FSTR_MAP to instantiate the global object

Parameters:
  • name – Name of the Map& reference to define

  • KeyType – Integral type to use for key

  • ContentType – Object type to declare for content

DEFINE_FSTR_MAP(name, KeyType, ContentType, ...)

Define a Map Object with global reference.

Note

Size will be calculated

Parameters:
  • name – Name of the Map& reference to define

  • KeyType – Integral type to use for key

  • ContentType – Object type to declare for content

  • ... – List of MapPair definitions { key, &content }

DEFINE_FSTR_MAP_LOCAL(name, KeyType, ContentType, ...)

Like DEFINE_FSTR_MAP except reference is declared static constexpr.

DEFINE_FSTR_MAP_SIZED(name, KeyType, ContentType, size, ...)

Define a Map Object with global reference, specifying the number of elements.

Parameters:
  • name – Name of the Map& reference to define

  • KeyType – Integral type to use for key

  • ContentType – Object type to declare for content

  • size – Number of elements

  • ... – List of MapPair definitions { key, &content }

DEFINE_FSTR_MAP_SIZED_LOCAL(name, KeyType, ContentType, size, ...)

Like DEFINE_FSTR_MAP_SIZED except reference is declared static.

DEFINE_FSTR_MAP_DATA(name, KeyType, ContentType, ...)

Define a Map data structure.

Note

Size will be calculated

Parameters:
  • name – Name of data structure

  • KeyType – Integral type to use for key

  • ContentType – Object type to declare for content

  • ... – List of MapPair definitions { key, &content }

DEFINE_FSTR_MAP_DATA_SIZED(name, KeyType, ContentType, size, ...)

Define a Map data structure, specifying the number of elements.

Parameters:
  • name – Name of data structure

  • KeyType – Integral type to use for key

  • ContentType – Object type to declare for content

  • size – Number of elements

  • ... – List of MapPair definitions { key, &content }

template<typename KeyType, class ContentType>
class MapPair
#include <MapPair.hpp>

describes a pair mapping key => data for a specified key type

Template Parameters:
  • KeyType – Integral, floating point, enum or String

  • ContentTypeObject type to use for content

Class Templates
template<typename KeyType, class ContentType, class Pair = MapPair<KeyType, ContentType>>
class Map : public FSTR::Object<Map<KeyType, ContentType>, MapPair<KeyType, ContentType>>

Class template to access an associative map.

Template Parameters:
  • KeyType

  • ContentType

Public Functions

inline const Pair valueAt(unsigned index) const

Get a map entry by index, if it exists.

Note

Result validity can be checked using if()

template<typename TRefKey, typename T = KeyType>
inline std::enable_if<!std::is_class<T>::value, int>::type indexOf(const TRefKey &key) const

Lookup an integral key and return the index.

Parameters:

key – Key to locate, must be compatible with KeyType for equality comparison

Return values:

int – If key isn’t found, return -1

template<typename TRefKey, typename T = KeyType>
inline std::enable_if<std::is_same<T, String>::value, int>::type indexOf(const TRefKey &key, bool ignoreCase = true) const

Lookup a String key and return the index.

Parameters:
  • key

  • ignoreCase – Whether search is case-sensitive (default: true)

Return values:

int – If key isn’t found, return -1

template<typename TRefKey>
inline const Pair operator[](const TRefKey &key) const

Lookup a key and return the entry, if found.

Note

Result validity can be checked using if()

Parameters:

key

inline MapPrinter<Map> printer() const

Returns a printer object for this array.

Note

ElementType must be supported by Print

template<typename KeyType, class ContentType>
class MapPair

describes a pair mapping key => data for a specified key type

Template Parameters:
  • KeyType – Integral, floating point, enum or String

  • ContentTypeObject type to use for content

Public Functions

inline operator IfHelperType() const

Provides bool() operator to determine if Pair is valid.

template<typename T = KeyType>
inline std::enable_if<!std::is_class<T>::value, KeyType>::type key() const

Get the key (non-class key types)

template<typename T = KeyType>
inline std::enable_if<std::is_same<T, String>::value, const KeyType&>::type key() const

Get the key (String key type)

inline const ContentType &content() const

Accessor to get a reference to the content.

Public Static Functions

static inline const MapPair empty()

Get an empty Pair object, identifies as invalid when lookup fails.

Streams
class FSTR::Stream : public IDataSourceStream

Alias: FlashMemoryStream

This is a Sming IDataSourceStream descendant which you can use to stream the contents of any FlashString object. It’s especially useful when used with IMPORT_FSTR:

IMPORT_FSTR(myLargeFile, PROJECT_DIR "/files/lots-of-stuff.txt");
FSTR::Stream fs(myLargeFile);
Serial.println(myLargefile);

Because the data is read in sections, it’s not limited by available RAM.

Note

Unless you need myLargeFile to be a global symbol, you’ll generally want to use IMPORT_FSTR_LOCAL().

Like a FileStream, you can also seek randomly within a FSTR::Stream, so you can use it as the basis for an elementary read-only filesystem.

See Maps for a more useful example.

class FSTR::TemplateStream : public TemplateStream

Alias: TemplateFlashMemoryStream

Standard templating stream for tag replacement.

Utilities
Importing files

For String and Array objects you can import data directly from a file using IMPORT_FSTR() or IMPORT_FSTR_ARRAY(). For example:

IMPORT_FSTR(myData, PROJECT_DIR "/files/myData.bin");

This defines a C++ reference to the data called myData so it can be referred to using DECLARE_FSTR() if required.

Attention

File paths must be absolute or the compiler won’t be able to locate it reliably.

Sming provides PROJECT_DIR and COMPONENT_PATH to help with this.

Note

A corresponding C symbol will also be defined, based on the provided name, to provide linkage with the imported data.

You generally shouldn’t have an issue with this as the symbols are restricted to file scope, but it is something to be aware of.

One use for imported files is to serve content via HTTP, like this:

void onFile(HttpRequest& request, HttpResponse& response)
{
   Serial.printf("myData is %u bytes long\n", myData.length());
   auto fs = new FSTR::Stream(myData);
   response.sendDataStream(fs);
}

Therefore files can be bound into the firmware and accessed without requiring a filing system. This idea is extended further using Maps.

Custom Imports

Use IMPORT_FSTR_DATA() to import the contents of a file without defining any C/C++ variable:

IMPORT_FSTR_DATA(myCustomData, PROJECT_DIR "/files/data.bin");

You’ll need to define an appropriate symbol:

struct MyCustomStruct {
   uint32_t length;
   char name[12];
   char description[20];
   uint8_t data[1024];
};

extern "C" const MyCustomStruct myCustomData;

You’ll still have to consider how the data is accessed. If it’s small and un-complicated you can just copy it into RAM:

MyCustomStruct buf;
memcpy_P(&buf, &myCustomData, sizeof(buf));
Custom Objects

A better way to handle large, complex structures is to define a custom Object to handle it. You can find an example of how to do this in test/app/custom.cpp, which does this:

  1. Define MyCustomStruct:

    struct MyCustomStruct {
       FSTR::ObjectBase object;
       char name[12];
       char description[20];
       FSTR::ObjectBase dataArray;
    };
    
  2. Define a base object type (CustomObject) using the FSTR::Object class template. This determines the underlying element type, generally char or uint8_t are most useful.

  3. Derive an Object class (MyCustomObject) to encapsulate access to MyCustomStruct.

  4. Use the IMPORT_FSTR_OBJECT() macro to import the custom data and define a global reference (customObject) of type MyCustomObject&.

  5. Use DECLARE_FSTR_OBJECT() macro to declare the reference in a header.

More complex examples may involve multiple custom Object types.

API Reference
DECL(t)

Wrap a type declaration so it can be passed with commas in it.

Example:

template <typename ElementType, size_t Columns>
struct MultiRow
{
    ElementType values[Columns];
}
These fail:
    DECLARE_FSTR_ARRAY(myArray, MultiRow<double, 3>);
    DECLARE_FSTR_ARRAY(myArray, (MultiRow<double, 3>));
Use DECL like this:
    DECLARE_FSTR_ARRAY(myArray, DECL((MultiRow<double, 3>)) );
Although for this example we should probably do this:
    using MultiRow_double_3 = MultiRow<double, 3>;
    DECLARE_FSTR_ARRAY(myArray, MultiRow_double_3);

IMPORT_FSTR_DATA(name, file)

Link the contents of a file.

This provides a more efficient way to read constant (read-only) file data. The file content is bound into firmware image at link time.

We need inline assembler’s .incbin instruction to actually import the data. We use a macro STR() so that if required the name can be resolved from a #defined value.

Use PROJECT_DIR to locate files in your project’s source tree:

IMPORT_FSTR_DATA(myFlashData, PROJECT_DIR "/files/my_flash_file.txt");
Use COMPONENT_PATH within a component.

No C/C++ symbol is declared, this is type-dependent and must be done separately:

extern "C" FSTR::String myFlashData;
If the symbol is not referenced the content will be discarded by the linker.

STR(x)
XSTR(x)
template<typename T>
struct argument_type
template<typename T, typename U>
struct argument_type<T(U)>
#include <Utility.hpp>
Sming Integration

Sming provides several aliases to provide compatibility with existing code:

Other pages
Upgrade notes
Version 1.0

FlashString was first introduced in Sming 3.7.0 on 17 November 2018, as a single file in the Wiring directory. Other than bugfixes and Host Emulator support it hasn’t had any significant updates.

Version 2.0

This library was introduced to Sming in version 4.0.1. If you are migrating from Sming 3.7.0 or later and have used FlashString in your projects, some minor changes may be necessary.

FlashString

This has been moved inside the FSTR namespace and renamed to String.

FlashString has been retained as an alias for convenience to avoid ambiguity when used with Wiring String.

FlashString::isEqual() has been renamed to equal() for consistency with Wiring String.

Tables

Table support has been improved and formalised using the Vector class. Previously:

DEFINE_FSTR_LOCAL(fstr1, "Test string #1");
DEFINE_FSTR_LOCAL(fstr2, "Test string #2");

static FSTR_TABLE(table) = {
   FSTR_PTR(fstr1),
   FSTR_PTR(fstr2),
};

Serial.println("FSTR tables[%u]\n", ARRAY_SIZE(table));
Serial.printf(" fstr1 = '%s'\n", String(*table[0]).c_str());
Serial.printf(" fstr1.length() = %u\n", table[0]->length());

Now you can do this:

DEFINE_FSTR_LOCAL(str1, "Test string #1");
DEFINE_FSTR_LOCAL(str2, "Test string #2");

DEFINE_FSTR_VECTOR(table, FlashString, &str1, &str2);

Serial.printf("FSTR table[%u]\n", table.length());
Serial.printf(" fstr1 = '%s'\n", String(table[0]).c_str());
Serial.printf(" fstr1.length() = %u\n", table[0].length());

And perform lookups:

Serial.print(” indexOf(‘Test STRING #1’) = “); Serial.println(table.indexOf(“Test STRING #1”));

Maps and other features

Associative maps have been added to support keys using a Flash String or an integral type. Content is typically a String, but can also be another Table or Map for building hierarchical structures.

Moving to class templates has added a lot of possibilities so I hope you have fun finding out what can be done with this library!

Suggestions for improvements and fixes always welcome :-)

Change Log
Sming 3.7.0

Here’s the PR summary from the original repository. The ‘pull-request’ links are rendered during Sming documentation build.

Thu Sep 20 18:00:58 2018 Pull Request #1438

Initial commit, the FlashString class plus macros

Fri Oct 5 14:52:24 2018 Pull Request #1459

Add FSTR_TABLE() macro to define a FlashString* pointer. Very basic table support so we can use it for looking up HTTP status strings, etc.

Wed Oct 24 09:13:50 2018 Pull Request #1502

Add streaming support - Add FlashMemoryStream class - Add FlashTemplateStream class - Add IMPORT_FSTR macro to bind a file into firmware image as FlashString object - Demonstrate usage using HttpServer_ConfigNetwork sample application. Note behaviour is unchanged, but settings.html is statically linked rather than using SPIFFS.

Sat May 11 14:25:05 2019 Pull Request #1690

Mark const FlashString& references as PROGMEM. The compiler still stores a pointer to long address so it can use relative addressing modes, and it puts these in RAM unless you tell it not to.

Also added DEFINE_FSTR_LOCAL macro to allow correct use inside function definitions.

Thu May 30 14:49:06 2019 Pull Request #1692

Sming Host Emulator! The IMPORT_FSTR feature requires architecture-dependent compiler support, so a separate version was added to support WIN32. The standard version works as-is for Linux.

Tue Jun 4 07:43:29 2019 Pull Request #1709

Fix array bounds issue detected via GCC-8 on Host build (#1709)

Mon Jul 8 07:21:58 2019 Pull Request #1757

Bugfix: LOAD_FSTR() doesn’t NUL-terminate buffered string (#1757)

Tue Jul 30 19:55:50 2019 Pull Request #1786

Revise IMPORT_FSTR macro so the name can itself be #defined (#1786)

Mon Oct 21 08:00:48 2019 Pull Request #1899

Add optional len parameter to Flashstring::isEqual(const char*)

Sming 4.0.0

Sat Nov 2 13:01:20 2019

Sming version 4.0.0 is due for release next week, and FlashString has proven to be a useful addition to the toolkit. Time for an upgrade.

Sming 4.0.1

FlashString has been expanded from a single file into its own Component library to:

  1. Improve support for tables and add associative mapping

  2. Ensure each class has its own header file

  3. Add methods so users don’t need to muck about with memcpy_P, etc.

  4. Move some code out of the header into a separate .cpp source file

  5. Add proper documentation

  6. Make it easier to port to other frameworks (e.g. Arduino)

  7. Get it out of Wiring - doesn’t really belong there and certainly don’t want to clutter up that directory

  8. Improve maintainability

  9. Fix compatibility with ArduinoJson 6.13.0

Change summary:

  • Move FlashString into a separate Component

  • Add FlashString::read() and readFlash() methods

  • Revise internals to be more consistent with naming

  • Improve table handling using new Vector class

  • Add associative mapping support with Map class

  • Revise structures so it contains only the length, obtaining data position using pointer arithmetic. This fixes an error with GCC 8.2.0 which didn’t like accessing zero-length arrays.

  • Use ObjectBase as the first element in a data structure to allow use of ‘safe’ static casting, handled using the as() method template

  • Documentation!

Technobabble
Why not just use a wrapper class?

A wrapper class would require this:

const struct {
   ...
} myFlashData PROGMEM = { ... };

void func()
{
   FlashString fs(myFlashData);
   Serial.println(fs);
}

Easy enough, just create a wrapper every time you need to use the data.

It does get a bit clunky though when you have a lot of strings, lists of strings or where strings are used in several places.

How about global/static wrappers

Like this:

FlashString fs(myFlashData);

void func()
{
   Serial.println(fs);
}

Each wrapper uses 4 bytes of RAM. We also need to bear in mind that at startup objects are being initialised so we cannot necessarily guarantee that our FlashString wrappers have been created by the time they’re needed in other code. Result: hang/crash.

How about a macro to create the wrapper?

Something like this:

void func()
{
   Serial.println(FS(myFlashData));
}

To be honest, I hadn’t really considered that one, but it is worth exploring.

Can’t I put the wrapper in PROGMEM?

A wrapper requires a constructor, each of which needs an initialiser which gets called at startup.

Which is why FlashString isn’t a wrapper.

FlashStrings can be used anywhere you can use PROGMEM data, including globally initialised classes.

There is a disadvantage to this approach: You can’t make copies of FlashString objects. They must always be accessed as references or pointers.

Reasons to use FlashString
  • Easy to use, treat like read-only String object

  • Use methods of FlashString instead of calls to memcpy_P, strcpy_P, etc.

  • Blocks of data stored in flash can be passed around easily as the length is known

  • Can be passed to functions instead of a String parameter, but without having to load it into RAM first

  • Can be streamed directly using FlashMemoryStream

  • Can be read randomly using FlashString::read()

  • Aligned read and copy operations provide excellent performance

  • Fast equality comparisons using length field to short-circuit comparison

  • Data can be imported and linked directly into the program image from a local file, and accessed as a FlashString

  • Custom structures can be defined and accessed as a FlashString

  • Operates transparently with ArduinoJson (patched as from version 6.13.0)

Reasons not to use FlashString
  • Storing lots of small strings can be inefficient because of length and alignment, and because a NUL terminator is always appended by DEFINE_FSTR macros even though it’s not generally required. (This is because of how C requires string arrays to be declared.)

    For example, “A” requires 8 bytes:

   01 00 00 00 // Length
   41 00 00 00 // "A\0" padded to word boundary

However, this disadvantage can be overcome by storing such strings in a single block
and accessing them using a :source:`Sming/Core/Data/CStringArray`.

Note

Some counted string implementations store the length field at offset -4 so we always point to the actual data. In this case, that doesn’t offer any advantages as directly accessing the data is discouraged. Therefore, the length field always comes first.

Structure packing

When using Arrays with 64-bit types (including double) this is what we define:

struct {
   Array<int64_t> object;
   int64_t data[5];
} x;

The object is a structure containing a single 32-bit value. Data arrays with uint8, uint16 or uint32 elements will start on the next 32-bit boundary, which is what we want.

With 64-bit values this is what the compiler does:

struct {
   Array<int64_t> object;
   uint32_t; // Packing added by compiler
   int64_t values[5];
} x;

Which messes things up of course. Therefore Array classes and data are packed.

This is currently only an issue for Array types, but it also means that if you want to use Array with custom data structures then they should also be packed. That means you need to pay careful attention to member alignment and if packing is required then add it manually.

TODO List
Missing methods

Behaviour of String should reflect WString. Review and add any missing methods. Note that implementations for some are likely non-trivial since we cannot assume the content will fit into RAM.

Benchmark filemap

Compare SPIFFS vs. file map:

  • File access times

  • File transfer times

Implement stream operator <<

For simpler printing. This is a large architectural decision as Sming doesn’t have any of this, neither it seems does Arduino although some libraries add support for it.

The advantage over Print/Printable is that support can be added using template functions without modifying the classes themselves. Formatting statements can be inserted to customise the output.

Formatted print output

We have separate argument for Array printing, but if we want to customise the text for each item as well then we have to use a regular for-loop and handle the separators as well.

Easiest is probably to just add format arguments to printTo() method.

Simple example: print an array of integers in HEX.

Need to consider with text escaping, probably leave that for external libraries.

Multi-dimensional arrays

This could be an array of structs, so operator[] returns an entire row. As an optimisation, could we define additional accessors for nested levels?

Alternatives:

  • Vector<Array>. Each array has to be defined before table.

  • Specialize operator for 2x2 case as it’s quite common. e.g. SubElementType operator[](unsigned row, unsigned col) const

Type Information

The flashLength_ value can be redefined like this:

length: 20;
elementSize: 3; ///< Number of bytes in each element, less one
type: 5; ///< Enumeration identifying element type
   Char = 0,
   Signed,
   Unsigned,
   Float,
   Enum,
   String,
   Array,
   Vector,
   Map,
   UserDefined = 16 // Start of user-defined types
Variant Object

Interprets objects based on type information at runtime. This would allow complex data structures to be defined and used without knowing the object types. For example, translated JSON or XML documents.

Translation tools

Say we wanted to access a read-only JSON document. How might we do that?

Easy. We write a Host application, so we can use all the existing libraries. Memory not an issue.

  1. Load the JSON document using ArduinoJson

  2. Serialize the document into FlashString-compatible declarations, producing a header file and a source file.

All we need to do then is write a JSON serializer to produce the appropriate structures.

About FlashString

FlashString was first introduced in Sming 3.7.0 on 17 November 2018.

I created it as a one-file solution to address these specific issues:

  1. Regular Strings are really useful for passing around any kind of heap-allocated data, but it’s limited by available RAM.

    The https://github.com/mikalhart/Flash library for Arduino provides a good approach, creating a wrapper class around the flash data. But I wanted it to be able to treat my FlashString classes as if they were just stored in flash so I wouldn’t have to wrap everything in a macro, like F() does.

    Solution: The FSTR::String (aka FlashString) class.

  2. Using PROGMEM directly for data is cumbersome, slow and fraught with danger: It’s inherently unsafe because of the alignment issues. Some smart cookie came up with a compiler patch so it could insert the correct instructions and thus avoid alignment exceptions. However, that still causes execution of inefficient code since the hardware requires we perform aligned accesses. Generating the structures correctly in the first place felt the best way forward.

    Solution: DEFINE_FSTR()

  3. Sharing flash data structures globally

    Solution: DECLARE_FSTR()

  4. How to get content into PROGMEM without having to manually convert everything into C structures. One solution to this is using external tools but that complicates the build process and felt un-necessary.

    Solution: IMPORT_FSTR()

  5. Relying on SPIFFS for serving fixed content is inefficient and problematic if/when the filesystem gets corrupted. I needed a solution which allowed large content to be served up without requiring a filesystem. The long term solution to this is, of course, a read-only filesystem but that is a complex thing indeed to do properly.

    Solution: FSTR::Stream (aka FlashMemoryStream) class.

Embedded microsystems

Back in the golden era of the Commodore 64 and the ZX81, embedded really meant something. You had to get your hands dirty and poke about in the hardware.

With modern hardware it’s cheating. I mean, how on earth can a basic Android ‘App’ weigh in at 20MB? I cannot get my head around that.

Of course, it’s just economics - far cheaper to throw lots of hardware resources at a problem than actually build it right in the first place. With all that memory kicking about it’s just pure laziness. IMHO. And of course that trend will continue because it feeds the hardware manufacturers in this disposable world.

Not a fan.

Anyway, before I lose the plot my point was that as the term ‘embedded’ has been hijacked it needs at least some qualification.

So I’ll say that this library, and my other work, is focused on embedded microsystems (EMS).

Why bother?

The main limitation of the ESP8266 is probably its RAM. So why not just use an ESP32? It’s got lots more RAM.

But it’s still a finite resource, and I can think of far better ways to use it than filling it up with constant data.

QED. RAM will always be a scarce commodity in an EMS.

Thanks

This library may have never been written if not for ArduinoJson. Of specific interest is the way class templates are used, the high quality of documentation, ease of use and test-driven approach. I have attempted to emulate those particular attributes here.

Special thanks to Slavey Karadzhov for his support and assistance over the past year mentoring me through the process of upgrading the Sming embedded framework. I’ve learned a phenomenal amount about modern development practices and it’s really given my coding practices a kick up the arse. Awesome :-)

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Hosted-Lib

This library provides replacement implementations for low-level operations which may then be accessed via the selected RPC interface.

See HostEd for further details.

References
Environment Variables
  • ENABLE_HOSTED_DIGITAL

  • ENABLE_HOSTED_WIRE

SoC support
  • host

HostEd

The hosted component allows Sming’s host emulator to run parts of the commands on an actual microcontroller. The communication is done via simplePRC and the microcontroller has to be flashed with a special application.

Overview

Sming’s host emulator allows easier debugging and development of embedded applications. This component named “Hosted” extends the host emulator and facilitates testing functionality that only a real microcontroller can provide as for example digital I/O operations or SPI operations.

For example in order to run the Basic_Blink application under the host emulator and run the actual blinking of a LED on a microcontroller we can compile the application using the following directives:

make SMING_ARCH=Host ENABLE_HOSTED=tcp HOSTED_SERVER_IP=192.168.4.1
  • SMING_ARCH=Host instructs the build system to build the application for the Host architecture.

  • ENABLE_HOSTED=tcp instructs the host emulator to communicate with the real microcontroller using TCP.

  • HOSTED_SERVER_IP=192.168.4.1 tells the host emulator where the server is running.

We need to compile and flash also a special application on the desired microcontroller. This application will act as an RPC Server and will execute the commands from the host emulator on the microcontroller.

In the samples directory you will find the sample applications that will turn your microcontroller into an RCP server.

The compilation and flashing for ESP32, for example, can be done using the following commands:

cd samples/tcp
make SMING_ARCH=Esp32 WIFI_SSID=YourSSID WIFI_PWD=YourPassword
make flash

If you replace SMING_ARCH=Esp32 with SMING_ARCH=Esp8266 then the hosted application will be compiled and flashed on a ESP8266 microcontroller. Make sure to replace the values of  WIFI_SSID and WIFI_PWD with the actual name and password for the Access Point (AP).

Communication

At the moment the communication between an application running on the Host and the RCP server running on a microcontroller can be done using TCP or serial interface.

The transport classes are located under include/Hosted/Transport.

Configuration
ENABLE_HOSTED

Default: empty (disabled)

Enables the hosted component. Valid values for the moment are: - tcp for communication over TCP network. - serial for communication over serial interface

HOSTED_SERVER_IP

Default: 192.168.13.1

Used only when ENABLE_HOSTED=tcp is specified. Specifies the IP address of the remote RPC server.

HOSTED_COM_PORT

Default: COM_PORT

Used only when ENABLE_HOSTED=serial is specified. Specifies which local communication port should be used to connect to the remote RPC server.

HOSTED_COM_SPEED

Default: 115200

Used only when ENABLE_HOSTED=serial is specified. Specifies the communication baud rate.

API Documentation
namespace Hosted

Functions

char convertType(const String &type)

Convert C type to format character See: https://docs.python.org/3.5/library/struct.html#format-strings

String convertFQN(const String &name)

Converts a name as given from PRETTY_FUNCTION to internal format. Examples: void a::sub(int) -> a::sub(: i) void TwoWire::pins(uint8_t,uint8_t) -> TwoWire::pins(: B B) uint8_t digitalRead(uint16_t) -> digitalRead(B: H)

Parameters:

name – source name

Return values:

converted – name

Variables

constexpr int COMMAND_NOT_FOUND = -1
class Client
#include <Client.h>

Public Functions

template<typename ...Args>
inline bool send(const String &functionName, Args... args)

Method to send commands to the remote server.

If the command is overloaded, one command name with two or more different signatures then the name has to be containing the full function signature. Example: “void digitalWrite(uint16_t, uint8_t)”. The name with the signature MUST be the same as the one produced from PRETTY_FUNCTION -> https://gcc.gnu.org/onlinedocs/gcc/Function-Names.html

Parameters:
  • functionName – Either the name or the name with the signature. Example: “digitalWrite” - will try to call the default digitalWrite function on the server

  • variable – arguments

Return values:

true – on success, false if the command is not available

template<typename R>
inline R wait()

This method will block the execution until a message is detected.

Return values:

HostedCommand

inline int getFunctionId(String name)

Fetches a list of commands supported on the RPC server and gives back the id of the desired command.

Parameters:

name – command name to query

Return values:

-1 – if not found. Otherwise the id of the function

inline bool getRemoteCommands()

Gets list of remote command names and their ids.

Return values:

true – on success, false otherwise

class Serial : public Stream
#include <Serial.h>

Public Functions

inline bool begin(uint32_t baud = 9600)

Initialise the serial port.

Parameters:

baud – BAUD rate of the serial port (Default: 9600)

inline virtual size_t write(uint8_t c) override

Writes a single character to output stream.

Parameters:

c – Character to write to output stream

Return values:

size_t – Quantity of characters written to output stream

inline virtual size_t readBytes(char *buffer, size_t length) override

Read chars from stream into buffer.

Terminates if length characters have been read or timeout (see setTimeout). Returns the number of characters placed in the buffer (0 means no valid data found).

Note

Inherited classes may provide more efficient implementations without timeout.

inline virtual size_t write(const uint8_t *buffer, size_t size)

Writes characters from a buffer to output stream.

Parameters:
  • buffer – Pointer to character buffer

  • size – Quantity of characters to write

Return values:

size_t – Quantity of characters written to stream

namespace Transport
class BaseTransport
#include <BaseTransport.h>

Subclassed by Hosted::Transport::SerialTransport, Hosted::Transport::TcpTransport

class SerialTransport : public Hosted::Transport::BaseTransport
#include <SerialTransport.h>
class TcpClientStream : public Stream
#include <TcpClientStream.h>

Public Functions

inline virtual size_t readBytes(char *buffer, size_t length) override

Read chars from stream into buffer.

Terminates if length characters have been read or timeout (see setTimeout). Returns the number of characters placed in the buffer (0 means no valid data found).

Note

Inherited classes may provide more efficient implementations without timeout.

inline virtual size_t write(const uint8_t *buffer, size_t size) override

Writes characters from a buffer to output stream.

Parameters:
  • buffer – Pointer to character buffer

  • size – Quantity of characters to write

Return values:

size_t – Quantity of characters written to stream

inline virtual size_t write(uint8_t c) override

Writes a single character to output stream.

Parameters:

c – Character to write to output stream

Return values:

size_t – Quantity of characters written to output stream

class TcpClientTransport : public Hosted::Transport::TcpTransport
#include <TcpClientTransport.h>
class TcpServerTransport : public Hosted::Transport::TcpTransport
#include <TcpServerTransport.h>
class TcpTransport : public Hosted::Transport::BaseTransport
#include <TcpTransport.h>

Subclassed by Hosted::Transport::TcpClientTransport, Hosted::Transport::TcpServerTransport

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Installable File System

Created for Sming Framework Project August 2018 by mikee47

I struggled to find anything like this for embedded systems. Probably didn’t look hard enough, but it seemed like a fun thing to do so here we are.

The term ‘IFS’ came about because of the ‘I’ naming convention for virtual ‘Interface’ classes, hence IFileSystem. The term ‘installable’ is entirely appropriate because IFS allows file systems to be loaded and unloaded dynamically. Or maybe I just nicked the term from Microsoft :-)

Overview

IFS is written in C++ and has these core components:

FileSystem API

File systems are implemented using the IFS::IFileSystem virtual class. This is, in essence, a single large ‘function table’ you will see in regular filesystem implementations.

Class methods are similar to SPIFFS (which is POSIX-like).

Note

A single Stat structure is used both for reading directory entries and for regular fileStat() operations.

This differs from regular file APIs but is intended to simplify operation.

Applications will typically use IFS::FileSystem instead, which adds additional methods and overloads such as String parameter support. This used to implement the standard ‘flat’ Sming filesystem API, with a few minor changes and a number of additions.

Two wrapper clases (IFS::File and IFS::Directory) are provided for applications to manage access to files and folders.

Firmware FileSystem (FWFS)

Files, directories and metadata are all stored as objects in read-only image. FWFS images are compact, fast to access and use very little RAM (approx. 240 bytes for file descriptors, etc.)

To support read/write data a writeable filesystem can be mounted in a sub-directory.

A python tool fsbuild is used to build an FWFS image from user files. See Filesystem builder.

This is integrated into the build system using the fwfs-build target for the partition. Example Hardware configuration fragment:

"partitions": {
   "fwfs1": {
      "address": "0x280000",
      "size": "0x60000",
      "type": "data",
      "subtype": "fwfs",
      "filename": "out/fwfs1.bin",
      "build": {
          "target": "fwfs-build",   // To build a FWFS image
          "config": "fsimage.fwfs"  // Configuration for the image
      }
   }
}

Sming provides the Basic IFS sample application which gives a worked example of this.

Simple filesystem definitions can be defined in-situ, rather than using external file:

"build": {
      "target": "fwfs-build",   // To build a FWFS image
      "config": {
         "name": "Simple filesystem",
         "source": {
            "/": "files"
         }
      }
}

The following basic IFS implementations are provided in this library:

IFS::FWFS::FileSystem

Firmware Filesystem. It is designed to support all features of IFS, whereas other filesystems may only use a subset.

IFS::HYFS::FileSystem

Hybrid filesystem. Uses FWFS as the read-only root filesystem, with a writeable filesystem ‘layered’ on top.

When a file is opened for writing it is transparently copied to the SPIFFS partition so it can be updated. Wiping the SPIFFS partition reverts the filesystem to its original state.

Note that files marked as ‘read-only’ on the FWFS system are blocked from this behaviour.

IFS::Host::FileSystem

For Host architecture this allows access to the Linux/Windows host filesystem.

IFS::Gdb::FileSystem

When running under a debugger this allows access to the Host filesystem. (Currently only works for ESP8266.)

IFS (and FWFS) has the following features:

Attributes

Files have a standard set of attribute flags plus modification time and simple role-based access control list (ACL).

Directories

Fully supported, and can be enumerated with associated file information using a standard opendir/readdir/closedir function set.

User metadata

Supported for application use. The API for this is loosely based on Linux extended attributes (non-POSIX). Attributes are small chunks of data attached to files and directories, each identified by a numeric IFS::AttributeTag.

Filesystem API

The Sming FileSystem functions are now wrappers around a single IFileSystem instance, which is provided by the application.

Streaming classes

Sming provides IFS implementations for these so they can be constructed on any filesystem, not just the main (global) one.

Dynamic loading

File systems may be loaded/created and unloaded/destroyed at runtime

Multiple filesystems

Applications may use any supported filesystem, or write their own, or use any combination of existing filesystems to meet requirements. The API is the same.

Mount points

FWFS is designed for use as a read-only root filing system, and supports mounting other filesystems in special directories.

FWFS

Many applications require a default, often fixed set of files. The easiest way is just to use SPIFFS. The problem is that power outages can corrupt a filesystem. For an embedded device that’s bad news. SPIFFS is also a bit overkill if you’re just storing configuration data, or it’s just for read-only use.

So what do you do if your filesystem gets wiped? Resetting a system back to a functional, default state can be tricky if the core user interface web files are gone. You could reformat and pull a standard set of files off a server somewhere. If your storage requirements are minimal, you could link the file data into your firmware as constant data blocks.

That’s kind of what FWFS does, but in a more structured and user-friendly way.

FWFS offers a more convenient solution by providing all your default files in a compact, fast, read-only format. Images can be mounted in separate partitions, linked into the program image itself or stored as files within another filesystem.

Note

This behaviour is supported by partitions (see Storage Management) using custom Storage::Device objects.

Redirection

FWFS incorporates a redirector. This works by creating a mount point (a named object), which looks like an empty directory. When accessed, this get redirected to the root of another filesystem. The maximum number of mount points is fixed at compile time, but file systems can be mounted and dismounted at any time.

Mount points are identified explicitly in the build configuration file:

"mountpoints": {
   "path/to/use/spiffs": 0,
   "path/to/use/littlefs": 1
}

The filesystem builder creates the MountPoint objects and tags them with the given volume indices. For example, the directory “path/to/use/littlefs” is attached to volume index #0.

Note

Unlike other filesystems you cannot use a regular directory as a mountpoint. To change the name of a mountpoint requires the filesystem image to be re-built and re-flashed.

Applications use the IFileSystem::setVolume() method to install the actual filesystem.

Streaming backup/archive support

The IFS::FWFS::ArchiveStream class can be used to generate streaming filesystem backups from any supported filesystem. The archive files are in FWFS format.

Here are some examples of how it can be used:

  • Stream filesystem (or directory) images directly to remote servers

  • Make local filesystem backups

  • Compact log files which don’t change much (think of ZIP files - just needs a compression plugin)

  • Backup entire filesystem a local file, an empty partition, etc.

  • Defragment/compact or repair a damaged filesystem by re-formatting then restoring from backup

The archiver has some additional features:

  • Specify whether to archive an entire filesystem or start from a specific directory

  • Specify whether to follow links (e.g. other filesystems in mountpoints) or not

  • Exclude any file or directory via custom callback (or by overriding methods)

  • Perform custom file data encoding such as compression or encryption via callbacks

  • Add additional metadata to files (comments, encryption codes, etc.)

See the Basic IFS sample for

Access Control

This came about because I wanted to secure down my ESP8266 web server applications so that only the basic index.html, stylesheets and accompanying javascript would be publicly accessibly. Everything else would require user authentication.

I also wanted to prevent certain users from accessing restricted files. Other users would also be able to edit files. So a simple role-based access control mechanism seemed appropriate.

Access control typically encapsulates two areas:

Authentication

Is the user who they say they are? Usually performed by validating a username/password combination.

Authorisation

What is the user permitted to do?

I’ll step aside for a brief word on security. Authentication is the weakest link because it’s exposed to public scrutiny. To avoid compromise authentication must only be done over a secured link. That means SSL.

If you have the option it’s usually best to put all your smart devices behind a secure proxy. The raspberry Pi is great for stuff like this. The Pi deals with keeping the public connection secure, and translates it into a regular HTTP connection for the ESP8266.

If you don’t have this option, but you need to connect your ESP8266 to the internet, use the SSL build for Sming.

Having done this, we don’t need to worry about encrypting passwords as the SSL layer will do that. We just need to make sure they’re good passwords.

In my applications authentication is done by matching username/password against the user database, stored in a JSON file. If successful, the session gets a token which appears in every subsequent request. The user database indicates a User Role, one of public, guest, user, manager or admin. IFS keeps an ‘Access Control List’ (ACL) for each file containing two entries (ACE), one for read access and another for write access. The ACE specifies the minimum assigned IFS::UserRole required for access.

This is probably as much as the filesystem needs to do. I can’t see that file ownership, inherited permissions or more finely-grained access permissions would be required, but having said that extending this system would probably be fairly straightforward.

Configuration filesystem

If an application only requires write access for configuration files, SPIFFS is overkill. These files would be updated very infrequently, so wear-levelling would be un-necessary. The names and number of files would probably also be known at build time, and an individual file could be limited to a fixed size, for example one or two flash sectors. A ConfigFileSystem implementation would not need to support file creation or deletion.

Such a system would require almost no static RAM allocation and code size would be tiny.

However, the LittleFS has excellent metadata support and is ideal for storing configuration information. This can be done using :IFS::FileSystem::setUserAttribute and read using :IFS::FileSystem::getUserAttribute or :IFS::FileSystem::enumAttributes.

Note

The ESP-IDF has a mechanism for flash-based configuration space via the NVS component. It is robust and flexible but uses a significant amount of RAM for buffering which may preclude its use with the ESP8266.

FWFS Objects

All files, directories and associated information elements are stored as ‘objects’. Files and directories are ‘named’ objects, which may contain other objects either directly or as references. Small objects (255 bytes or less) are stored directly, larger ones get their own file. Maximum object size is 16Mbytes.

File content is stored in un-named data objects. A named object can have any number of these and will be treated as a single entity for read/write operations. File ‘fragments’ do not need to be contiguous, and are reassembled during read operations.

Named objects can be enumerated using IFS::IFileSystem::readdir(). Internally, FWFS uses handles to access any named object. Handles are allocated from a static pool to avoid excessive dynamic (heap) allocation. Users can attach their own data to any named object using custom object types.

The filesystem layout is displayed during initial mount if this library is built with DEBUG_VERBOSE_LEVEL = 3.

Why FWFS?

There are many existing candidates for a read-only system, so why do we need another one? Here are some reasons:

  • SPIFFS and LittleFS could be used in read-only mode but they are not designed for space-efficiency. Images are therefore larger than necessary, sometimes considerably larger. This is also true of other such filesystems designed for Linux, etc.

    FWFS is designed to produce the smallest possible images to conserve limited flash storage. It therefore has a high effective capacity, i.e. you can put a lot more in there than with other filesystems.

  • With ROMFS, for example, information is laid out with headers first, followed by data. The root directory and volume information are at the front.

    FWFS works in reverse by writing out file contents first, then file headers and then directory records. The root directory comes at the end, followed by the volume information record. This allows images to be created as a stream because directory records can be efficiently constructed in RAM as each file or subdirectory record is written out. This keeps memory usage low.

    In addition, checksums and additional metadata can be created while file data is written out. This could be required for compressing or encrypting the contents, or for error tolerance. For example, if corruption is encountered whilst reading file contents this can be noted in the metadata which is written out afterwards.

    Filesystem images are therefore generated in a single pass, with each file or directory only read once.

  • Standard attribute support not well-suited to embedded microsystems.

    The small set of standard metadata defined by IFS is designed to solve specific problems with typical IOT applications.

Code dependencies

Written initially for Sming, the library should be fairly portable to other systems.

No definitions from SPIFFS or other modules should be used in the public interface; such dependencies should be managed internally.

Applications should avoid using filesystem-dependent calls, structures or error codes. Such code, if necessary, should be placed into a separate module.

Implementation details

The traditional way to implement installable filing systems is using function tables, such as you’ll see in Linux. One reason is because the Linux kernel is written in C, not C++. For Sming, a virtual class seems the obvious choice, however there are some pros and cons.

VMT
Advantages
  • Compiler ensures correct ordering of methods, parameter type checking

  • Simpler coding

  • Extending and overriding is natural

Function table
Advantages
  • Portable to C applications (although with some fudging so are VMTs).

Disadvantages
  • Care required to keep function order and parameters correct. Very likely we’d use a bunch of macros to deal with this.

Macros

We could #define the active filing system name which the FileSystem functions would map to the appropriate call. For example, fileOpen would get mapped to SPIFlashFileSystem_open(). We need to provide macros for defining file system functions.

Advantages
  • Fast

Disadvantages
  • Complicated

  • Prone to bugs

  • Not C++

Configuration variables
FWFS_DEBUG

default: 0

Set to 1 to enable more detailed debugging information.

ENABLE_FILE_SIZE64

default: disabled

Set to 1 to enable support for 64-bit files. This requires ENABLE_STORAGE_SIZE64 to be set.

API
namespace IFS

Return compression corresponding to given string

param str:

retval Compression::Type:

Compression::Type getCompressionType(const char *str, Compression::Type defaultValue = Compression::Type::None)
inline Compression::Type getCompressionType(const String &str, Compression::Type defaultValue = Compression::Type::None)

Return the access type value for the given string.

param str:

param defaultRole:

Returned if string isn’t recognsed

retval UserRole:

UserRole getUserRole(const char *str, UserRole defaultRole)
inline UserRole getUserRole(const String &str, UserRole defaultRole)

Typedefs

using AttributeEnumCallback = Delegate<bool(AttributeEnum &e)>

Return true to continue enumeration, false to stop.

using ErrorCode = int
using FileAttributes = BitSet<uint8_t, FileAttribute, size_t(FileAttribute::MAX)>

File attributes are stored as a bitmask.

using DirHandle = struct ImplFileDir*
using OpenFlags = BitSet<uint8_t, OpenFlag, size_t(OpenFlag::MAX)>
using FileHandle = int16_t

File handle.

References an open file

using FileID = uint32_t

File identifier.

Contained within Stat, uniquely identifies any file on the file system.

Enums

enum class AttributeTag : uint16_t

Identifies a specific attribute.

Values:

enumerator XX
enumerator User

First user attribute.

enum ControlCode

See IFS::IFileSystem::fcontrol

These are weakly typed as values may be defined elsewhere.

Values:

enumerator FCNTL_GET_MD5_HASH

Get stored MD5 hash for file.

FWFS calculates this when the filesystem image is built and can be used by applications to verify file integrity.

On success, returns size of the hash itself (16) If the file is zero-length then Error::NotFound will be returned as the hash is not stored for empty files.

enumerator FCNTL_SET_VOLUME_LABEL

Set volume label.

enumerator FCNTL_USER_BASE

Start of user-defined codes.

Codes before this are reserved for system use

enum class FileAttribute

Values:

enumerator XX
enumerator MAX
enum class OpenFlag

Values:

enumerator XX
enumerator MAX
enum class UserRole : uint8_t

Values:

enumerator XX
enumerator MAX

Actually maxmimum value + 1…

Functions

String getAclString(const IFS::ACL &acl)

Return a brief textual representation for an ACL Suitable for inclusion in a file listing.

Parameters:

acl

Return values:

String

inline AttributeTag getUserAttributeTag(uint8_t value)
size_t getAttributeSize(AttributeTag tag)
String getFileAttributeString(FileAttributes attr)

Get the string representation for the given set of file attributes suitable for inclusion in a file listing.

Parameters:

attr

Return values:

String

FileSystem *getDefaultFileSystem()

Framework should implement this method.

FileSystem *createFirmwareFilesystem(Storage::Partition partition)

Create a firmware filesystem.

Parameters:

partition

Return values:

FileSystem* – constructed filesystem object

FileSystem *createHybridFilesystem(Storage::Partition fwfsPartition, IFileSystem *flashFileSystem)

Create a hybrid filesystem.

Parameters:
  • fwfsPartition – Base read-only filesystem partition

  • flashFileSystem – The filesystem to use for writing

Return values:

FileSystem* – constructed filesystem object

FileSystem *mountArchive(FileSystem &fs, const String &filename)

Mount an FWFS archive.

Parameters:
  • fs – Filesystem where file is located

  • filename – Name of archive file

Return values:

FileSystem* – constructed filesystem object

inline constexpr OpenFlags operator|(OpenFlag a, OpenFlag b)
time_t fsGetTimeUTC()

Get current timestamp in UTC.

Note

Filing systems must store timestamps in UTC Use this function; makes porting easier.

Return values:

time_t

char getChar(UserRole role)
UserRole getUserRole(char code, UserRole defaultRole)
bool isRootPath(const char *&path)

Check if path is root directory.

Paths equal to “/” or “” are empty and considered equivalent to nullptr. Methods or functions can use this macro to resolve these for simpler parsing.

Parameters:

Path – to check, set to nullptr if it’s the root directory

Return values:

bool – true if path is root directory

void checkStat(Stat &stat)
FileSystem *createFatFilesystem(Storage::Partition partition)

Create a FAT filesystem.

Parameters:

partition

Return values:

FileSystem* – constructed filesystem object

Variables

constexpr ErrorCode FS_OK = Error::Success
class DirectoryTemplate : public SectionTemplate
#include <DirectoryTemplate.h>

Directory stream class.

Subclassed by IFS::HtmlDirectoryTemplate, IFS::JsonDirectoryTemplate

Public Functions

inline virtual bool nextRecord() override

Move to next record.

Return values:

bool – true to emit section, false to skip

class FileStream : public IFS::FsBase, public ReadWriteStream
#include <FileStream.h>

File stream class.

Subclassed by FileStream, GdbFileStream, HostFileStream

Public Functions

void attach(FileHandle file, size_t size)

Attach this stream object to an open file handle.

Parameters:
  • file

  • size

bool open(const String &fileName, IFS::OpenFlags openFlags = OpenFlag::Read)

Open a file by path, and attach this stream object to it.

Note

call getLastError() to determine cause of failure

Parameters:
  • fileName – Full path to file

  • openFlags

Return values:

bool – true on success, false on error

void close()

Close file.

inline virtual StreamType getStreamType() const override

Get the stream type.

Return values:

StreamType – The stream type.

virtual size_t write(const uint8_t *buffer, size_t size) override

Write chars to stream.

Note

Although this is defined in the Print class, ReadWriteStream uses this as the core output method so descendants are required to implement it

Parameters:
  • buffer – Pointer to buffer to write to the stream

  • size – Quantity of chars to write

Return values:

size_t – Quantity of chars written to stream

inline virtual int read() override

Read one character and moves the stream pointer.

Return values:

The – character that was read or -1 if none is available

virtual size_t readBytes(char *buffer, size_t length) override

Read chars from stream into buffer.

Terminates if length characters have been read or timeout (see setTimeout). Returns the number of characters placed in the buffer (0 means no valid data found).

Note

Inherited classes may provide more efficient implementations without timeout.

virtual uint16_t readMemoryBlock(char *data, int bufSize) override

Read a block of memory.

Todo:

Should IDataSourceStream::readMemoryBlock return same data type as its bufSize param?

Parameters:
  • data – Pointer to the data to be read

  • bufSize – Quantity of chars to read

Return values:

uint16_t – Quantity of chars read

virtual int seekFrom(int offset, SeekOrigin origin) override

Change position in stream.

Note

This method is implemented by streams which support random seeking, such as files and memory streams.

Parameters:
  • offset

  • origin

Return values:

New – position, < 0 on error

inline virtual bool isFinished() override

Check if all data has been read.

Return values:

bool – True on success.

String fileName() const

Filename of file stream is attached to.

Return values:

String – invalid if stream isn’t open

inline bool fileExist() const

Determine if file exists.

Return values:

bool – true if stream contains valid file

inline virtual String getName() const override

Returns name of the resource.

Note

Commonly used to obtain name of file

Return values:

String

virtual MimeType getMimeType() const override

Get MIME type for stream content.

Return values:

MimeType

inline virtual bool isValid() const override

Determine if the stream object contains valid data.

Note

Where inherited classes are initialised by constructor this method indicates whether that was successful or not (e.g. FileStream)

Return values:

bool – true if valid, false if invalid

inline size_t getPos() const

Get the offset of cursor from beginning of data.

Return values:

size_t – Cursor offset

inline size_t getSize() const

Get the total file size.

Return values:

size_tFile size

inline virtual int available() override

Return the maximum bytes available to read, from current position.

Return values:

int – -1 is returned when the size cannot be determined

virtual String id() const override

Returns unique id of the resource.

Return values:

String – the unique id of the stream.

bool truncate(size_t newSize)

Reduce the file size.

Parameters:

newSize

Return values:

bool – true on success

inline bool truncate()

Truncate file at current position.

Return values:

bool – true on success

class HtmlDirectoryTemplate : public IFS::DirectoryTemplate
#include <HtmlDirectoryTemplate.h>

Read-only stream access to directory listing with HTML output.

class JsonDirectoryTemplate : public IFS::DirectoryTemplate
#include <JsonDirectoryTemplate.h>

Read-only stream providing directory listing in JSON format.

struct ACL
#include <Access.h>
struct AttributeEnum
#include <Attribute.h>

Attribute information passed to enumeration callback.

Public Members

AttributeTag tag = {}

The attribute tag.

size_t size = {0}

Size of returned data, may be less than attrsize if buffer too small.

size_t attrsize = {0}

Actual attribute size.

void *buffer

User-provided buffer with tag value.

size_t bufsize

User-provided buffer size.

struct Compression
#include <Compression.h>

A compression descriptor.

class Directory : public IFS::FsBase
#include <Directory.h>

Wrapper class for enumerating a directory.

Public Functions

bool open(const String &dirName = nullptr)

Open a directory and attach this stream object to it.

Note

call getLastError() to determine cause of failure

Parameters:

dirName – Default is root directory

Return values:

bool – true on success, false on error

void close()

Close directory.

bool rewind()

Rewind directory stream to start so it can be re-enumerated.

Note

call getLastError() to determine cause of failure

Return values:

bool – true on success, false on error

inline const String &getDirName() const

Name of directory stream is attached to.

Return values:

String – invalid if stream isn’t open

inline bool dirExist() const

Determine if directory exists.

Return values:

bool – true if stream is attached to a directory

String getPath() const

Get path with leading separator /path/to/dir.

String getParent() const

Get parent directory.

Return values:

String – invalid if there is no parent directory

struct Extent
#include <Extent.h>

Defines the location of a contiguous run of file data.

e.g. offset: 0 length: 251 skip: 5 repeat: 30 Describes 30 blocks of data each of 251 bytes with a 5-byte gap between them. Thus run contains 7530 bytes of file data, consuming 7680 bytes of storage.

skip/repeat is necessary for SPIFFS to keep RAM usage sensible. Thus, a sample 267kByte application image has 1091 extents, but requires perhaps only 60 entries.

Public Members

storage_size_t offset

From start of partition.

uint32_t length

In bytes.

uint16_t skip

Skip bytes to next repeat.

uint16_t repeat

Number of repeats.

class File : public IFS::FsBase
#include <File.h>

Wraps up all file access methods.

Common flag combinations

static constexpr OpenFlags ReadOnly = {OpenFlag::Read}

Read-only.

static constexpr OpenFlags WriteOnly = {OpenFlag::Write}

Write-only.

static constexpr OpenFlags ReadWrite = {OpenFlag::Read | OpenFlag::Write}

Read + Write.

static constexpr OpenFlags Create = {OpenFlag::Create}

Create file if it doesn’t exist.

static constexpr OpenFlags Append = {OpenFlag::Append}

Append to existing file.

static constexpr OpenFlags Truncate = {OpenFlag::Truncate}

Truncate existing file to zero length.

static constexpr OpenFlags CreateNewAlways{OpenFlag::Create | OpenFlag::Truncate}

Create new file or overwrite file if it exists.

Public Functions

inline bool stat(Stat &stat)

get file information

Parameters:

stat – structure to return information in, may be null

Return values:

bool – true on success

inline int control(ControlCode code, void *buffer, size_t bufSize)

Low-level and non-standard file control operations.

To simplify usage the same buffer is used for both input and output. Only the size of the buffer is provided. If a specific FCNTL code requires more information then it will be contained within the provided data.

Parameters:
  • code – FCNTL_XXX code

  • buffer – Input/Output buffer

  • bufSize – Size of buffer

Return values:

int – error code or, on success, data size

template<typename T>
inline bool open(const T &path, OpenFlags flags = OpenFlag::Read)

open a file by name/path

Parameters:
  • path – full path to file

  • flags – opens for opening file

Return values:

bool – true on success

inline bool close()

close an open file

Return values:

bool – true on success

inline int read(void *data, size_t size)

read content from a file and advance cursor

Parameters:
  • data – buffer to write into

  • size – size of file buffer, maximum number of bytes to read

Return values:

int – number of bytes read or error code

inline int write(const void *data, size_t size)

write content to a file at current position and advance cursor

Parameters:
  • data – buffer to read from

  • size – number of bytes to write

Return values:

int – number of bytes written or error code

inline file_offset_t seek(file_offset_t offset, SeekOrigin origin)

change file read/write position

Parameters:
  • offset – position relative to origin

  • origin – where to seek from (start/end or current position)

Return values:

int – current position or error code

inline bool eof()

determine if current file position is at end of file

Return values:

bool – true if at EOF or file is invalid

inline file_offset_t tell()

get current file position

Return values:

int32_t – current position relative to start of file, or error code

inline bool truncate(file_size_t new_size)

Truncate (reduce) the size of an open file.

Parameters:

newSize

Return values:

bool – true on success

inline bool truncate()

Truncate an open file at the current cursor position.

Return values:

bool – true on success

inline bool flush()

flush any buffered data to physical media

Return values:

bool – true on success

inline bool setacl(const ACL &acl)

Set access control information for file.

Parameters:

acl

Return values:

bool – true on success

inline bool settime(time_t mtime)

Set modification time for file.

Note

any subsequent writes to file will reset this to current time

Return values:

bool – true on success

inline bool setcompression(const Compression &compression)

Set file compression information.

Parameters:

compression

Return values:

bool – true on success

inline bool remove()

remove (delete) an open file (and close it)

Return values:

bool – true on success

inline file_size_t getSize()

Get size of file.

Return values:

uint32_t – Size of file in bytes, 0 on error

inline file_offset_t readContent(size_t size, ReadContentCallback callback)

Read from current file position and invoke callback for each block read.

Parameters:
  • size – Maximum number of bytes to read

  • callback

Return values:

int – Number of bytes processed, or error code

inline file_offset_t readContent(ReadContentCallback callback)

Read from current file position to end of file and invoke callback for each block read.

Parameters:

callback

Return values:

file_offset_t – Number of bytes processed, or error code

inline String getContent()

Read content of the file, from current position.

Note

After calling this function the content of the file is placed in to a string. The result will be an invalid String (equates to false) if the file could not be read. If the file exists, but is empty, the result will be an empty string “”.

Return values:

StringString variable in to which to read the file content

inline FileHandle release()

Return current file handle and release ownership.

class FileCopier
#include <FileCopier.h>

Class to manage copying of files and directories including attributes.

Public Types

using ErrorHandler = Delegate<bool(const ErrorInfo &info)>

Return true to ignore error and continue copying, false to stop.

struct ErrorInfo
#include <FileCopier.h>

Error information passed to callback.

class FileSystem : public IFS::IFileSystem
#include <FileSystem.h>

Installable File System base class.

Adds additional methods to ease use over base IFileSystem.

rename a file

param oldpath:

param newpath:

retval int:

error code

using ReadContentCallback = Delegate<int(const char *buffer, size_t size)>

Callback for readContent method.

Param buffer:

Param size:

Retval int:

Return number of bytes consumed, < size to stop If < 0 then this is returned as error code to readContent call.

inline int remove(const String &path)

remove (delete) a file by path

Parameters:

path

Return values:

int – error code

template<typename T>
inline int setacl(const T &file, const ACL &acl)

Set access control information for file.

Parameters:
  • file – handle or path to file

  • acl

Return values:

int – error code

template<typename T>
inline int setattr(const T &file, FileAttributes attr)

Set file attributes.

Parameters:
  • file – handle or path to file

  • attr

Return values:

int – error code

template<typename T>
inline int settime(const T &file, time_t mtime)

Set modification time for file.

Note

any subsequent writes to file will reset this to current time

Parameters:

file – handle or path to file

Return values:

int – error code

template<typename T>
inline int setcompression(const T &file, const Compression &compression)

Set file compression information.

Parameters:
  • file

  • compression

Return values:

int – error code

file_size_t getSize(FileHandle file)

Get size of file.

Parameters:

fileFile handle

Return values:

file_size_t – Size of file in bytes, 0 on error

file_size_t getSize(const char *fileName)

Get size of file.

Parameters:

fileName – Name of file

Return values:

file_size_t – Size of file in bytes, 0 on error

file_offset_t readContent(FileHandle file, size_t size, ReadContentCallback callback)

Read from current file position and invoke callback for each block read.

Parameters:
  • file

  • size – Maximum number of bytes to read

  • callback

Return values:

file_offset_t – Number of bytes processed, or error code

file_offset_t readContent(FileHandle file, ReadContentCallback callback)

Read from current file position to end of file and invoke callback for each block read.

Parameters:
  • file

  • callback

Return values:

file_offset_t – Number of bytes processed, or error code

file_offset_t readContent(const String &filename, ReadContentCallback callback)

Read entire file content in blocks, invoking callback after every read.

Parameters:
  • filename

  • callback

Return values:

file_offset_t – Number of bytes processed, or error code

int remove(const char *path) = 0

remove (delete) a file by path

Parameters:

path

Return values:

int – error code

Read content of a file

After calling this function the content of the file is placed in to a c-string Ensure there is sufficient space in the buffer for file content plus extra trailing null, i.e. at least bufSize + 1 Always check the return value!

param fileName:

Name of file to read from

param buffer:

Pointer to a character buffer in to which to read the file content

param bufSize:

Quantity of bytes to read from file

retval size_t:

Quantity of bytes read from file or zero on failure

Returns 0 if the file could not be read

Create or replace file with defined content

This function creates a new file or replaces an existing file and populates the file with the content of a c-string buffer.

param fileName:

Name of file to create or replace

param content:

Pointer to c-string containing content to populate file with

retval int:

Number of bytes transferred or error code

param length:

(optional) number of characters to write

Public Functions

inline int opendir(const String &path, DirHandle &dir)

open a directory for reading

int makedirs(const char *path)

Create a directory and any intermediate directories if they do not already exist.

Parameters:

path – Path to directory. If no trailing ‘/’ is present the final element is considered a filename.

Return values:

int – error code

int truncate(const char *fileName, file_size_t newSize)

Truncate a file to a specific size.

Parameters:

fileNameFile to truncate

Return values:

int – new file size, or error code

String getContent(const String &fileName)

Read content of a file.

Note

After calling this function the content of the file is placed in to a string. The result will be an invalid String (equates to false) if the file could not be read. If the file exists, but is empty, the result will be an empty string “”.

Parameters:

fileName – Name of file to read from

Return values:

StringString variable in to which to read the file content

int opendir(const char *path, DirHandle &dir) = 0

open a directory for reading

Parameters:
  • path – path to directory. nullptr is interpreted as root directory

  • dir – returns a pointer to the directory object

Return values:

int – error code

int mkdir(const char *path) = 0

Create a directory.

Only the final directory in the path is guaranteed to be created. Usually, this call will fail if intermediate directories are not present. Use IFS::FileSystem::makedirs() for this purpose.

Parameters:

path – Path to directory

Return values:

int – error code

int stat(const char *path, Stat *stat) = 0

get file information

Returned stat will indicate whether the path is a mountpoint or directory. For a mount point, stats for the root directory of the mounted filesystem must be obtained by opening a handle then using fstat:

int handle = fs.open("path-to-mountpoint");
Stat stat;
fs.fstat(handle, &stat);
fs.close(handle);
Parameters:
  • path – name or path of file/directory/mountpoint

  • s – structure to return information in, may be null to do a simple file existence check

Return values:

int – error code

int fstat(FileHandle file, Stat *stat) = 0

get file information

Parameters:
  • file – handle to open file

  • stat – structure to return information in, may be null

Return values:

int – error code

FileHandle open(const char *path, OpenFlags flags) = 0

open a file (or directory) by path

This function may also be used to obtain a directory handle to perform various operations such as enumerating attributes. Calls to read or write on such handles will typically fail.

Parameters:
  • path – full path to file

  • flags – Desired access and other options

Return values:

FileHandle – file handle or error code

int ftruncate(FileHandle file, file_size_t new_size) = 0

Truncate (reduce) the size of an open file.

Note

In POSIX ftruncate() can also make the file bigger, however SPIFFS can only reduce the file size and will return an error if newSize > fileSize

Parameters:
  • file – Open file handle, must have Write access

  • newSize

Return values:

intError code

int rename(const char *oldpath, const char *newpath) = 0

rename a file

Parameters:
  • oldpath

  • newpath

Return values:

int – error code

class FsBase
#include <FsBase.h>

Subclassed by IFS::Directory, IFS::FWFS::ArchiveStream, IFS::File, IFS::FileStream

Public Functions

inline int getLastError()

determine if an error occurred during operation

Return values:

int – filesystem error code

class IFileSystem
#include <IFileSystem.h>

Installable File System base class.

Construction and initialisation of a filing system is implementation-dependent so there are no methods here for that.

Methods are defined as virtual abstract unless we actually have a default base implementation. Whilst some methods could just return Error::NotImplemented by default, keeping them abstract forces all file system implementations to consider them so provides an extra check for completeness.

Note

The ‘I’ implies Installable but could be for Interface :-)

Subclassed by IFS::FAT::FileSystem, IFS::FWFS::FileSystem, IFS::FileSystem, IFS::Gdb::FileSystem, IFS::HYFS::FileSystem, IFS::Host::FileSystem

Public Functions

inline virtual ~IFileSystem()

Filing system implementations should dismount and cleanup here.

virtual int mount() = 0

Mount file system, performing any required initialisation.

Return values:

error – code

virtual int getinfo(Info &info) = 0

get filing system information

Parameters:

info – structure to read information into

Return values:

int – error code

inline virtual int setProfiler(IProfiler *profiler)

Set profiler instance to enable debugging and performance assessment.

Parameters:

profiler

Return values:

int – error code - profiling may not be supported on all filesystems

inline virtual String getErrorString(int err)

get the text for a returned error code

Return values:

String

inline virtual int setVolume(uint8_t index, IFileSystem *fileSystem)

Set volume for mountpoint.

Parameters:
  • index – Volume index

  • fileSystem – The filesystem to root at this mountpoint

Return values:

int – error code

virtual int opendir(const char *path, DirHandle &dir) = 0

open a directory for reading

Parameters:
  • path – path to directory. nullptr is interpreted as root directory

  • dir – returns a pointer to the directory object

Return values:

int – error code

virtual int readdir(DirHandle dir, Stat &stat) = 0

read a directory entry

Note

File system allocates entries structure as it usually needs to track other information. It releases memory when closedir() is called.

Parameters:
  • dir

  • stat

Return values:

int – error code

virtual int rewinddir(DirHandle dir) = 0

Reset directory read position to start.

Parameters:

dir

Return values:

int – error code

virtual int closedir(DirHandle dir) = 0

close a directory object

Parameters:

dir – directory to close

Return values:

int – error code

virtual int mkdir(const char *path) = 0

Create a directory.

Only the final directory in the path is guaranteed to be created. Usually, this call will fail if intermediate directories are not present. Use IFS::FileSystem::makedirs() for this purpose.

Parameters:

path – Path to directory

Return values:

int – error code

virtual int stat(const char *path, Stat *stat) = 0

get file information

Returned stat will indicate whether the path is a mountpoint or directory. For a mount point, stats for the root directory of the mounted filesystem must be obtained by opening a handle then using fstat:

int handle = fs.open("path-to-mountpoint");
Stat stat;
fs.fstat(handle, &stat);
fs.close(handle);
Parameters:
  • path – name or path of file/directory/mountpoint

  • s – structure to return information in, may be null to do a simple file existence check

Return values:

int – error code

virtual int fstat(FileHandle file, Stat *stat) = 0

get file information

Parameters:
  • file – handle to open file

  • stat – structure to return information in, may be null

Return values:

int – error code

inline virtual int fcontrol(FileHandle file, ControlCode code, void *buffer, size_t bufSize)

Low-level and non-standard file control operations.

To simplify usage the same buffer is used for both input and output. Only the size of the buffer is provided. If a specific FCNTL code requires more information then it will be contained within the provided data.

Parameters:
  • file

  • code – FCNTL_XXX code

  • buffer – Input/Output buffer

  • bufSize – Size of buffer

Return values:

int – error code or, on success, data size

virtual FileHandle open(const char *path, OpenFlags flags) = 0

open a file (or directory) by path

This function may also be used to obtain a directory handle to perform various operations such as enumerating attributes. Calls to read or write on such handles will typically fail.

Parameters:
  • path – full path to file

  • flags – Desired access and other options

Return values:

FileHandle – file handle or error code

virtual int close(FileHandle file) = 0

close an open file

Parameters:

file – handle to open file

Return values:

int – error code

virtual int read(FileHandle file, void *data, size_t size) = 0

read content from a file and advance cursor

Parameters:
  • file – handle to open file

  • data – buffer to write into

  • size – size of file buffer, maximum number of bytes to read

Return values:

int – number of bytes read or error code

virtual int write(FileHandle file, const void *data, size_t size) = 0

write content to a file at current position and advance cursor

Parameters:
  • file – handle to open file

  • data – buffer to read from

  • size – number of bytes to write

Return values:

int – number of bytes written or error code

virtual file_offset_t lseek(FileHandle file, file_offset_t offset, SeekOrigin origin) = 0

change file read/write position

Parameters:
  • file – handle to open file

  • offset – position relative to origin

  • origin – where to seek from (start/end or current position)

Return values:

file_offset_t – current position or error code

virtual int eof(FileHandle file) = 0

determine if current file position is at end of file

Parameters:

file – handle to open file

Return values:

int – 0 - not EOF, > 0 - at EOF, < 0 - error

virtual file_offset_t tell(FileHandle file) = 0

get current file position

Parameters:

file – handle to open file

Return values:

file_offset_t – current position relative to start of file, or error code

virtual int ftruncate(FileHandle file, file_size_t new_size) = 0

Truncate (reduce) the size of an open file.

Note

In POSIX ftruncate() can also make the file bigger, however SPIFFS can only reduce the file size and will return an error if newSize > fileSize

Parameters:
  • file – Open file handle, must have Write access

  • newSize

Return values:

intError code

virtual int flush(FileHandle file) = 0

flush any buffered data to physical media

Parameters:

file – handle to open file

Return values:

int – error code

virtual int fsetxattr(FileHandle file, AttributeTag tag, const void *data, size_t size) = 0

Set an extended attribute on an open file.

Note

Attributes may not be written to disk until flush() or close() are called

Parameters:
  • file – handle to open file

  • tag – The attribute to write

  • data – Content of the attribute. Pass nullptr to remove the attribute (if possible).

  • size – Size of the attribute in bytes

Return values:

int – error code

virtual int fgetxattr(FileHandle file, AttributeTag tag, void *buffer, size_t size) = 0

Get an extended attribute from an open file.

Parameters:
  • file – handle to open file

  • tag – The attribute to read

  • buffer – Buffer to receive attribute content

  • size – Size of the buffer

Return values:

int – error code, on success returns size of attribute (which may be larger than size)

virtual int fenumxattr(FileHandle file, AttributeEnumCallback callback, void *buffer, size_t bufsize) = 0

Enumerate attributes.

Parameters:
  • file – handle to open file

  • callback – Callback function to invoke for each attribute found

  • buffer – Buffer to use for reading attribute data. Use nullptr if only tags are required

  • bufsize – Size of buffer

Return values:

int – error code, on success returns number of attributes read

virtual int setxattr(const char *path, AttributeTag tag, const void *data, size_t size) = 0

Set an extended attribute for a file given its path.

Parameters:
  • path – Full path to file (or directory)

  • tag – The attribute to write

  • data – Content of the attribute. Pass nullptr to remove the attribute (if possible).

  • size – Size of the attribute in bytes

Return values:

int – error code

virtual int getxattr(const char *path, AttributeTag tag, void *buffer, size_t size) = 0

Get an attribute from a file given its path.

Parameters:
  • file – Full path to file (or directory)

  • tag – The attribute to read

  • buffer – Buffer to receive attribute content

  • size – Size of the buffer

Return values:

int – error code, on success returns size of attribute (which may be larger than size)

inline virtual int fgetextents(FileHandle file, Storage::Partition *part, Extent *list, uint16_t extcount)

Get extents for a file.

Parameters:
  • file – Handle to open file

  • part – Partition where the file lives (OUT, OPTIONAL)

  • list – Buffer for extents (OPTIONAL)

  • extcount – Maximum number of extents to return in list

Return values:

int – Total number of extents for file (may be larger than ‘extcount’), or error code

virtual int rename(const char *oldpath, const char *newpath) = 0

rename a file

Parameters:
  • oldpath

  • newpath

Return values:

int – error code

virtual int remove(const char *path) = 0

remove (delete) a file by path

Parameters:

path

Return values:

int – error code

virtual int fremove(FileHandle file) = 0

remove (delete) a file by handle

Parameters:

file – handle to open file

Return values:

int – error code

virtual int format() = 0

format the filing system

Note

this does a default format, returning file system to a fresh state The filing system implementation may define more specialised methods which can be called directly.

Return values:

int – error code

inline virtual int check()

Perform a file system consistency check.

Note

if possible, issues should be resolved. Returns 0 if file system checked out OK. Otherwise there were issues: < 0 for unrecoverable errors,

0 for recoverable errors.

Return values:

int – error code

struct Info
#include <IFileSystem.h>

Basic information about filing system.

Subclassed by IFS::IFileSystem::NameInfo

Public Members

Type type = {}

The filing system type identifier.

Attributes attr = {}

Attribute flags.

size_t maxNameLength = {255}

Maximum length of a single file name.

size_t maxPathLength = {255}

Maximum length of a full file path.

uint32_t volumeID = {0}

Unique identifier for volume.

NameBuffer name

Buffer for name.

volume_size_t volumeSize = {0}

Size of volume, in bytes.

volume_size_t freeSpace = {0}

Available space, in bytes.

struct NameInfo : public IFS::IFileSystem::Info
#include <IFileSystem.h>

Filing system information with buffer for name.

struct NameBuffer
#include <NameBuffer.h>

defines a ‘safe’ name buffer

Note

Instead of including a fixed name array in Stat (and IFileSystem::Info) structures, we use a NameBuffer to identify a separate buffer. This has several advantages:

- Maximum size is not fixed
- Finding and copying the name is optional
- Actual name length is returned in the 'length' field, regardless of size
- A NameBuffer structure (or one containing it) only requires initialising once before
a loop operation as buffer/size are preserved.
There are fancier ways to do this but a structure is transparent and requires no heap allocation.

Note

length always reflects the required name/path length, and may be longer than size.

Subclassed by IFS::FileNameBuffer

Public Functions

inline NameBuffer(String &s)

Make a NameBuffer point to contents of a String.

inline int copy(const char *src, uint16_t srclen)

copies text from a source buffer into a name buffer

Note

length field is always set to srclen, regardless of number of characters copied.

Parameters:
  • src – source name

  • srclen – number of characters in name

inline int addSep()

When building file paths this method simplified appending separators.

Note

if the path is not empty, a separator character is appended

Return values:

int – error code

inline char *endptr()

get a pointer to the next write position

Note

use space() to ensure buffer doesn’t overrun When writing text be sure to call terminate() when complete

Return values:

char*

inline uint16_t space()

get the number of free characters available

Note

returns 0 if buffer has overrun

Return values:

uint16_t

inline void terminate()

ensure the buffer has a nul terminator, even if it means overwriting content

inline bool overflow() const

determine if name buffer overflowed

Note

Compares returned length with buffer size; A nul terminator is always appended, so size should be >= (length + 1)

Public Members

char *buffer = {nullptr}

Buffer to store name.

uint16_t size = {0}

IN: Size of buffer.

uint16_t length = {0}

OUT: length of name.

struct FileNameBuffer : public IFS::NameBuffer
#include <NameBuffer.h>

a quick’n’dirty name buffer with maximum path allocation

class IProfiler
#include <Profiler.h>

Filesystems may optionally provide performance statistics.

Subclassed by IFS::Profiler

Public Functions

virtual void erase(storage_size_t address, size_t size) = 0

Called BEFORE an erase operation.

class Profiler : public IFS::IProfiler
#include <Profiler.h>

Public Functions

inline virtual void erase(storage_size_t address, size_t size) override

Called BEFORE an erase operation.

struct Stat
#include <Profiler.h>
struct Stat
#include <Stat.h>

File Status structure.

Subclassed by IFS::NameStat

Public Functions

inline Stat &operator=(const Stat &rhs)

assign content from another Stat structure

Note

All fields are copied as for a normal assignment, except for ‘name’, where rhs.name contents are copied into our name buffer.

inline bool isDir() const

Is this a directory (or mountpoint) ?

Public Members

IFileSystem *fs = {nullptr}

The filing system owning this file.

NameBuffer name

Name of file.

file_size_t size = {0}

Size of file in bytes.

FileID id = {0}

Internal file identifier.

TimeStamp mtime = {}

File modification time.

ACL acl = {UserRole::None, UserRole::None}

Access Control.

struct NameStat : public IFS::Stat
#include <Stat.h>

version of Stat with integrated name buffer

Note

provide for convenience

struct TimeStamp
#include <TimeStamp.h>

Manage IFS timestamps stored as an unsigned 32-bit value.

A signed 32-bit value containing seconds will overflow in about 136 years. time_t starts at 1970.

Public Functions

String toString(const char *dtsep = " ") const

Convert time to local time for display.

namespace Debug

Typedefs

using Options = BitSet<uint8_t, Option, 2>

Enums

enum class Option

Values:

enumerator recurse
enumerator attributes

Functions

void printFsInfo(Print &out, FileSystem &fs)
void printAttrInfo(Print &out, FileSystem &fs, const String &filename)
int listDirectory(Print &out, FileSystem &fs, const String &path, Options options = 0)
namespace Error

Enums

enum class Value

Values:

enumerator XX
enumerator MAX

Functions

String toString(int err)

get text for an error code

Parameters:

err

Return values:

String

inline bool isSystem(int err)

Determine if the given IFS error code is system-specific.

inline int fromSystem(int syscode)

Translate system error code into IFS error code.

inline int toSystem(int err)

Translate IFS error code into SYSTEM code.

Variables

constexpr ErrorCode USER = {-100}
constexpr ErrorCode SYSTEM = {-1000}
namespace FAT

Functions

int translateFatfsResult(uint8_t result, bool diskio_write)
String fatfsErrorToStr(uint8_t err)
ErrorCode calculateFatParam(Partition partition, const FormatOptions &opt, FatParam &param)

Deduce FAT volume parameters for given space.

Parameters:
  • partition – The partition to format

  • opt – Formatting options

  • param – On success, contains calculated parameters for FAT volume

Return values:

ErrorCode – When partitioning using MBR format, this method can be used to determine the Sys indicator value setting.

ErrorCode formatVolume(Partition partition, const FatParam &param)

Format partition using pre-calculated FAT parameters.

Parameters:
  • partition – The partition to format

  • param – Detailed FAT parameters (returned from calculateFatParam)

Return values:

ErrorCode – This function allows fine control over exactly how a FAT partition is constructed. Generally the calculateFatParam function should be used to populate the param structure, then any modifications can be made as required before actually formatting the volume.

inline ErrorCode formatVolume(Partition partition, const FormatOptions &opt = {})

Format partition with a blank FAT volume.

Parameters:
  • partition – The partition to format

  • opt – Formatting options

Return values:

ErrorCode

class FileSystem : public IFS::IFileSystem
#include <FileSystem.h>

Wraps fatfs

Public Functions

virtual int mount() override

Mount file system, performing any required initialisation.

Return values:

error – code

virtual int getinfo(Info &info) override

get filing system information

Parameters:

info – structure to read information into

Return values:

int – error code

virtual int setProfiler(IProfiler *profiler) override

Set profiler instance to enable debugging and performance assessment.

Parameters:

profiler

Return values:

int – error code - profiling may not be supported on all filesystems

virtual String getErrorString(int err) override

get the text for a returned error code

Return values:

String

virtual int opendir(const char *path, DirHandle &dir) override

open a directory for reading

Parameters:
  • path – path to directory. nullptr is interpreted as root directory

  • dir – returns a pointer to the directory object

Return values:

int – error code

virtual int readdir(DirHandle dir, Stat &stat) override

read a directory entry

Note

File system allocates entries structure as it usually needs to track other information. It releases memory when closedir() is called.

Parameters:
  • dir

  • stat

Return values:

int – error code

virtual int rewinddir(DirHandle dir) override

Reset directory read position to start.

Parameters:

dir

Return values:

int – error code

virtual int closedir(DirHandle dir) override

close a directory object

Parameters:

dir – directory to close

Return values:

int – error code

virtual int mkdir(const char *path) override

Create a directory.

Only the final directory in the path is guaranteed to be created. Usually, this call will fail if intermediate directories are not present. Use IFS::FileSystem::makedirs() for this purpose.

Parameters:

path – Path to directory

Return values:

int – error code

virtual int stat(const char *path, Stat *stat) override

get file information

Returned stat will indicate whether the path is a mountpoint or directory. For a mount point, stats for the root directory of the mounted filesystem must be obtained by opening a handle then using fstat:

int handle = fs.open("path-to-mountpoint");
Stat stat;
fs.fstat(handle, &stat);
fs.close(handle);
Parameters:
  • path – name or path of file/directory/mountpoint

  • s – structure to return information in, may be null to do a simple file existence check

Return values:

int – error code

virtual int fstat(FileHandle file, Stat *stat) override

get file information

Parameters:
  • file – handle to open file

  • stat – structure to return information in, may be null

Return values:

int – error code

virtual int fcontrol(FileHandle file, ControlCode code, void *buffer, size_t bufSize) override

Low-level and non-standard file control operations.

To simplify usage the same buffer is used for both input and output. Only the size of the buffer is provided. If a specific FCNTL code requires more information then it will be contained within the provided data.

Parameters:
  • file

  • code – FCNTL_XXX code

  • buffer – Input/Output buffer

  • bufSize – Size of buffer

Return values:

int – error code or, on success, data size

virtual int fsetxattr(FileHandle file, AttributeTag tag, const void *data, size_t size) override

Set an extended attribute on an open file.

Note

Attributes may not be written to disk until flush() or close() are called

Parameters:
  • file – handle to open file

  • tag – The attribute to write

  • data – Content of the attribute. Pass nullptr to remove the attribute (if possible).

  • size – Size of the attribute in bytes

Return values:

int – error code

virtual int fgetxattr(FileHandle file, AttributeTag tag, void *buffer, size_t size) override

Get an extended attribute from an open file.

Parameters:
  • file – handle to open file

  • tag – The attribute to read

  • buffer – Buffer to receive attribute content

  • size – Size of the buffer

Return values:

int – error code, on success returns size of attribute (which may be larger than size)

virtual int fenumxattr(FileHandle file, AttributeEnumCallback callback, void *buffer, size_t bufsize) override

Enumerate attributes.

Parameters:
  • file – handle to open file

  • callback – Callback function to invoke for each attribute found

  • buffer – Buffer to use for reading attribute data. Use nullptr if only tags are required

  • bufsize – Size of buffer

Return values:

int – error code, on success returns number of attributes read

virtual int setxattr(const char *path, AttributeTag tag, const void *data, size_t size) override

Set an extended attribute for a file given its path.

Parameters:
  • path – Full path to file (or directory)

  • tag – The attribute to write

  • data – Content of the attribute. Pass nullptr to remove the attribute (if possible).

  • size – Size of the attribute in bytes

Return values:

int – error code

virtual int getxattr(const char *path, AttributeTag tag, void *buffer, size_t size) override

Get an attribute from a file given its path.

Parameters:
  • file – Full path to file (or directory)

  • tag – The attribute to read

  • buffer – Buffer to receive attribute content

  • size – Size of the buffer

Return values:

int – error code, on success returns size of attribute (which may be larger than size)

virtual FileHandle open(const char *path, OpenFlags flags) override

open a file (or directory) by path

This function may also be used to obtain a directory handle to perform various operations such as enumerating attributes. Calls to read or write on such handles will typically fail.

Parameters:
  • path – full path to file

  • flags – Desired access and other options

Return values:

FileHandle – file handle or error code

virtual int close(FileHandle file) override

close an open file

Parameters:

file – handle to open file

Return values:

int – error code

virtual int read(FileHandle file, void *data, size_t size) override

read content from a file and advance cursor

Parameters:
  • file – handle to open file

  • data – buffer to write into

  • size – size of file buffer, maximum number of bytes to read

Return values:

int – number of bytes read or error code

virtual int write(FileHandle file, const void *data, size_t size) override

write content to a file at current position and advance cursor

Parameters:
  • file – handle to open file

  • data – buffer to read from

  • size – number of bytes to write

Return values:

int – number of bytes written or error code

virtual file_offset_t lseek(FileHandle file, file_offset_t offset, SeekOrigin origin) override

change file read/write position

Parameters:
  • file – handle to open file

  • offset – position relative to origin

  • origin – where to seek from (start/end or current position)

Return values:

file_offset_t – current position or error code

virtual int eof(FileHandle file) override

determine if current file position is at end of file

Parameters:

file – handle to open file

Return values:

int – 0 - not EOF, > 0 - at EOF, < 0 - error

virtual file_offset_t tell(FileHandle file) override

get current file position

Parameters:

file – handle to open file

Return values:

file_offset_t – current position relative to start of file, or error code

virtual int ftruncate(FileHandle file, file_size_t new_size) override

Truncate (reduce) the size of an open file.

Note

In POSIX ftruncate() can also make the file bigger, however SPIFFS can only reduce the file size and will return an error if newSize > fileSize

Parameters:
  • file – Open file handle, must have Write access

  • newSize

Return values:

intError code

virtual int flush(FileHandle file) override

flush any buffered data to physical media

Parameters:

file – handle to open file

Return values:

int – error code

virtual int rename(const char *oldpath, const char *newpath) override

rename a file

Parameters:
  • oldpath

  • newpath

Return values:

int – error code

virtual int remove(const char *path) override

remove (delete) a file by path

Parameters:

path

Return values:

int – error code

virtual int fremove(FileHandle file) override

remove (delete) a file by handle

Parameters:

file – handle to open file

Return values:

int – error code

virtual int format() override

format the filing system

Note

this does a default format, returning file system to a fresh state The filing system implementation may define more specialised methods which can be called directly.

Return values:

int – error code

virtual int check() override

Perform a file system consistency check.

Note

if possible, issues should be resolved. Returns 0 if file system checked out OK. Otherwise there were issues: < 0 for unrecoverable errors,

0 for recoverable errors.

Return values:

int – error code

struct FormatOptions
#include <Format.h>

Formatting options.

Public Members

SysTypes types = {0}

Valid partition format types.

uint8_t numFats = {1}

Number of FATs (1 or 2)

unsigned align = {0}

Data area alignment (sector)

unsigned numRootEntries = {512}

Number of root directory entries.

uint32_t clusterSize = {4096}

Cluster size (byte)

struct FatParam
#include <Format.h>

Public Members

uint32_t sectorsPerBlock

Flash erase block size.

uint16_t sectorsPerCluster

Set to 0 for auto-calculation.

namespace FWFS

Functions

template<typename T>
static T at_offset(const void *current, int offset)
template<typename T>
static T at_offset(void *current, int offset)
FileAttributes getFileAttributes(Object::Attributes objattr)
Object::Attributes getObjectAttributes(FileAttributes fileAttr)

Variables

constexpr size_t FWFS_BASE_OFFSET = {sizeof(uint32_t)}
constexpr uint32_t FWFILESYS_START_MARKER = {0x53465746}
constexpr uint32_t FWFILESYS_END_MARKER = {0x46574653}
class ArchiveStream : public IFS::FsBase, public IDataSourceStream
#include <ArchiveStream.h>

Supports direct streaming into FWFS archive format.

Data needs to be enumerated so that all child files and directories are written first. As this happens, the parent directory is built as a list of object references.

The size of all child objects must be known before the containing object is written out. It should be sufficient to buffer all of these into an internal stream. This would only be a problem if large child objects are used. This is possible, but the builder avoids doing it and we should too.

Top-level object is a Volume, below that is the root Directory. That means objects are written in the order: child files/directories Root directory Volume End marker

Subclassed by ArchiveStream

Public Functions

inline ArchiveStream(FileSystem *fileSystem, VolumeInfo volumeInfo, String rootPath = nullptr, Flags flags = 0)

Construct an archive stream.

Parameters:
  • fileSystem – The filesystem to read

  • rootPath – Where to root the generated filesystem

  • flags

inline virtual bool filterStat(const Stat &stat)

Override this method to filter items.

Use to omit temporary files or directories.

Parameters:

stat – The current item

Return values:

bool – Return true to process the item, false to skip it

inline virtual IBlockEncoder *createEncoder(FileInfo &file)

Override this method to implement custom encoding such as compression or encryption.

To support compression or encryption, this method can create the appropriate stream type and set the appropriate attributes using methods of FileInfo.

Parameters:

file – Details of the file being archived

Return values:

IBlockEncoder* – Stream to use for file content. Return nullptr for default behaviour.

inline const String &getCurrentPath() const

Get the current path being processed.

virtual uint16_t readMemoryBlock(char *data, int bufSize) override

Read a block of memory.

Todo:

Should IDataSourceStream::readMemoryBlock return same data type as its bufSize param?

Parameters:
  • data – Pointer to the data to be read

  • bufSize – Quantity of chars to read

Return values:

uint16_t – Quantity of chars read

virtual int seekFrom(int offset, SeekOrigin origin) override

Change position in stream.

Note

This method is implemented by streams which support random seeking, such as files and memory streams.

Parameters:
  • offset

  • origin

Return values:

New – position, < 0 on error

inline virtual bool isFinished() override

Check if all data has been read.

Return values:

bool – True on success.

inline virtual MimeType getMimeType() const override

Get MIME type for stream content.

Return values:

MimeType

void reset()

Reset stream to beginning.

class FileInfo
#include <ArchiveStream.h>

Passed to callbacks to allow modification of output data.

Public Functions

int setAttribute(AttributeTag tag, const void *data, size_t size)

Set an additional attribute on the file.

These are written out before existing file metadata is copied, so will take priority. For example, if we set the compression attribute here then that is the one which the filesystem will use when mounted. However, the original compression attribute will still be present which may be helpful.

template<typename ...ParamTypes>
inline int setUserAttribute(uint8_t tagValue, ParamTypes... params)

Set an additional user attribute.

struct VolumeInfo
#include <ArchiveStream.h>

Public Members

String name

Volume Name.

uint32_t id = {0}

File system ID to store.

TimeStamp creationTime = {0}

Volume creation time, default is current system time (UTC)

class IBlockEncoder
#include <BlockEncoder.h>

Virtual base class to support (file) data encryption and compression.

Encryption and compression are typically done in blocks of a fixed size. To support these operations an instance of this class is created which encodes data one block at a time. Each block is stored separately and the resulting file consists of a chain of these blocks. This is natively supported by FWFS.

If the final data size is known in advance then the implementation will return just a single data stream.

Subclassed by IFS::FWFS::BasicEncoder

Public Functions

virtual IDataSourceStream *getNextStream() = 0

@Implement this method and return nullptr when all blocks have been encoded.

The stream returned must know it’s size (i.e. available() must not return -1). The encoder owns any stream objects created so is responsible for destroying them when finished. This allows them to be re-used if appropriate.

class BasicEncoder : public IFS::FWFS::IBlockEncoder
#include <BlockEncoder.h>

Public Functions

inline virtual IDataSourceStream *getNextStream() override

@Implement this method and return nullptr when all blocks have been encoded.

The stream returned must know it’s size (i.e. available() must not return -1). The encoder owns any stream objects created so is responsible for destroying them when finished. This allows them to be re-used if appropriate.

struct FWFileDesc
#include <FileSystem.h>

FWFS File Descriptor.

Public Members

FWObjDesc odFile

File object.

uint32_t dataSize

Total size of data.

uint32_t cursor

Current read/write offset within file data.

struct FWVolume
#include <FileSystem.h>

FWFS Volume definition for mount points.

class FileSystem : public IFS::IFileSystem
#include <FileSystem.h>

Implementation of firmware filing system using IFS.

Public Functions

virtual int mount() override

Mount file system, performing any required initialisation.

Return values:

error – code

virtual int getinfo(Info &info) override

get filing system information

Parameters:

info – structure to read information into

Return values:

int – error code

virtual String getErrorString(int err) override

get the text for a returned error code

Return values:

String

virtual int setVolume(uint8_t index, IFileSystem *fileSystem) override

Set volume for mountpoint.

Parameters:
  • index – Volume index

  • fileSystem – The filesystem to root at this mountpoint

Return values:

int – error code

virtual int opendir(const char *path, DirHandle &dir) override

open a directory for reading

Parameters:
  • path – path to directory. nullptr is interpreted as root directory

  • dir – returns a pointer to the directory object

Return values:

int – error code

virtual int readdir(DirHandle dir, Stat &stat) override

read a directory entry

Note

File system allocates entries structure as it usually needs to track other information. It releases memory when closedir() is called.

Parameters:
  • dir

  • stat

Return values:

int – error code

virtual int rewinddir(DirHandle dir) override

Reset directory read position to start.

Parameters:

dir

Return values:

int – error code

virtual int closedir(DirHandle dir) override

close a directory object

Parameters:

dir – directory to close

Return values:

int – error code

virtual int mkdir(const char *path) override

Create a directory.

Only the final directory in the path is guaranteed to be created. Usually, this call will fail if intermediate directories are not present. Use IFS::FileSystem::makedirs() for this purpose.

Parameters:

path – Path to directory

Return values:

int – error code

virtual int stat(const char *path, Stat *stat) override

get file information

Returned stat will indicate whether the path is a mountpoint or directory. For a mount point, stats for the root directory of the mounted filesystem must be obtained by opening a handle then using fstat:

int handle = fs.open("path-to-mountpoint");
Stat stat;
fs.fstat(handle, &stat);
fs.close(handle);
Parameters:
  • path – name or path of file/directory/mountpoint

  • s – structure to return information in, may be null to do a simple file existence check

Return values:

int – error code

virtual int fstat(FileHandle file, Stat *stat) override

get file information

Parameters:
  • file – handle to open file

  • stat – structure to return information in, may be null

Return values:

int – error code

virtual int fcontrol(FileHandle file, ControlCode code, void *buffer, size_t bufSize) override

Low-level and non-standard file control operations.

To simplify usage the same buffer is used for both input and output. Only the size of the buffer is provided. If a specific FCNTL code requires more information then it will be contained within the provided data.

Parameters:
  • file

  • code – FCNTL_XXX code

  • buffer – Input/Output buffer

  • bufSize – Size of buffer

Return values:

int – error code or, on success, data size

virtual int fsetxattr(FileHandle file, AttributeTag tag, const void *data, size_t size) override

Set an extended attribute on an open file.

Note

Attributes may not be written to disk until flush() or close() are called

Parameters:
  • file – handle to open file

  • tag – The attribute to write

  • data – Content of the attribute. Pass nullptr to remove the attribute (if possible).

  • size – Size of the attribute in bytes

Return values:

int – error code

virtual int fgetxattr(FileHandle file, AttributeTag tag, void *buffer, size_t size) override

Get an extended attribute from an open file.

Parameters:
  • file – handle to open file

  • tag – The attribute to read

  • buffer – Buffer to receive attribute content

  • size – Size of the buffer

Return values:

int – error code, on success returns size of attribute (which may be larger than size)

virtual int fenumxattr(FileHandle file, AttributeEnumCallback callback, void *buffer, size_t bufsize) override

Enumerate attributes.

Parameters:
  • file – handle to open file

  • callback – Callback function to invoke for each attribute found

  • buffer – Buffer to use for reading attribute data. Use nullptr if only tags are required

  • bufsize – Size of buffer

Return values:

int – error code, on success returns number of attributes read

virtual int setxattr(const char *path, AttributeTag tag, const void *data, size_t size) override

Set an extended attribute for a file given its path.

Parameters:
  • path – Full path to file (or directory)

  • tag – The attribute to write

  • data – Content of the attribute. Pass nullptr to remove the attribute (if possible).

  • size – Size of the attribute in bytes

Return values:

int – error code

virtual int getxattr(const char *path, AttributeTag tag, void *buffer, size_t size) override

Get an attribute from a file given its path.

Parameters:
  • file – Full path to file (or directory)

  • tag – The attribute to read

  • buffer – Buffer to receive attribute content

  • size – Size of the buffer

Return values:

int – error code, on success returns size of attribute (which may be larger than size)

virtual FileHandle open(const char *path, OpenFlags flags) override

open a file (or directory) by path

This function may also be used to obtain a directory handle to perform various operations such as enumerating attributes. Calls to read or write on such handles will typically fail.

Parameters:
  • path – full path to file

  • flags – Desired access and other options

Return values:

FileHandle – file handle or error code

virtual int close(FileHandle file) override

close an open file

Parameters:

file – handle to open file

Return values:

int – error code

virtual int read(FileHandle file, void *data, size_t size) override

read content from a file and advance cursor

Parameters:
  • file – handle to open file

  • data – buffer to write into

  • size – size of file buffer, maximum number of bytes to read

Return values:

int – number of bytes read or error code

virtual int write(FileHandle file, const void *data, size_t size) override

write content to a file at current position and advance cursor

Parameters:
  • file – handle to open file

  • data – buffer to read from

  • size – number of bytes to write

Return values:

int – number of bytes written or error code

virtual file_offset_t lseek(FileHandle file, file_offset_t offset, SeekOrigin origin) override

change file read/write position

Parameters:
  • file – handle to open file

  • offset – position relative to origin

  • origin – where to seek from (start/end or current position)

Return values:

file_offset_t – current position or error code

virtual int eof(FileHandle file) override

determine if current file position is at end of file

Parameters:

file – handle to open file

Return values:

int – 0 - not EOF, > 0 - at EOF, < 0 - error

virtual file_offset_t tell(FileHandle file) override

get current file position

Parameters:

file – handle to open file

Return values:

file_offset_t – current position relative to start of file, or error code

virtual int ftruncate(FileHandle file, file_size_t new_size) override

Truncate (reduce) the size of an open file.

Note

In POSIX ftruncate() can also make the file bigger, however SPIFFS can only reduce the file size and will return an error if newSize > fileSize

Parameters:
  • file – Open file handle, must have Write access

  • newSize

Return values:

intError code

virtual int flush(FileHandle file) override

flush any buffered data to physical media

Parameters:

file – handle to open file

Return values:

int – error code

virtual int fgetextents(FileHandle file, Storage::Partition *part, Extent *list, uint16_t extcount) override

Get extents for a file.

Parameters:
  • file – Handle to open file

  • part – Partition where the file lives (OUT, OPTIONAL)

  • list – Buffer for extents (OPTIONAL)

  • extcount – Maximum number of extents to return in list

Return values:

int – Total number of extents for file (may be larger than ‘extcount’), or error code

virtual int rename(const char *oldpath, const char *newpath) override

rename a file

Parameters:
  • oldpath

  • newpath

Return values:

int – error code

virtual int remove(const char *path) override

remove (delete) a file by path

Parameters:

path

Return values:

int – error code

virtual int fremove(FileHandle file) override

remove (delete) a file by handle

Parameters:

file – handle to open file

Return values:

int – error code

inline virtual int format() override

format the filing system

Note

this does a default format, returning file system to a fresh state The filing system implementation may define more specialised methods which can be called directly.

Return values:

int – error code

inline virtual int check() override

Perform a file system consistency check.

Note

if possible, issues should be resolved. Returns 0 if file system checked out OK. Otherwise there were issues: < 0 for unrecoverable errors,

0 for recoverable errors.

Return values:

int – error code

struct Object
#include <Object.h>

Object structure.

Note

all objects conform to this structure. Only the first word (4 bytes) are required to nagivate the file system. All objects have an 8, 16 or 24-bit size field. Content is always immediately after this field. Reference objects are always 8-bit sized.

Public Types

enum class Attribute

Object attributes.

Note

these are bit values

Values:

enumerator ReadOnly

Object should not be modified or deleted.

enumerator Archive

Object modified flag.

Note

Object has been changed on disk. Typically used by backup applications

enumerator Encrypted

Object data is encrypted.

This is just a hint. Applications will typically provide additional user metadata to provide any additional information required for decryption.

enumerator MAX
using ID = uint32_t

Object identifier (offset from start of image)

Public Functions

inline size_t contentOffset() const

return offset to start of object content

inline uint32_t contentSize() const

return size of object content, excluding header and size fields

Note

must check return error code

Return values:

size – or error code

inline uint32_t size() const

total size this object occupies in the image

Return values:

size – or error code

Public Members

uint8_t typeData

Stored type plus flag.

uint32_t value

32-bit identifier, e.g. volume ID

uint16_t _contentSize

Object size (excluding this header)

uint8_t namelen

Length of object name.

uint8_t _contentSizeHigh

Allows data up to 16MByte.

struct FWObjDesc
#include <Object.h>

FWFS Object Descriptor.

Public Members

Object::ID id

location

Object obj = {}

The object structure.

class ObjectBuffer
#include <ObjectBuffer.h>

Class to manage writing object data into a stream.

namespace Gdb
class FileSystem : public IFS::IFileSystem
#include <FileSystem.h>

IFS implementation of Host filing system.

Public Functions

inline virtual int mount() override

Mount file system, performing any required initialisation.

Return values:

error – code

virtual int getinfo(Info &info) override

get filing system information

Parameters:

info – structure to read information into

Return values:

int – error code

virtual String getErrorString(int err) override

get the text for a returned error code

Return values:

String

inline virtual int opendir(const char *path, DirHandle &dir) override

open a directory for reading

Parameters:
  • path – path to directory. nullptr is interpreted as root directory

  • dir – returns a pointer to the directory object

Return values:

int – error code

inline virtual int rewinddir(DirHandle dir) override

Reset directory read position to start.

Parameters:

dir

Return values:

int – error code

inline virtual int readdir(DirHandle dir, Stat &stat) override

read a directory entry

Note

File system allocates entries structure as it usually needs to track other information. It releases memory when closedir() is called.

Parameters:
  • dir

  • stat

Return values:

int – error code

inline virtual int closedir(DirHandle dir) override

close a directory object

Parameters:

dir – directory to close

Return values:

int – error code

inline virtual int mkdir(const char *path) override

Create a directory.

Only the final directory in the path is guaranteed to be created. Usually, this call will fail if intermediate directories are not present. Use IFS::FileSystem::makedirs() for this purpose.

Parameters:

path – Path to directory

Return values:

int – error code

virtual int stat(const char *path, Stat *stat) override

get file information

Returned stat will indicate whether the path is a mountpoint or directory. For a mount point, stats for the root directory of the mounted filesystem must be obtained by opening a handle then using fstat:

int handle = fs.open("path-to-mountpoint");
Stat stat;
fs.fstat(handle, &stat);
fs.close(handle);
Parameters:
  • path – name or path of file/directory/mountpoint

  • s – structure to return information in, may be null to do a simple file existence check

Return values:

int – error code

virtual int fstat(FileHandle file, Stat *stat) override

get file information

Parameters:
  • file – handle to open file

  • stat – structure to return information in, may be null

Return values:

int – error code

inline virtual int fsetxattr(FileHandle file, AttributeTag tag, const void *data, size_t size) override

Set an extended attribute on an open file.

Note

Attributes may not be written to disk until flush() or close() are called

Parameters:
  • file – handle to open file

  • tag – The attribute to write

  • data – Content of the attribute. Pass nullptr to remove the attribute (if possible).

  • size – Size of the attribute in bytes

Return values:

int – error code

inline virtual int fgetxattr(FileHandle file, AttributeTag tag, void *buffer, size_t size) override

Get an extended attribute from an open file.

Parameters:
  • file – handle to open file

  • tag – The attribute to read

  • buffer – Buffer to receive attribute content

  • size – Size of the buffer

Return values:

int – error code, on success returns size of attribute (which may be larger than size)

inline virtual int fenumxattr(FileHandle file, AttributeEnumCallback callback, void *buffer, size_t bufsize) override

Enumerate attributes.

Parameters:
  • file – handle to open file

  • callback – Callback function to invoke for each attribute found

  • buffer – Buffer to use for reading attribute data. Use nullptr if only tags are required

  • bufsize – Size of buffer

Return values:

int – error code, on success returns number of attributes read

inline virtual int setxattr(const char *path, AttributeTag tag, const void *data, size_t size) override

Set an extended attribute for a file given its path.

Parameters:
  • path – Full path to file (or directory)

  • tag – The attribute to write

  • data – Content of the attribute. Pass nullptr to remove the attribute (if possible).

  • size – Size of the attribute in bytes

Return values:

int – error code

inline virtual int getxattr(const char *path, AttributeTag tag, void *buffer, size_t size) override

Get an attribute from a file given its path.

Parameters:
  • file – Full path to file (or directory)

  • tag – The attribute to read

  • buffer – Buffer to receive attribute content

  • size – Size of the buffer

Return values:

int – error code, on success returns size of attribute (which may be larger than size)

virtual FileHandle open(const char *path, OpenFlags flags) override

open a file (or directory) by path

This function may also be used to obtain a directory handle to perform various operations such as enumerating attributes. Calls to read or write on such handles will typically fail.

Parameters:
  • path – full path to file

  • flags – Desired access and other options

Return values:

FileHandle – file handle or error code

virtual int close(FileHandle file) override

close an open file

Parameters:

file – handle to open file

Return values:

int – error code

virtual int read(FileHandle file, void *data, size_t size) override

read content from a file and advance cursor

Parameters:
  • file – handle to open file

  • data – buffer to write into

  • size – size of file buffer, maximum number of bytes to read

Return values:

int – number of bytes read or error code

virtual int write(FileHandle file, const void *data, size_t size) override

write content to a file at current position and advance cursor

Parameters:
  • file – handle to open file

  • data – buffer to read from

  • size – number of bytes to write

Return values:

int – number of bytes written or error code

virtual file_offset_t lseek(FileHandle file, file_offset_t offset, SeekOrigin origin) override

change file read/write position

Parameters:
  • file – handle to open file

  • offset – position relative to origin

  • origin – where to seek from (start/end or current position)

Return values:

file_offset_t – current position or error code

virtual int eof(FileHandle file) override

determine if current file position is at end of file

Parameters:

file – handle to open file

Return values:

int – 0 - not EOF, > 0 - at EOF, < 0 - error

virtual file_offset_t tell(FileHandle file) override

get current file position

Parameters:

file – handle to open file

Return values:

file_offset_t – current position relative to start of file, or error code

inline virtual int ftruncate(FileHandle file, file_size_t new_size) override

Truncate (reduce) the size of an open file.

Note

In POSIX ftruncate() can also make the file bigger, however SPIFFS can only reduce the file size and will return an error if newSize > fileSize

Parameters:
  • file – Open file handle, must have Write access

  • newSize

Return values:

intError code

inline virtual int flush(FileHandle file) override

flush any buffered data to physical media

Parameters:

file – handle to open file

Return values:

int – error code

virtual int rename(const char *oldpath, const char *newpath) override

rename a file

Parameters:
  • oldpath

  • newpath

Return values:

int – error code

virtual int remove(const char *path) override

remove (delete) a file by path

Parameters:

path

Return values:

int – error code

inline virtual int fremove(FileHandle file) override

remove (delete) a file by handle

Parameters:

file – handle to open file

Return values:

int – error code

inline virtual int format() override

format the filing system

Note

this does a default format, returning file system to a fresh state The filing system implementation may define more specialised methods which can be called directly.

Return values:

int – error code

inline virtual int check() override

Perform a file system consistency check.

Note

if possible, issues should be resolved. Returns 0 if file system checked out OK. Otherwise there were issues: < 0 for unrecoverable errors,

0 for recoverable errors.

Return values:

int – error code

namespace Host

Functions

FileSystem &getFileSystem()
inline int syserr()

Get IFS error code for the current system errno.

int mapFlags(OpenFlags flags)

Get corresponding host flags for given IFS flags.

String getErrorString(int err)
class FileSystem : public IFS::IFileSystem
#include <FileSystem.h>

IFS implementation of Host filing system.

Public Functions

virtual int mount() override

Mount file system, performing any required initialisation.

Return values:

error – code

virtual int getinfo(Info &info) override

get filing system information

Parameters:

info – structure to read information into

Return values:

int – error code

virtual String getErrorString(int err) override

get the text for a returned error code

Return values:

String

virtual int opendir(const char *path, DirHandle &dir) override

open a directory for reading

Parameters:
  • path – path to directory. nullptr is interpreted as root directory

  • dir – returns a pointer to the directory object

Return values:

int – error code

virtual int rewinddir(DirHandle dir) override

Reset directory read position to start.

Parameters:

dir

Return values:

int – error code

virtual int readdir(DirHandle dir, Stat &stat) override

read a directory entry

Note

File system allocates entries structure as it usually needs to track other information. It releases memory when closedir() is called.

Parameters:
  • dir

  • stat

Return values:

int – error code

virtual int closedir(DirHandle dir) override

close a directory object

Parameters:

dir – directory to close

Return values:

int – error code

virtual int mkdir(const char *path) override

Create a directory.

Only the final directory in the path is guaranteed to be created. Usually, this call will fail if intermediate directories are not present. Use IFS::FileSystem::makedirs() for this purpose.

Parameters:

path – Path to directory

Return values:

int – error code

virtual int stat(const char *path, Stat *stat) override

get file information

Returned stat will indicate whether the path is a mountpoint or directory. For a mount point, stats for the root directory of the mounted filesystem must be obtained by opening a handle then using fstat:

int handle = fs.open("path-to-mountpoint");
Stat stat;
fs.fstat(handle, &stat);
fs.close(handle);
Parameters:
  • path – name or path of file/directory/mountpoint

  • s – structure to return information in, may be null to do a simple file existence check

Return values:

int – error code

virtual int fstat(FileHandle file, Stat *stat) override

get file information

Parameters:
  • file – handle to open file

  • stat – structure to return information in, may be null

Return values:

int – error code

virtual int fsetxattr(FileHandle file, AttributeTag tag, const void *data, size_t size) override

Set an extended attribute on an open file.

Note

Attributes may not be written to disk until flush() or close() are called

Parameters:
  • file – handle to open file

  • tag – The attribute to write

  • data – Content of the attribute. Pass nullptr to remove the attribute (if possible).

  • size – Size of the attribute in bytes

Return values:

int – error code

virtual int fgetxattr(FileHandle file, AttributeTag tag, void *buffer, size_t size) override

Get an extended attribute from an open file.

Parameters:
  • file – handle to open file

  • tag – The attribute to read

  • buffer – Buffer to receive attribute content

  • size – Size of the buffer

Return values:

int – error code, on success returns size of attribute (which may be larger than size)

virtual int fenumxattr(FileHandle file, AttributeEnumCallback callback, void *buffer, size_t bufsize) override

Enumerate attributes.

Parameters:
  • file – handle to open file

  • callback – Callback function to invoke for each attribute found

  • buffer – Buffer to use for reading attribute data. Use nullptr if only tags are required

  • bufsize – Size of buffer

Return values:

int – error code, on success returns number of attributes read

virtual int setxattr(const char *path, AttributeTag tag, const void *data, size_t size) override

Set an extended attribute for a file given its path.

Parameters:
  • path – Full path to file (or directory)

  • tag – The attribute to write

  • data – Content of the attribute. Pass nullptr to remove the attribute (if possible).

  • size – Size of the attribute in bytes

Return values:

int – error code

virtual int getxattr(const char *path, AttributeTag tag, void *buffer, size_t size) override

Get an attribute from a file given its path.

Parameters:
  • file – Full path to file (or directory)

  • tag – The attribute to read

  • buffer – Buffer to receive attribute content

  • size – Size of the buffer

Return values:

int – error code, on success returns size of attribute (which may be larger than size)

virtual FileHandle open(const char *path, OpenFlags flags) override

open a file (or directory) by path

This function may also be used to obtain a directory handle to perform various operations such as enumerating attributes. Calls to read or write on such handles will typically fail.

Parameters:
  • path – full path to file

  • flags – Desired access and other options

Return values:

FileHandle – file handle or error code

virtual int close(FileHandle file) override

close an open file

Parameters:

file – handle to open file

Return values:

int – error code

virtual int read(FileHandle file, void *data, size_t size) override

read content from a file and advance cursor

Parameters:
  • file – handle to open file

  • data – buffer to write into

  • size – size of file buffer, maximum number of bytes to read

Return values:

int – number of bytes read or error code

virtual int write(FileHandle file, const void *data, size_t size) override

write content to a file at current position and advance cursor

Parameters:
  • file – handle to open file

  • data – buffer to read from

  • size – number of bytes to write

Return values:

int – number of bytes written or error code

virtual file_offset_t lseek(FileHandle file, file_offset_t offset, SeekOrigin origin) override

change file read/write position

Parameters:
  • file – handle to open file

  • offset – position relative to origin

  • origin – where to seek from (start/end or current position)

Return values:

file_offset_t – current position or error code

virtual int eof(FileHandle file) override

determine if current file position is at end of file

Parameters:

file – handle to open file

Return values:

int – 0 - not EOF, > 0 - at EOF, < 0 - error

virtual file_offset_t tell(FileHandle file) override

get current file position

Parameters:

file – handle to open file

Return values:

file_offset_t – current position relative to start of file, or error code

virtual int ftruncate(FileHandle file, file_size_t new_size) override

Truncate (reduce) the size of an open file.

Note

In POSIX ftruncate() can also make the file bigger, however SPIFFS can only reduce the file size and will return an error if newSize > fileSize

Parameters:
  • file – Open file handle, must have Write access

  • newSize

Return values:

intError code

virtual int flush(FileHandle file) override

flush any buffered data to physical media

Parameters:

file – handle to open file

Return values:

int – error code

virtual int rename(const char *oldpath, const char *newpath) override

rename a file

Parameters:
  • oldpath

  • newpath

Return values:

int – error code

virtual int remove(const char *path) override

remove (delete) a file by path

Parameters:

path

Return values:

int – error code

inline virtual int fremove(FileHandle file) override

remove (delete) a file by handle

Parameters:

file – handle to open file

Return values:

int – error code

inline virtual int format() override

format the filing system

Note

this does a default format, returning file system to a fresh state The filing system implementation may define more specialised methods which can be called directly.

Return values:

int – error code

inline virtual int check() override

Perform a file system consistency check.

Note

if possible, issues should be resolved. Returns 0 if file system checked out OK. Otherwise there were issues: < 0 for unrecoverable errors,

0 for recoverable errors.

Return values:

int – error code

namespace HYFS
class FileSystem : public IFS::IFileSystem
#include <FileSystem.h>

Public Functions

virtual int mount() override

Mount file system, performing any required initialisation.

Return values:

error – code

virtual int getinfo(Info &info) override

get filing system information

Parameters:

info – structure to read information into

Return values:

int – error code

virtual String getErrorString(int err) override

get the text for a returned error code

Return values:

String

virtual int setVolume(uint8_t index, IFileSystem *fileSystem) override

Set volume for mountpoint.

Parameters:
  • index – Volume index

  • fileSystem – The filesystem to root at this mountpoint

Return values:

int – error code

virtual int opendir(const char *path, DirHandle &dir) override

open a directory for reading

Parameters:
  • path – path to directory. nullptr is interpreted as root directory

  • dir – returns a pointer to the directory object

Return values:

int – error code

virtual int readdir(DirHandle dir, Stat &stat) override

read a directory entry

Note

File system allocates entries structure as it usually needs to track other information. It releases memory when closedir() is called.

Parameters:
  • dir

  • stat

Return values:

int – error code

virtual int rewinddir(DirHandle dir) override

Reset directory read position to start.

Parameters:

dir

Return values:

int – error code

virtual int closedir(DirHandle dir) override

close a directory object

Parameters:

dir – directory to close

Return values:

int – error code

virtual int mkdir(const char *path) override

Create a directory.

Only the final directory in the path is guaranteed to be created. Usually, this call will fail if intermediate directories are not present. Use IFS::FileSystem::makedirs() for this purpose.

Parameters:

path – Path to directory

Return values:

int – error code

virtual int stat(const char *path, Stat *stat) override

get file information

Returned stat will indicate whether the path is a mountpoint or directory. For a mount point, stats for the root directory of the mounted filesystem must be obtained by opening a handle then using fstat:

int handle = fs.open("path-to-mountpoint");
Stat stat;
fs.fstat(handle, &stat);
fs.close(handle);
Parameters:
  • path – name or path of file/directory/mountpoint

  • s – structure to return information in, may be null to do a simple file existence check

Return values:

int – error code

virtual int fstat(FileHandle file, Stat *stat) override

get file information

Parameters:
  • file – handle to open file

  • stat – structure to return information in, may be null

Return values:

int – error code

virtual int fcontrol(FileHandle file, ControlCode code, void *buffer, size_t bufSize) override

Low-level and non-standard file control operations.

To simplify usage the same buffer is used for both input and output. Only the size of the buffer is provided. If a specific FCNTL code requires more information then it will be contained within the provided data.

Parameters:
  • file

  • code – FCNTL_XXX code

  • buffer – Input/Output buffer

  • bufSize – Size of buffer

Return values:

int – error code or, on success, data size

virtual int fsetxattr(FileHandle file, AttributeTag tag, const void *data, size_t size) override

Set an extended attribute on an open file.

Note

Attributes may not be written to disk until flush() or close() are called

Parameters:
  • file – handle to open file

  • tag – The attribute to write

  • data – Content of the attribute. Pass nullptr to remove the attribute (if possible).

  • size – Size of the attribute in bytes

Return values:

int – error code

virtual int fgetxattr(FileHandle file, AttributeTag tag, void *buffer, size_t size) override

Get an extended attribute from an open file.

Parameters:
  • file – handle to open file

  • tag – The attribute to read

  • buffer – Buffer to receive attribute content

  • size – Size of the buffer

Return values:

int – error code, on success returns size of attribute (which may be larger than size)

virtual int fenumxattr(FileHandle file, AttributeEnumCallback callback, void *buffer, size_t bufsize) override

Enumerate attributes.

Parameters:
  • file – handle to open file

  • callback – Callback function to invoke for each attribute found

  • buffer – Buffer to use for reading attribute data. Use nullptr if only tags are required

  • bufsize – Size of buffer

Return values:

int – error code, on success returns number of attributes read

virtual int setxattr(const char *path, AttributeTag tag, const void *data, size_t size) override

Set an extended attribute for a file given its path.

Parameters:
  • path – Full path to file (or directory)

  • tag – The attribute to write

  • data – Content of the attribute. Pass nullptr to remove the attribute (if possible).

  • size – Size of the attribute in bytes

Return values:

int – error code

virtual int getxattr(const char *path, AttributeTag tag, void *buffer, size_t size) override

Get an attribute from a file given its path.

Parameters:
  • file – Full path to file (or directory)

  • tag – The attribute to read

  • buffer – Buffer to receive attribute content

  • size – Size of the buffer

Return values:

int – error code, on success returns size of attribute (which may be larger than size)

virtual FileHandle open(const char *path, OpenFlags flags) override

open a file (or directory) by path

This function may also be used to obtain a directory handle to perform various operations such as enumerating attributes. Calls to read or write on such handles will typically fail.

Parameters:
  • path – full path to file

  • flags – Desired access and other options

Return values:

FileHandle – file handle or error code

virtual int close(FileHandle file) override

close an open file

Parameters:

file – handle to open file

Return values:

int – error code

virtual int read(FileHandle file, void *data, size_t size) override

read content from a file and advance cursor

Parameters:
  • file – handle to open file

  • data – buffer to write into

  • size – size of file buffer, maximum number of bytes to read

Return values:

int – number of bytes read or error code

virtual int write(FileHandle file, const void *data, size_t size) override

write content to a file at current position and advance cursor

Parameters:
  • file – handle to open file

  • data – buffer to read from

  • size – number of bytes to write

Return values:

int – number of bytes written or error code

virtual file_offset_t lseek(FileHandle file, file_offset_t offset, SeekOrigin origin) override

change file read/write position

Parameters:
  • file – handle to open file

  • offset – position relative to origin

  • origin – where to seek from (start/end or current position)

Return values:

file_offset_t – current position or error code

virtual int eof(FileHandle file) override

determine if current file position is at end of file

Parameters:

file – handle to open file

Return values:

int – 0 - not EOF, > 0 - at EOF, < 0 - error

virtual file_offset_t tell(FileHandle file) override

get current file position

Parameters:

file – handle to open file

Return values:

file_offset_t – current position relative to start of file, or error code

virtual int ftruncate(FileHandle file, file_size_t new_size) override

Truncate (reduce) the size of an open file.

Note

In POSIX ftruncate() can also make the file bigger, however SPIFFS can only reduce the file size and will return an error if newSize > fileSize

Parameters:
  • file – Open file handle, must have Write access

  • newSize

Return values:

intError code

virtual int flush(FileHandle file) override

flush any buffered data to physical media

Parameters:

file – handle to open file

Return values:

int – error code

virtual int fgetextents(FileHandle file, Storage::Partition *part, Extent *list, uint16_t extcount) override

Get extents for a file.

Parameters:
  • file – Handle to open file

  • part – Partition where the file lives (OUT, OPTIONAL)

  • list – Buffer for extents (OPTIONAL)

  • extcount – Maximum number of extents to return in list

Return values:

int – Total number of extents for file (may be larger than ‘extcount’), or error code

virtual int rename(const char *oldpath, const char *newpath) override

rename a file

Parameters:
  • oldpath

  • newpath

Return values:

int – error code

virtual int remove(const char *path) override

remove (delete) a file by path

Parameters:

path

Return values:

int – error code

virtual int fremove(FileHandle file) override

remove (delete) a file by handle

Parameters:

file – handle to open file

Return values:

int – error code

virtual int format() override

format the filing system

Note

this does a default format, returning file system to a fresh state The filing system implementation may define more specialised methods which can be called directly.

Return values:

int – error code

virtual int check() override

Perform a file system consistency check.

Note

if possible, issues should be resolved. Returns 0 if file system checked out OK. Otherwise there were issues: < 0 for unrecoverable errors,

0 for recoverable errors.

Return values:

int – error code

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Networking Support

Contains core networking protocol support classes.

DNS: Domain Name System

https://en.m.wikipedia.org/wiki/Domain_Name_System

Server API
enum class DnsReplyCode

Values:

enumerator NoError
enumerator FormError
enumerator ServerFailure
enumerator NonExistentDomain
enumerator NotImplemented
enumerator Refused
enumerator YXDomain
enumerator YXRRSet
enumerator NXRRSet
DNS_QR_QUERY
DNS_QR_RESPONSE
DNS_OPCODE_QUERY
struct DnsHeader
#include <DnsServer.h>
class DnsServer : public UdpConnection
#include <DnsServer.h>

DNS server class.

Public Functions

inline void setErrorReplyCode(DnsReplyCode replyCode)

Set error reply code.

inline void setTTL(uint32_t ttl)

Set message Time-To-Live in seconds.

bool start(uint16_t port, const String &domainName, const IpAddress &resolvedIP)

Start the DNS server.

Parameters:
  • port

  • domainName

  • resolvedIP

Return values:

bool – true if successful, false if there are no sockets available.

void stop()

Stop the DNS server.

FTP: File Transfer Protocol

https://en.m.wikipedia.org/wiki/File_Transfer_Protocol

Server API
class FtpServer : public CustomFtpServer
#include <FtpServer.h>

Provides FTP server.

Public Functions

virtual IFS::UserRole validateUser(const char *login, const char *pass) override

Validate user.

Parameters:
  • login – User name

  • pass – User password

Return values:

IFS::UserRole – Returns assigned user role, None if user not validated

HTTP: HyperText Transfer Protocol

https://en.m.wikipedia.org/wiki/Hypertext_Transfer_Protocol

Build Variables
HTTP_SERVER_EXPOSE_NAME

Default: 1 (enabled)

Adds “HttpServer/Sming” to the SERVER field in response headers. If disabled, the SERVER field is omitted from all responses.

HTTP_SERVER_EXPOSE_VERSION

Default: 0 (disabled)

Adds the current Sming build version to the SERVER field in response headers. For example, “Sming/4.0.0-rc2”.

Requires HTTP_SERVER_EXPOSE_NAME to be enabled.

HTTP_SERVER_EXPOSE_DATE

Default: 0 (disabled)

Sets the DATE field in response headers.

API Documentation
Client API
using RequestQueue = ObjectQueue<HttpRequest, HTTP_REQUEST_POOL_SIZE>

Provides http client connection.

class HttpClient
#include <HttpClient.h>

Subclassed by Ota::Network::HttpUpgrader

Public Functions

inline virtual ~HttpClient()

HttpClient destructor.

Note

DON’T call cleanup. If you want to free all resources from HttpClients the correct sequence will be to

  1. Delete all instances of HttpClient

  2. Call the static method HttpClient::cleanup();

inline bool downloadString(const Url &url, RequestCompletedDelegate requestComplete, size_t maxLength = NETWORK_SEND_BUFFER_SIZE)

Queue request to download content as string (in memory)

Parameters:
  • url – URL from which the content will be fetched

  • requestComplete – Completion callback

  • maxLength – maximum bytes to store in memory. If the response is bigger than maxLength then the rest bytes will be discarded. Use this parameter wisely as setting the value too high may consume all available RAM resulting in device restart and Denial-Of-Service

bool downloadFile(const Url &url, const String &saveFileName, RequestCompletedDelegate requestComplete = nullptr)

Queue request to download a file.

Parameters:
  • url – Source of file data

  • saveFileName – Path to save file to. Optional: specify nullptr to use name from url

  • requestComplete – Completion callback

inline HttpRequest *createRequest(const Url &url)

Helper function to create a new request on a URL.

Parameters:

url

Return values:

HttpRequest*

Public Static Functions

static inline void cleanup()

Use this method to clean all object pools.

Server API
class HttpResource
#include <HttpResource.h>

Instances of this class are registered with an HttpServer for a specific URL.

Subclassed by HttpMultipartResource, WebsocketResource

Public Functions

inline virtual void shutdown(HttpServerConnection &connection)

Takes care to cleanup the connection.

Public Members

HttpResourceDelegate onUrlComplete = nullptr

URL is ready. Path and status code are available.

HttpServerConnectionBodyDelegate onBody = nullptr

resource wants to process the raw body data

HttpResourceDelegate onHeadersComplete = nullptr

headers are ready

HttpResourceDelegate onRequestComplete = nullptr

request is complete OR upgraded

HttpServerConnectionUpgradeDelegate onUpgrade = nullptr

request is upgraded and raw data is passed to it

class PluginRef : public LinkedObjectTemplate<PluginRef>
#include <HttpResource.h>
class HttpResourceTree : public ObjectMap<String, HttpResource>
#include <HttpResourceTree.h>

Class to map URL paths to classes which handle them.

Public Functions

inline HttpResource *setDefault(HttpResource *resource)

Set the default resource handler.

Parameters:

resource – The default resource handler

inline HttpResource *setDefault(const HttpResourceDelegate &onRequestComplete)

Set the default resource handler, identified by “*” wildcard.

Parameters:

onRequestComplete – The default resource handler

inline HttpResource *setDefault(const HttpPathDelegate &callback)

Set the default resource handler, identified by “*” wildcard.

inline HttpResource *getDefault()

Get the current default resource handler, if any.

Return values:

HttpResource*

HttpResource *set(const String &path, const HttpResourceDelegate &onRequestComplete)

Set a callback to handle the given path.

Note

Path should start with slash. Trailing slashes will be removed.

Note

Any existing handler for this path is replaced

Parameters:
  • path – URL path

  • onRequestComplete – Delegate to handle this path

Return values:

HttpResource* – The created resource object

template<class ...Tail>
inline HttpResource *set(const String &path, const HttpResourceDelegate &onRequestComplete, HttpResourcePlugin *plugin, Tail... plugins)

Set a callback to handle the given path, with one or more plugins.

Note

Path should start with slash. Trailing slashes will be removed.

Note

Any existing handler for this path is replaced

Parameters:
  • path – URL path

  • onRequestComplete – Delegate to handle this path

  • plugin – Plugins to register for the resource

Return values:

HttpResource* – The created resource object

HttpResource *set(String path, const HttpPathDelegate &callback)

Add a new path resource with a callback.

Note

Path should start with slash. Trailing slashes will be removed

Note

Any existing handler for this path is replaced

Parameters:
  • path – URL path

  • callback – The callback that will handle this path

template<class ...Tail>
inline HttpResource *set(const String &path, const HttpPathDelegate &callback, HttpResourcePlugin *plugin, Tail... plugins)

Add a new path resource with callback and one or more plugins.

Note

Path should start with slash. Trailing slashes will be removed

Note

Any existing handler for this path is replaced

Parameters:
  • path – URL path

  • callback – The callback that will handle this path

  • plugin – - optional resource plugin

Return values:

HttpResource* – The created resource object

inline void set(const K &key, V *value)

Set a key value.

Parameters:
  • key

  • value

struct HttpServerSettings
#include <HttpServer.h>

Public Members

uint16_t maxActiveConnections = 10

maximum number of concurrent requests..

uint16_t keepAliveSeconds = 0

default seconds to keep the connection alive before closing it

int minHeapSize = -1

min heap size that is required to accept connection, -1 means use server default

bool useDefaultBodyParsers = 1

if the default body parsers, as form-url-encoded, should be used

bool closeOnContentError = true

close the connection if a body parser or resource fails to parse the body content.

class HttpServer : public TcpServer
#include <HttpServer.h>

Public Functions

void configure(const HttpServerSettings &settings)

Allows changing the server configuration.

inline void setBodyParser(const String &contentType, HttpBodyParserDelegate parser)

Allows content-type specific parsing of the body based on content-type.

Parameters:
  • contentType – Can be full content-type like ‘application/json’, or ‘application/*’ or ‘*’. If there is exact match for the content-type wildcard content-types will not be used. There can be only one catch-all ‘*’ body parser and that will be the last registered

  • parser

inline void setBodyParser(MimeType mimeType, HttpBodyParserDelegate parser)

Allows content-type specific parsing of the body based on content-type.

Parameters:
  • mimeType

  • parser

Public Members

HttpResourceTree paths

Maps paths to resources which deal with incoming requests.

Support API
namespace ContentType

Obtain MIME type value from file name or path, with extension

MimeType fromFullFileName(const char *fileName, MimeType unknown)
Parameters:
  • fileName – As NUL-terminated string

  • unknown – Value to return if type cannot be determined

Return values:

MimeType

inline MimeType fromFullFileName(const String &fileName, MimeType unknown)
Parameters:

fileName – As wiring String

Obtain content type string from file name or path, with extension

String fromFullFileName(const char *fileName)
Parameters:

fileName – as NUL-terminated string

Return values:

String

inline String fromFullFileName(const String &fileName)
Parameters:

fileName – as wiring String

Functions

MimeType fromFileExtension(const char *extension, MimeType unknown)

Obtain MIME type value from file extension.

Parameters:
  • extension – excluding ‘.’ separator (e.g. “htm”, “json”)

  • unknown – Value to return if type cannot be determined

Return values:

MimeType

String fromFileExtension(const char *extension)

Obtain content type string from file extension.

Parameters:

extension – excluding ‘.’ separator (e.g. “htm”, “json”)

Return values:

String

inline String fromFileExtension(const String &extension)

Obtain content type string from file extension.

Parameters:

extension

Return values:

String

MimeType fromString(const char *str)

Get enumerated value for a MIME type string.

Parameters:

str

Return values:

MimeType – If empty, null or unrecognised returns MIME_UNKNOWN

inline MimeType fromString(const String &str)

Get enumerated value for a MIME type string.

Parameters:

str

Return values:

MimeType – If empty, null or unrecognised returns MIME_UNKNOWN

Defines

ENABLE_HTTP_REQUEST_AUTH
HTTP_MAX_HEADER_SIZE
HTTP_REQUEST_POOL_SIZE
XX(num, name, string)
XX(num, name, string)
XX(num, name, string)
XX(num, name, string)
XX(n, s)
XX(n, s)
HTTP_PARSER_ERRNO(p)

Typedefs

using HttpFiles = ObjectMap<String, ReadWriteStream>

Enums

enum class HttpMethod

Strongly-typed enum which shadows http_method from http_parser library.

{

Values:

enumerator XX
enum class HttpStatus

HTTP status code.

Values:

enumerator XX
enum class HttpError

HTTP error codes.

Values:

enumerator XX
enum HttpConnectionState

Identifies current state for an HTTP connection.

Values:

enumerator eHCS_Ready
enumerator eHCS_StartSending
enumerator eHCS_SendingHeaders
enumerator eHCS_StartBody
enumerator eHCS_SendingBody
enumerator eHCS_Sent
enumerator eHCS_WaitResponse

Functions

String toString(HttpError err)

Return a descriptive string for the given error.

String httpGetErrorDescription(HttpError err)

Return a descriptive string for the given error.

String toString(HttpStatus code)

Return a descriptive string for an HTTP status code.

inline String httpGetStatusText(unsigned code)

Return a descriptive string for an HTTP status code.

inline String toString(HttpMethod method)

Return text for an HTTP method.

class HttpHeaders : public HttpHeaderFields, private HashMap<HttpHeaderFieldName, String>

Encapsulates a set of HTTP header information.

Todo:

add name and/or value escaping

Note

fields are stored as a map of field names vs. values. Standard fields may be accessed using enumeration tags. Behaviour is as for HashMap, with the addition of methods to support enumerated field names.

Subclassed by SSDP::BaseMessage< HttpHeaders >

Public Functions

const String &operator[](const String &name) const

Fetch a reference to the header field value by name.

Note

if the field doesn’t exist a null String reference is returned

Parameters:

name

Return values:

constString& Reference to value

inline String &operator[](const String &name)

Fetch a reference to the header field value by name.

Note

if the field doesn’t exist it is created with the default null value

Parameters:

name

Return values:

String& – Reference to value

inline String operator[](unsigned index) const

Return the HTTP header line for the value at the given index.

Note

if the index is invalid,

Parameters:

index

Return values:

String

inline bool contains(const String &name) const

Determine if given header field is present.

bool append(const HttpHeaderFieldName &name, const String &value)

Append value to multi-value field.

Parameters:
  • name

  • value

Return values:

bool – false if value exists and field does not permit multiple values

class HeaderConst : public BaseElement<true>
class Iterator : public HashMap<K, V>::Iterator<true>
class HttpRequest

Encapsulates an incoming or outgoing request.

Set request body content

inline HttpRequest *setBody(const String &body)

Set body from String object.

HttpRequest *setBody(String &&body) noexcept

Set body from String object using move semantics: body will be invalid on return.

HttpRequest *setBody(IDataSourceStream *stream)

Set body using given stream object, and retain ownership.

HttpRequest *setBody(const uint8_t *rawData, size_t length)

Set body content by copying binary data.

Parameters:
  • rawData – Data to copy

  • length – Number of bytes to copy

Public Types

using SslInitDelegate = Delegate<void(Ssl::Session &session, HttpRequest &request)>

Callback delegate type used to initialise an SSL session for a given request.

Public Functions

inline HttpRequest(const HttpRequest &value)

Copy constructor.

Note

Internal streams are not copied so these must be dealt with afterwards

inline HttpRequest *clone() const

Clone this request into a new object using the copy constructor.

Return values:

HttpRequest* – The new request object

inline HttpRequest *setFile(const String &formElementName, ReadWriteStream *stream)

Sets a file to be sent.

Parameters:
  • formElementName – The name of the element in the form

  • stream – Pointer to the stream (doesn’t have to be a FileStream)

Return values:

HttpRequest*

inline const String &getHeader(const String &name)

Get header field value.

Parameters:

name – Name of field

Return values:

constString& Value, will be invalid (i.e. if() == false) if field not present

inline const String &getPostParameter(const String &name)

Get POST parameter value.

Parameters:

name – Name of parameter

Return values:

constString& Value, will be invalid (i.e. if() == false) if field not present

inline String getQueryParameter(const String &name, const String &defaultValue = nullptr) const

Get parameter from query fields.

Parameters:
  • name – Name of parameter

  • defaultValue – Optional default value to use if requested parameter not present

inline String getBody()

Moves content from the body stream into a String.

Note

Move semantics are used to ensure that no/minimal additional memory is required. If your application has set a non-memory stream type then the method will fail and return an invalid String. The stream content will be left unchanged.

Return values:

String

inline IDataSourceStream *getBodyStream()

Return the current body stream.

Note

may return null

Return values:

IDataSourceStream*

HttpRequest *setResponseStream(ReadWriteStream *stream)

Instead of storing the response body we can set a stream that will take care to process it.

Note

The response to this request will be stored in the user-provided stream.

Parameters:

stream

Return values:

HttpRequest*

inline ReadWriteStream *getResponseStream()

Get the response stream (if any)

void reset()

Clear buffers and reset to default state in preparation for another request.

inline HttpRequest *onSslInit(SslInitDelegate delegate)

To customise SSL session options, provide a callback.

Parameters:

delegate – Invoked before creating SSL connection

String toString() const

Tries to present a readable version of the current request values.

Return values:

String

Public Members

Url uri

Request URL.

HttpMethod method = HTTP_GET

Request method.

HttpHeaders headers

Request headers.

HttpParams postParams

POST parameters.

HttpFiles files

Attached files.

int retries = 0

how many times the request should be send again…

void *args = nullptr

Used to store data that should be valid during a single request.

Public Static Functions

static inline String toString(const HttpRequest &req)

Tries to present a readable version of the request.

Parameters:

req

Return values:

String

class HttpResponse

Represents either an incoming or outgoing response to a HTTP request.

Public Functions

bool sendFile(const String &fileName, bool allowGzipFileCheck = true)

Send file by name.

Parameters:
  • fileName

  • allowGzipFileCheck – If true, check file extension to see if content compressed

Return values:

bool

bool sendNamedStream(IDataSourceStream *newDataStream)

Parse and send stream, using the name to determine the content type.

Parameters:

newDataStream – If not set already, the contentType will be obtained from the name of this stream

Return values:

bool

inline bool sendDataStream(IDataSourceStream *newDataStream, enum MimeType type)

Send data from the given stream object.

Parameters:
  • newDataStream

  • type

Return values:

false – on error

bool sendDataStream(IDataSourceStream *newDataStream, const String &reqContentType = nullptr)

Send data from the given stream object.

Note

all data is submitted via stream so called by internal routines

Parameters:
  • newDataStream

  • reqContentType

Return values:

on – error returns false and stream will have been destroyed so any external references to it must be invalidated.

inline String getBody()

Moves content from the body stream into a String.

Note

Move semantics are used to ensure that no/minimal additional memory is required. If your application has set a non-memory stream type then the method will fail and return an invalid String. The stream content will be left unchanged.

Return values:

String

void reset()

reset response so it can be re-used

void setBuffer(ReadWriteStream *buffer)

Called by connection to specify where incoming response data is written.

Parameters:

buffer

void freeStreams()

release allocated stream memory

inline bool isSuccess()

Determine if the response status indicates success.

String toString() const

Tries to present a readable version of the current response values.

Return values:

String

Public Members

HttpStatus code = HTTP_STATUS_OK

The HTTP status response code.

HttpHeaders headers

Response headers.

ReadWriteStream *buffer = nullptr

Internal stream for storing strings and receiving responses.

IDataSourceStream *stream = nullptr

The body stream.

MQTT: MQ Telemetry Transport

https://en.m.wikipedia.org/wiki/MQTT

Client API
enum MqttClientState

Values:

enumerator eMCS_Ready
enumerator eMCS_SendingData
using MqttDelegate = Delegate<int(MqttClient &client, mqtt_message_t *message)>
using MqttRequestQueue = ObjectQueue<mqtt_message_t, MQTT_REQUEST_POOL_SIZE>
MQTT_REQUEST_POOL_SIZE
MQTT_CLIENT_CONNECTED
MQTT_FLAG_RETAINED
class MqttClient : protected TcpClient
#include <MqttClient.h>

Public Functions

inline void setKeepAlive(uint16_t seconds)

Sets keep-alive time. That information is sent during connection to the server.

Parameters:

seconds

inline void setPingRepeatTime(uint16_t seconds)

Sets the interval in which to ping the remote server if there was no activity

Parameters:

seconds

bool setWill(const String &topic, const String &message, uint8_t flags = 0)

Sets last will and testament

Parameters:
  • topic

  • message

  • flags – QoS, retain, etc flags

Return values:

bool

bool connect(const Url &url, const String &uniqueClientName)

Connect to a MQTT server.

Parameters:
  • url – URL in the form “mqtt://user:password@server:port” or “mqtts://user:password@server:port”

  • uniqueClientName

Return values:

bool

bool publish(const String &topic, const String &message, uint8_t flags = 0)

Publish a message.

Parameters:
  • topic

  • message – Message content as String

  • flags – Optional flags

Return values:

bool

bool publish(const String &topic, IDataSourceStream *stream, uint8_t flags = 0)

Publish a message.

Parameters:
  • topic

  • message – Message content as read-only stream

  • flags – Optional flags

Return values:

bool

bool subscribe(const String &topic)

Subscribe to a topic.

Parameters:

topic

Return values:

bool

bool unsubscribe(const String &topic)

Unsubscribe from a topic.

Parameters:

topic

Return values:

bool

inline void setEventHandler(mqtt_type_t type, MqttDelegate handler)

Register a callback function to be invoked on incoming event notification.

Parameters:
  • type – Type of event to be notified of

  • handler – The callback. Pass nullptr to cancel callback.

inline void setPayloadParser(MqttPayloadParser payloadParser = nullptr)

Sets or clears a payload parser (for PUBLISH messages from the server to us)

Note

We no longer have size limitation for incoming or outgoing messages but in order to prevent running out of memory we have a “sane” payload parser that will read up to 1K of payload

inline void setConnectedHandler(MqttDelegate handler)

Sets a handler to be called after successful MQTT connection.

Parameters:

handler

inline void setPublishedHandler(MqttDelegate handler)

Sets a handler to be called after receiving confirmation from the server for a published message from the client.

Parameters:

handler

inline void setMessageHandler(MqttDelegate handler)

Sets a handler to be called after receiving a PUBLISH message from the server.

Parameters:

handler

inline void setDisconnectHandler(TcpClientCompleteDelegate handler)

Sets a handler to be called on disconnect from the server.

Parameters:

handler

inline void setCompleteDelegate(TcpClientCompleteDelegate completeCb = nullptr)

Set or clear the callback for connection close.

Parameters:

completeCb – callback delegate or nullptr

Public Static Functions

static inline uint8_t getFlags(mqtt_qos_t QoS, mqtt_retain_t retain = MQTT_RETAIN_FALSE, mqtt_dup_t dup = MQTT_DUP_FALSE)

Compute the flags value.

Parameters:
  • QoS – - Quality of Service

  • retain – - Retain flag

  • dup – - Duplicate delivery

Return values:

uint8_t – calculated flags value

NTP: Network Time Protocol

https://en.m.wikipedia.org/wiki/Network_Time_Protocol

Client API
using NtpTimeResultDelegate = Delegate<void(NtpClient &client, time_t ntpTime)>
NTP_PORT
NTP_PACKET_SIZE
NTP_VERSION
NTP_MODE_CLIENT
NTP_MODE_SERVER
NTP_DEFAULT_SERVER
NTP_DEFAULT_AUTOQUERY_SECONDS
NTP_MIN_AUTOQUERY_SECONDS

Minimum autoquery interval.

NTP_CONNECTION_TIMEOUT_MS

Time to retry query when network connection unavailable.

NTP_RESPONSE_TIMEOUT_MS

Time to wait before retrying NTP query.

class NtpClient : protected UdpConnection
#include <NtpClient.h>

NTP client class.

Public Functions

inline NtpClient()

Instantiates NTP client object.

inline NtpClient(NtpTimeResultDelegate onTimeReceivedCb)

Instantiates NTP client object.

Parameters:

onTimeReceivedCb – Callback delegate to be called when NTP time result is received

NtpClient(const String &reqServer, unsigned reqIntervalSeconds, NtpTimeResultDelegate onTimeReceivedCb = nullptr)

Instantiates NTP client object.

Parameters:
  • reqServer – IP address or hostname of NTP server; nullptr to use default server

  • reqIntervalSeconds – Quantity of seconds between NTP requests

  • onTimeReceivedCb – Callback delegate to be called when NTP time result is received (Default: None)

void requestTime()

Request time from NTP server.

Note

Instigates request. Result is handled by NTP result handler function if defined

inline void setNtpServer(const String &server)

Set the NTP server.

Parameters:

server – IP address or hostname of NTP server

void setAutoQuery(bool autoQuery)

Enable / disable periodic query.

Parameters:

autoQuery – True to enable periodic query of NTP server

void setAutoQueryInterval(unsigned seconds)

Set query period.

Parameters:

seconds – Period in seconds between periodic queries

inline void setAutoUpdateSystemClock(bool autoUpdateClock)

Enable / disable update of system clock.

Parameters:

autoUpdateClock – True to update system clock with NTP result.

SMTP: Simple Mail Transfer Protocol

https://en.m.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol

Client API
enum SmtpState

Values:

enumerator eSMTP_Banner
enumerator eSMTP_Hello
enumerator eSMTP_StartTLS
enumerator eSMTP_SendAuth
enumerator eSMTP_SendingAuthLogin
enumerator eSMTP_RequestingAuthChallenge
enumerator eSMTP_SendAuthResponse
enumerator eSMTP_SendingAuth
enumerator eSMTP_Ready
enumerator eSMTP_SendMail
enumerator eSMTP_SendingMail
enumerator eSMTP_SendRcpt
enumerator eSMTP_SendingRcpt
enumerator eSMTP_SendData
enumerator eSMTP_SendingData
enumerator eSMTP_SendHeader
enumerator eSMTP_SendingHeaders
enumerator eSMTP_StartBody
enumerator eSMTP_SendingBody
enumerator eSMTP_Sent
enumerator eSMTP_Quitting
enumerator eSMTP_Disconnect
using SmtpClientCallback = Delegate<int(SmtpClient &client, int code, char *status)>
SMTP_QUEUE_SIZE
SMTP_ERROR_LENGTH
SMTP_CODE_SERVICE_READY

SMTP response codes

SMTP_CODE_BYE
SMTP_CODE_AUTH_OK
SMTP_CODE_REQUEST_OK
SMTP_CODE_AUTH_CHALLENGE
SMTP_CODE_START_DATA
SMTP_OPT_PIPELINE
SMTP_OPT_STARTTLS
SMTP_OPT_AUTH_PLAIN
SMTP_OPT_AUTH_LOGIN
SMTP_OPT_AUTH_CRAM_MD5
class MailMessage
#include <MailMessage.h>

Public Functions

inline MailMessage &setHeader(const String &name, const String &value)

Set a header value.

Parameters:
  • name

  • value

Return values:

MailMessage&

HttpHeaders &getHeaders()

Get a reference to the current set of headers.

Return values:

HttpHeaders&

MailMessage &setBody(const String &body, MimeType mime = MIME_TEXT)

Sets the body of the email.

Parameters:
  • body

  • mime

Return values:

MailMessage&

MailMessage &setBody(String &&body, MimeType mime = MIME_TEXT) noexcept

Sets the body of the email using move semantics.

Parameters:
  • body – Will be moved into message then invalidated

  • mime

Return values:

MailMessage&

MailMessage &setBody(IDataSourceStream *stream, MimeType mime = MIME_TEXT)

Sets the body of the email.

Parameters:
  • stream

  • mime

Return values:

MailMessage&

MailMessage &addAttachment(FileStream *stream)

Adds attachment to the email.

Parameters:

stream

Return values:

MailMessage&

MailMessage &addAttachment(IDataSourceStream *stream, MimeType mime, const String &filename = "")

Adds attachment to the email.

Parameters:
  • stream

  • mime

  • filename

Return values:

MailMessage&

MailMessage &addAttachment(IDataSourceStream *stream, const String &mime, const String &filename = "")

Adds attachment to the email.

Parameters:
  • stream

  • mime

  • filename

Return values:

MailMessage&

class SmtpClient : protected TcpClient
#include <SmtpClient.h>

Unnamed Group

bool send(const String &from, const String &to, const String &subject, const String &body)

Queues a single message before it is sent later to the SMTP server.

Parameters:
  • from

  • to

  • subject

  • body – The body in plain text format

Return values:

bool – true when the message was queued successfully, false otherwise

Public Functions

bool connect(const Url &url)

Connects to remote URL.

Parameters:

url – Provides the protocol, remote server, port and user credentials allowed protocols:

  • smtp - clear text SMTP

  • smtps - SMTP over SSL connection

bool send(MailMessage *message)

Powerful method to queues a single message before it is sent later to the SMTP server.

Parameters:

message

Return values:

bool – true when the message was queued successfully, false otherwise

MailMessage *getCurrentMessage()

Gets the current message.

Return values:

MailMessage* – The message, or NULL if none is scheduled

void quit()

Sends a quit command to the server and closes the TCP connection.

inline SmtpState getState()

Returns the current state of the SmtpClient.

inline void onMessageSent(SmtpClientCallback callback)

Callback that will be called every time a message is sent successfully.

Parameters:

callback

inline void onServerError(SmtpClientCallback callback)

Callback that will be called every an error occurs.

Parameters:

callback

TCP: Transmission Control Protocol

https://en.m.wikipedia.org/wiki/Transmission_Control_Protocol

Connection API
enum TcpConnectionEvent

Values:

enumerator eTCE_Connected

Occurs after connection establishment.

enumerator eTCE_Received

Occurs on data receive.

enumerator eTCE_Sent
enumerator eTCE_Poll
using TcpConnectionDestroyedDelegate = Delegate<void(TcpConnection&)>
NETWORK_DEBUG
NETWORK_SEND_BUFFER_SIZE
class TcpConnection : public IpConnection
#include <TcpConnection.h>

Subclassed by FtpDataStream, FtpServerConnection, TcpClient, TcpServer

Public Functions

inline int writeString(const char *data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)

Writes string data directly to the TCP buffer.

Parameters:
  • data – null terminated string

  • apiflags – TCP_WRITE_FLAG_COPY, TCP_WRITE_FLAG_MORE

Return values:

int – negative on error, 0 when retry is needed or positive on success

inline int writeString(const String &data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)

Writes string data directly to the TCP buffer.

Parameters:
  • data

  • apiflags – TCP_WRITE_FLAG_COPY, TCP_WRITE_FLAG_MORE

Return values:

int – negative on error, 0 when retry is needed or positive on success

virtual int write(const char *data, int len, uint8_t apiflags = TCP_WRITE_FLAG_COPY)

Base write operation.

Parameters:
  • data

  • len

  • apiflags – TCP_WRITE_FLAG_COPY, TCP_WRITE_FLAG_MORE

Return values:

int – negative on error, 0 when retry is needed or positive on success

int write(IDataSourceStream *stream)

Writes stream data directly to the TCP buffer.

Parameters:
  • stream

  • apiflags – TCP_WRITE_FLAG_COPY, TCP_WRITE_FLAG_MORE

Return values:

int – negative on error, 0 when retry is needed or positive on success

inline void setDestroyedDelegate(TcpConnectionDestroyedDelegate destroyedDelegate)

Sets a callback to be called when the object instance is destroyed.

Parameters:

destroyedDelegate

inline void setSslInitHandler(Ssl::Session::InitDelegate handler)

Set the SSL session initialisation callback.

Parameters:

handler

inline Ssl::Session *getSsl()

Get a pointer to the current SSL session object.

Note that this is typically used so we can query properties of an established session. If you need to change session parameters this must be done via setSslInitHandler.

Client API
enum TcpClientState

Values:

enumerator eTCS_Ready
enumerator eTCS_Connecting
enumerator eTCS_Connected
enumerator eTCS_Successful
enumerator eTCS_Failed
enum TcpClientCloseAfterSentState

Values:

enumerator eTCCASS_None
enumerator eTCCASS_AfterSent
enumerator eTCCASS_AfterSent_Ignore_Received
using TcpClientEventDelegate = Delegate<void(TcpClient &client, TcpConnectionEvent sourceEvent)>
using TcpClientCompleteDelegate = Delegate<void(TcpClient &client, bool successful)>
using TcpClientDataDelegate = Delegate<bool(TcpClient &client, char *data, int size)>
TCP_CLIENT_TIMEOUT
class TcpClient : public TcpConnection
#include <TcpClient.h>

Subclassed by HttpConnection, MqttClient, SmtpClient

Public Functions

inline void setReceiveDelegate(TcpClientDataDelegate receiveCb = nullptr)

Set or clear the callback for received data.

Parameters:

receiveCb – callback delegate or nullptr

inline void setCompleteDelegate(TcpClientCompleteDelegate completeCb = nullptr)

Set or clear the callback for connection close.

Parameters:

completeCb – callback delegate or nullptr

bool send(IDataSourceStream *source, bool forceCloseAfterSent = false)

Sends data stream.

Note

This function takes ownership of the stream pointer!

Parameters:
  • source – stream pointer

  • forceCloseAfterSent

Return values:

bool – true when the stream can be used. When false the stream will be deleted.

inline void setCloseAfterSent(bool ignoreIncomingData = false)

Schedules the connection to get closed after the data is sent

Parameters:

ignoreIncomingData – when that flag is set the connection will start ignoring incoming data.

inline void commit()

Tries to send the pending data immediately.

Note

Call this method to decrease latency. Use it carefully.

Server API
using TcpClientConnectDelegate = Delegate<void(TcpClient *client)>
TCP_SERVER_TIMEOUT
class TcpServer : public TcpConnection
#include <TcpServer.h>

Subclassed by CustomFtpServer, HttpServer

Telnet

https://en.m.wikipedia.org/wiki/Telnet

This is a very simple protocol which can be implemented using a TcpServer class. See TelnetServer for an example application.

UDP: User Datagram Protocol

https://en.m.wikipedia.org/wiki/User_Datagram_Protocol

Connection API
using UdpConnectionDataDelegate = Delegate<void(UdpConnection &connection, char *data, int size, IpAddress remoteIP, uint16_t remotePort)>
class UdpConnection : public IpConnection
#include <UdpConnection.h>

Subclassed by DnsServer, NtpClient, SSDP::Server, mDNS::Server

Public Functions

bool setMulticast(IpAddress ip)

Sets the UDP multicast IP.

Note

This method works only when LWIP is compiled with LWIP_MULTICAST_TX_OPTIONS

Parameters:

ip

Return values:

true – when LWIP supports this operation, false otherwise

bool setMulticastTtl(size_t ttl)

Sets the UDP multicast Time-To-Live(TTL).

Note

This method works only when LWIP is compiled with LWIP_MULTICAST_TX_OPTIONS

Parameters:

ttl – - time to live in hops. For example if a milticast UDP packet needs to pass through two routes to reach the receiver then the TTL should be set to 2

Return values:

true – when LWIP supports this operation, false otherwise

URL: Uniform Resource Locator

https://en.m.wikipedia.org/wiki/URL

inline String toString(const Url &url)
URI_SCHEME_MAP(XX)
XX(name, str, port)

Common URI scheme strings.

class Url
#include <Url.h>

Class to manage URL instance.

Note

The various URL components are stored in un-escaped format for ease of editing. Unless otherwise indicated, methods act on un-escaped text. Methods used to obtain escaped versions are clearly marked. Any attached fragment (marked bv ‘#’) in the URL is discarded

Public Functions

inline Url(const String &urlString)

Construct a URL object from a regular escaped string.

Parameters:

urlString – Escaped URL

inline Url(const char *urlString)

Construct a URL object from a regular null-terminated escaped string.

Parameters:

urlString – Escaped URL

Url &operator=(String urlString)

Copy assignment operator.

Note

urlString is modified by so no point in passing const reference

Parameters:

urlString – Escaped URL

inline Url &operator=(const char *urlString)

Copy assignment operator, for C-style strings.

Parameters:

urlString – Escaped URL

String toString() const

Get escaped URL.

Return values:

String

inline int getPort() const

Obtain the actual port number to be used.

Note

If not specified, the default scheme port is returned

Return values:

int

String getHostWithPort() const

Get hostname+port part of URL string.

Note

Neither of these is subject to escaping

Return values:

String

inline String getRelativePath() const

Get path without leading separator.

Return values:

String

String getPathWithQuery() const

Get path with any query parameters attached.

Note

Both path and query values are escaped

Return values:

String

String getFileName() const

Obtain the filename part of the URL path.

Return values:

String

void debugPrintTo(Print &p) const

Printable output for debugging.

Parameters:

p

Public Members

String Scheme

without “:” and “//”

String Host

hostname or IP address

int Port = 0

Undefined by default.

String Path

with leading “/”

String Fragment

Without ‘#’.

Public Static Functions

static int getDefaultPort(const String &scheme)

Obtain the default port for a given scheme.

Return values:

int – 0 if scheme is not recognised or has no standard port defined

WebSocket Protocol

https://en.m.wikipedia.org/wiki/WebSocket

Connection API
enum WsConnectionState

Current state of Websocket connection.

Values:

enumerator eWSCS_Ready
enumerator eWSCS_Open
enumerator eWSCS_Closed
using WebsocketList = Vector<WebsocketConnection*>
using WebsocketDelegate = Delegate<void(WebsocketConnection&)>
using WebsocketMessageDelegate = Delegate<void(WebsocketConnection&, const String&)>
using WebsocketBinaryDelegate = Delegate<void(WebsocketConnection&, uint8_t *data, size_t size)>
WEBSOCKET_VERSION
struct WsFrameInfo
#include <WebsocketConnection.h>
class WebsocketConnection
#include <WebsocketConnection.h>

Subclassed by WebsocketClient

Public Functions

WebsocketConnection(HttpConnection *connection = nullptr, bool isClientConnection = true)

Constructs a websocket connection on top of http client or server connection.

Parameters:
  • connection – the transport connection

  • isClientConnection – true when the passed connection is an http client connection

bool bind(HttpRequest &request, HttpResponse &response)

Binds websocket connection to an http server connection.

Parameters:
  • request

  • response

Return values:

bool – true on success, false otherwise

bool send(const char *message, size_t length, ws_frame_type_t type = WS_FRAME_TEXT)

Sends a websocket message from a buffer.

Parameters:
  • message

  • length – Quantity of data in message

  • type

inline bool send(const String &message, ws_frame_type_t type = WS_FRAME_TEXT)

Sends websocket message from a String.

Note

A String may contain arbitrary data, not just text, so can use this for any frame type

Parameters:
bool send(IDataSourceStream *source, ws_frame_type_t type = WS_FRAME_TEXT, bool useMask = false, bool isFin = true)

Sends websocket message from a stream.

Parameters:
  • source – The stream to send - we get ownership of the stream

  • type

  • useMask – MUST be true for client connections

  • isFin – true if this is the final frame

Return values:

bool – true on success

inline bool sendString(const String &message)

Sends a string websocket message.

Parameters:

message

inline bool sendBinary(const uint8_t *data, size_t length)

Sends a binary websocket message.

Parameters:
  • data

  • length

void close()

Closes a websocket connection (without closing the underlying http connection)

void reset()

Resets a websocket connection.

inline void setUserData(void *userData)

Attaches a user data to a websocket connection.

Parameters:

userData

inline void *getUserData()

Retrieves user data attached.

Return values:

void* – The user data previously set by setUserData()

inline bool operator==(const WebsocketConnection &rhs) const

Test if another connection refers to the same object.

Parameters:

rhs – The other WebsocketConnection to compare with

Return values:

bool

inline void setConnectionHandler(WebsocketDelegate handler)

Sets the callback handler to be called after successful websocket connection.

Parameters:

handler

inline void setMessageHandler(WebsocketMessageDelegate handler)

Sets the callback handler to be called after a websocket message is received.

Parameters:

handler

inline void setBinaryHandler(WebsocketBinaryDelegate handler)

Sets the callback handler to be called after a binary websocket message is received.

Parameters:

handler

inline void setPongHandler(WebsocketDelegate handler)

Sets the callback handler to be called when pong reply received.

Parameters:

handler

inline void setDisconnectionHandler(WebsocketDelegate handler)

Sets the callback handler to be called before closing a websocket connection.

Parameters:

handler

void activate()

Should be called after a websocket connection is established to activate the websocket parser and allow sending of websocket data.

bool onConnected()

Call this method when the websocket connection was (re)activated.

Return values:

bool – true on success

inline HttpConnection *getConnection()

Gets the underlying HTTP connection.

Return values:

HttpConnection*

void setConnection(HttpConnection *connection, bool isClientConnection = true)

Sets the underlying (transport ) HTTP connection.

Parameters:
  • connection – the transport connection

  • isClientConnection – true when the passed connection is an http client connection

inline WsConnectionState getState()

Gets the state of the websocket connection.

Return values:

WsConnectionState

Public Static Functions

static void broadcast(const char *message, size_t length, ws_frame_type_t type = WS_FRAME_TEXT)

Broadcasts a message to all active websocket connections.

Parameters:
  • message

  • length

  • type

static inline void broadcast(const String &message, ws_frame_type_t type = WS_FRAME_TEXT)

Broadcasts a message to all active websocket connections.

Parameters:
  • message

  • type

static inline const WebsocketList &getActiveWebsockets()

Obtain the list of active websockets.

Note

Return value is const as only restricted operations should be carried out on the list.

Return values:

const – WebsocketList&

Client API
class WebsocketClient : protected WebsocketConnection
#include <WebsocketClient.h>

Websocket Client.

Public Functions

bool connect(const Url &url)

Connects websocket client to server.

Parameters:

urlUrl address of websocket server

inline void sendPing(const String &payload = nullptr)

Send websocket ping to server.

Parameters:

payload – Maximum 255 bytes

Return values:

bool – true if the data can be send, false otherwise

inline void sendPong(const String &payload = nullptr)

Send websocket ping to server.

Parameters:

payload – Maximum 255 bytes

Return values:

bool – true if the data can be send, false otherwise

inline void setSslInitHandler(Ssl::Session::InitDelegate handler)

Set the SSL session initialisation callback.

Parameters:

handler

inline void setBinaryHandler(WebsocketBinaryDelegate handler)

Sets the callback handler to be called after a binary websocket message is received.

Parameters:

handler

inline void setConnectionHandler(WebsocketDelegate handler)

Sets the callback handler to be called after successful websocket connection.

Parameters:

handler

inline void setDisconnectionHandler(WebsocketDelegate handler)

Sets the callback handler to be called before closing a websocket connection.

Parameters:

handler

inline void setMessageHandler(WebsocketMessageDelegate handler)

Sets the callback handler to be called after a websocket message is received.

Parameters:

handler

bool send(const char *message, size_t length, ws_frame_type_t type = WS_FRAME_TEXT)

Sends a websocket message from a buffer.

Parameters:
  • message

  • length – Quantity of data in message

  • type

inline bool send(const String &message, ws_frame_type_t type = WS_FRAME_TEXT)

Sends websocket message from a String.

Note

A String may contain arbitrary data, not just text, so can use this for any frame type

Parameters:
bool send(IDataSourceStream *source, ws_frame_type_t type = WS_FRAME_TEXT, bool useMask = false, bool isFin = true)

Sends websocket message from a stream.

Parameters:
  • source – The stream to send - we get ownership of the stream

  • type

  • useMask – MUST be true for client connections

  • isFin – true if this is the final frame

Return values:

bool – true on success

inline bool sendBinary(const uint8_t *data, size_t length)

Sends a binary websocket message.

Parameters:
  • data

  • length

inline bool sendString(const String &message)

Sends a string websocket message.

Parameters:

message

void close()

Closes a websocket connection (without closing the underlying http connection)

inline WsConnectionState getState()

Gets the state of the websocket connection.

Return values:

WsConnectionState

Other networking libraries:

Configuration variables
ENABLE_WIFI_DEBUG

default: 0 (disabled)

RP2040 only

Set to 1 to enable additional debugging output for processing WiFi events.

References
Used by
Environment Variables
SoC support
Storage Management

This Component provides support for using storage devices in a structured way by partitioning them into areas for specific uses. Partitions may can contain information such as:

  • Application (firmware) images

  • Filesystem(s)

  • Configuration/calibration/parameter data

  • Custom flash storage areas

A single partition table is located on the main flash device, Storage::spiFlash, and defines all partitions with a unique name and associated Storage::Partition::Type / Storage::Partition::SubType.

Hardware configuration

Each project has an associated Hardware configuration, specified by the HWCONFIG setting: this is a JSON file with a .hw extension.

For user convenience, the configuration file may contain comments however these are stripped before processing.

The build system locates the file by searching, in order:

  • {PROJECT_DIR} the root project directory

  • {SMING_HOME}/Arch/{SMING_ARCH}

  • {SMING_HOME}

Each architecture provides a standard configuration which defines such things as the partition table location and standard system partitions. Other configurations inherit from this by providing a base_config value.

You can list the available configs like this:

make hwconfig-list

This also shows the file path should you wish to view or edit it.

To select and view the resulting configuration, do this:

make hwconfig HWCONFIG=spiffs

or, to show the partition map:

make map HWCONFIG=spiffs

Note

You can set HWCONFIG in your project’s component.mk file, however as with other configuration variables it will be overridden by the cached value set on the command line.

For example, if you want to change from standard to standard-4m for your project, first add this line to your component.mk file:

HWCONFIG := standard-4m

Then either run make HWCONFIG=standard-4m or make config-clean.

Hardware configuration options

Commonly used settings can be stored in an option library for easier use. The library files are named options.json and located in the place as .hw files.

For example, we can do this:

make HWCONFIG=standard HWCONFIG_OPTS=4m,spiffs

This loads the ‘standard’ profile then merges the fragments found in the option library with the given names. This is how the standard-4m profile is constructed.

If using this approach, remember to updated your project’s component.mk with the desired settings, and verify the layout is correct using make map.

OTA updates

When planning OTA updates please check that the displayed partition map corresponds to your project. For example, the partition table requires a free sector so must not overlap other partitions.

Your OTA update process must include a step to write the partition table to the correct location. See Partition table migration.

It is not necessary to update the bootloader. See rBoot for further information.

Custom configurations

To customise the hardware configuration for a project, for example ‘my_project’:

  1. Create a new configuration file in your project root, such as my_project.hw:

    {
       "name": "My project config",
       "base_config": "spiffs",
       "options": ["vdd"]
    }
    

    You can use any available configuration as the base_config. Option fragments can be pulled in as shown. See Hardware configuration options.

  2. If required, modify any inherited settings:

    {
       "name": "My config",
       "base_config": "standard",
       "devices": {
          "spiFlash": {
             "speed": 80,
             "mode": "qio",
             "size": "2M"
          }
       },
       "partitions": {
          "rom0": {
             "address": "0x10000",
             "size": "0x80000"
          }
       }
    }
    

    This will adjust flash parameters (previously via SPI_SPEED, SPI_MODE and SPI_SIZE), and the location/size of the primary application partition.

  3. Add any additional partitions:

    {
       "name": "My config",
       "base_config": "standard-4m",
       "partitions": {
          "rom0": {
             "address": "0x10000",
             "size": "0x80000"
          },
          "spiffs1": {
                "address": "0x00280000",
                "size": "256K",
                "type": "data",
                "subtype": "spiffs",
                "filename": "$(FW_BASE)/spiffs1_rom.bin",
                "build": {
                   "target": "spiffsgen",
                   "files": "files/spiffs1"
                }
          }
       }
    }
    

    This adds a second SPIFFS partition, and instructs the build system to generate an image file for it using the files in the project’s files/spiffs1 directory.

  4. Select the new configuration and re-build the project:

    make HWCONFIG=my_project
    

    You should also add this to your project’s component.mk file:

    HWCONFIG := my_project
    
  5. Program your device:

    make flash
    

    This will flash everything: bootloader, partition table and all defined partitions (those with a filename entry).

Note

The build system isn’t smart enough to track dependencies for partition build targets.

To rebuild these manually type:

make buildpart

These will be removed when make clean is run, but you can also clean them separately thus:

make part-clean
Partition maps

This is a concise view of your flash partitions. Display it like this:

make map

For the Basic Storage sample application, we get this:

Basic_Storage: Invoking 'map' for Esp8266 (debug) architecture
Partition map:
Device            Start       End         Size        Type      SubType   Name              Filename
----------------  ----------  ----------  ----------  --------  --------  ----------------  ------------
spiFlash          0x00000000  0x00001fff          8K                      Boot Sector
spiFlash          0x00002000  0x00002fff          4K                      Partition Table
spiFlash          0x00003000  0x00003fff          4K  data      phy       phy_init          $(FLASH_INIT_DATA)
spiFlash          0x00004000  0x00007fff         16K  data      sysparam  sys_param
spiFlash          0x00008000  0x000fffff        992K  app       factory   rom0              $(RBOOT_ROM_0_BIN)
spiFlash          0x00100000  0x001effff        960K                      (unused)
spiFlash          0x001f0000  0x001f3fff         16K  user      0         user0             user0.bin
spiFlash          0x001f4000  0x001f7fff         16K  user      1         user1
spiFlash          0x001f8000  0x001fffff         32K                      (unused)
spiFlash          0x00200000  0x0027ffff        512K  data      spiffs    spiffs0           $(SPIFF_BIN_OUT)
spiFlash          0x00280000  0x002bffff        256K  data      spiffs    spiffs1           $(FW_BASE)/spiffs1_rom.bin
spiFlash          0x002c0000  0x002fffff        256K  data      spiffs    spiffs2           $(FW_BASE)/spiffs2_rom.bin
spiFlash          0x00300000  0x003fffff          1M                      (unused)

For comparison, here’s the output for Esp32:

Basic_Storage: Invoking 'map' for Esp32 (debug) architecture
Partition map:
Device            Start       End         Size        Type      SubType   Name              Filename
----------------  ----------  ----------  ----------  --------  --------  ----------------  ------------
spiFlash          0x00000000  0x00007fff         32K                      Boot Sector
spiFlash          0x00008000  0x00008fff          4K                      Partition Table
spiFlash          0x00009000  0x0000efff         24K  data      nvs       nvs
spiFlash          0x0000f000  0x0000ffff          4K  data      phy       phy_init
spiFlash          0x00010000  0x001fffff       1984K  app       factory   factory           $(TARGET_BIN)
spiFlash          0x001f0000  0x001f3fff         16K  user      0         user0             user0.bin
spiFlash          0x001f4000  0x001f7fff         16K  user      1         user1
spiFlash          0x001f8000  0x001fffff         32K                      (unused)
spiFlash          0x00200000  0x0027ffff        512K  data      spiffs    spiffs0           $(SPIFF_BIN_OUT)
spiFlash          0x00280000  0x002bffff        256K  data      spiffs    spiffs1           $(FW_BASE)/spiffs1_rom.bin
spiFlash          0x002c0000  0x002fffff        256K  data      spiffs    spiffs2           $(FW_BASE)/spiffs2_rom.bin
spiFlash          0x00300000  0x003fffff          1M                      (unused)

To compare this with the partition map programmed into a device, do this:

make readmap map
JSON validation

When the binary partition table is built or updated, the configuration is first validated against a schema Sming/Components/Storage/schema.json.

This complements the checks performed by the hwconfig tool.

You can run the validation manually like this:

make hwconfig-validate

See JSON Schema for details about JSON schemas.

Configuration
HWCONFIG

default: standard

Set this to the hardware configuration to use for your project.

Default configurations:

standard

Base profile with 1MB flash size which should work on all device variants. Located in the Sming/Arch/{SMING_ARCH} directory.

standard-4m

Overrides standard to set 4Mbyte flash size

spiffs

Adds a single SPIFFS partition. See SPIFFS IFS Library.

Other configurations may be available, depending on architecture. You can see these by running make hwconfig-list.

For example, to select spiffs add the following line to your project:

HWCONFIG := spiffs

You will also need to run make HWCONFIG=spiffs to change the cached value (or make config-clean to reset everything).

HWCONFIG_OPTS

Set this to adjust the hardware profile using option fragments. See Hardware configuration options.

ENABLE_STORAGE_SIZE64

Build with ENABLE_STORAGE_SIZE64=1 to enable support for storage devices of more than 4GB capacity.

Device and partition addresses and sizes use the storage_size_t type, which by default is uint32_t. Setting this value changes it to uint64_t.

When enabling this setting, care must be taken in code especially with printf style format strings such as in debug statements. The safest way to handle both cases is like this:

debug_i("Partition size: %llu", uint64_t(part.size()));
Binary partition table

Sming uses the same binary partition table structure as ESP-IDF, located immediately after the boot sector. However, it is organised slightly differently to allow partitions to be registered for multiple storage devices.

Entries are fixed 32-byte structures, Storage::esp_partition_info_t, organised as follows:

  • The first entry is always a storage type defining the main spiFlash device.

  • This is followed by regular partition entries sorted in ascending address order. There may be gaps between the partitions.

  • The partition table md5sum entry is inserted as normal

  • If any external devices are defined: - A SMING_EXTENSION entry, which the esp32 bootloader interprets as the end of the partition table. - The next entry is a storage type for the external device. - This is followed by regular partition entries as before. - A second md5sum entry is inserted for the entire partition table thus far

  • The end of the partition table is identified by an empty sector (i.e. all bytes 0xFF).

Partition API

This is a C++ interface. Some examples:

Storage::Partition part = Storage::findPartition("spiffs0"); // Find by name
if(part) {
  Serial << part << endl;
} else {
  Serial << "spiffs0 partition NOT Found" << endl;
}

// Enumerate all partitions
for(auto part: Storage::findPartition()) {
  Serial << part << endl;
}

// Enumerate all SPIFFS partitions
for(auto part: Storage::findPartition(Storage::Partition::SubType::Data::spiffs)) {
  Serial << part << endl;
}

A Storage::Partition object is just a wrapper and can be freely copied around. It defines methods which should be used to read/write/erase the partition contents.

Each partition has an associated Storage::Device. This is usually Storage::spiFlash for the main flash device.

Other devices must be registered via Storage::PartitionTable::registerStorageDevice().

You can query partition entries from a Storage object directly, for example:

#include <Storage/SpiFlash.h>

for(auto part: Storage::spiFlash->partitions()) {
  Serial << part << endl;
}
External Storage

If your design has additional fixed storage devices, such as SPI RAM, flash or EEPROM, you can take advantage of the partition API to manage them as follows:

  • Implement a class to manage the storage, inheriting from Storage::Device.

  • Create a custom hardware configuration for your project and add a devices entry describing your storage device, plus partition entries: the device field identifies which device these entries relate to.

  • Create an instance of your custom device and make a call to Storage::registerDevice() in your init() function (or elsewhere if more appropriate).

See Disk Storage for how devices such as SD flash cards are managed.

API
namespace Storage

Get power of 2 for given value

See also

Use isLog2() to confirm value is power of 2

param value:

Must be an exact power of 2

retval uint8_t:

Result n such that value == 1 << n

template<typename T>
constexpr std::enable_if<(sizeof(T) <= 4), uint8_t>::type getSizeBits(T value)
template<typename T>
constexpr std::enable_if<(sizeof(T) > 4), uint8_t>::type getSizeBits(T value)

Enums

enum class Mode

Values:

enumerator ReadOnly
enumerator Write

Write but do not erase, region should be pre-erased.

enumerator BlockErase

Erase blocks as required before writing.

Functions

inline bool isSize64(uint64_t value)

Determine if a value requires 64-bits to store.

inline bool isSize64(int64_t value)

Determine if a value requires 64-bits to store.

template<typename T>
constexpr bool isLog2(T value)

Determine if a value is an exact power of 2.

void initialize()

Called early in the startup phase.

const Device::List getDevices()

Get read-only reference to device list.

bool registerDevice(Device *device)

Register a storage device.

Return values:

bool – true on success, false if another device already registered with same name

bool unRegisterDevice(Device *device)

Unregister a storage device.

Use extreme care: behaviour is unpredictable if partitions are in use

Device *findDevice(const String &name)

Find a registered device.

Partition findPartition(const String &name)

Find the first partition matching the given name.

inline Iterator findPartition(Partition::Type type = Partition::Type::any, uint8_t subType = Partition::SubType::any)

Find partitions of the given type.

template<typename T>
std::enable_if<std::is_enum<T>::value, Iterator>::type findPartition(T subType)
template<typename T>
Storage::Partition findDefaultPartition(T subType)

Variables

constexpr uint16_t ESP_PARTITION_MAGIC = {0x50AA}

Identifies a valid partition.

constexpr uint16_t ESP_PARTITION_MAGIC_MD5 = {0xEBEB}

Identifies an MD5 hash block.

constexpr size_t ESP_PARTITION_TABLE_MAX_LEN = {0xC00}
ProgMem progMem
SpiFlash *spiFlash
SysMem sysMem
class FileDevice : public Storage::Device
#include <FileDevice.h>

Create custom storage device using backing file.

Public Functions

inline FileDevice(const String &name, IFS::IFileSystem &fileSys, IFS::FileHandle file, storage_size_t size)

Construct a file device with custom size.

Parameters:
  • name – Name of device

  • fileSys – File system where file is located

  • file – Handle to open file

  • size – Size of device in bytes

inline FileDevice(const String &name, IFS::IFileSystem &fileSys, IFS::FileHandle file)

Construct a device using existing file.

Device will match size of existing file

Parameters:
  • name – Name of device

  • fileSys – File system where file is located

  • file – Handle to open file

inline virtual String getName() const override

Obtain unique device name.

inline virtual Type getType() const override

Obtain device type.

inline virtual storage_size_t getSize() const override

Obtain addressable size of this device.

Return values:

storage_size_t – Must be at least as large as the value declared in the hardware configuration

inline virtual size_t getBlockSize() const override

Obtain smallest allocation unit for erase operations.

virtual bool read(storage_size_t address, void *buffer, size_t len) override

Read data from the storage device.

Parameters:
  • address – Where to start reading

  • dst – Buffer to store data

  • size – Size of data to be read, in bytes.

Return values:

bool – true on success, false on error

virtual bool write(storage_size_t address, const void *data, size_t len) override

Write data to the storage device.

Parameters:
  • address – Where to start writing

  • src – Data to write

  • size – Size of data to be written, in bytes.

Return values:

bool – true on success, false on error

virtual bool erase_range(storage_size_t address, storage_size_t len) override

Erase a region of storage in preparation for writing.

Parameters:
  • address – Where to start erasing

  • size – Size of region to erase, in bytes

Return values:

bool – true on success, false on error

class Device : public LinkedObjectTemplate<Device>
#include <Device.h>

Represents a storage device (e.g. flash memory)

Subclassed by Storage::Disk::BlockDevice, Storage::FileDevice, Storage::ProgMem, Storage::SpiFlash, Storage::StreamDevice, Storage::SysMem

Public Types

enum class Type : uint8_t

Storage type.

Values:

enumerator partitionType
enumerator XX

Public Functions

inline const PartitionTable &partitions() const

Provide read-only access to partition table.

inline PartitionTable &editablePartitions()

Provide full access to partition table.

inline bool loadPartitions(uint32_t tableOffset)

Load partition table entries @tableOffset Location of partition table to read.

Return values:

bool – true on success, false on failure

bool loadPartitions(Device &source, uint32_t tableOffset)

Load partition table entries from another table.

Parameters:

sourceDevice to load entries from @tableOffset Location of partition table to read

Return values:

bool – true on success, false on failure

virtual String getName() const = 0

Obtain unique device name.

inline virtual uint32_t getId() const

Obtain device ID.

Return values:

uint32_t – typically flash chip ID

virtual size_t getBlockSize() const = 0

Obtain smallest allocation unit for erase operations.

virtual storage_size_t getSize() const = 0

Obtain addressable size of this device.

Return values:

storage_size_t – Must be at least as large as the value declared in the hardware configuration

virtual Type getType() const = 0

Obtain device type.

virtual bool read(storage_size_t address, void *dst, size_t size) = 0

Read data from the storage device.

Parameters:
  • address – Where to start reading

  • dst – Buffer to store data

  • size – Size of data to be read, in bytes.

Return values:

bool – true on success, false on error

virtual bool write(storage_size_t address, const void *src, size_t size) = 0

Write data to the storage device.

Parameters:
  • address – Where to start writing

  • src – Data to write

  • size – Size of data to be written, in bytes.

Return values:

bool – true on success, false on error

virtual bool erase_range(storage_size_t address, storage_size_t size) = 0

Erase a region of storage in preparation for writing.

Parameters:
  • address – Where to start erasing

  • size – Size of region to erase, in bytes

Return values:

bool – true on success, false on error

inline virtual uint16_t getSectorSize() const

Get sector size, the unit of allocation for block-access devices.

Override this method only if the device does not support standard 512-byte sector access. For example, ‘Advanced-Format’ drives use 4096-byte sectors.

inline virtual storage_size_t getSectorCount() const

Obtain total number of sectors on this device.

inline virtual bool sync()

Flush any pending writes to the physical media.

Devices with intermediate buffering should implement this method.

Return values:

bool – Return false if sync operation failed.

class Iterator
#include <Iterator.h>
class Partition
#include <Partition.h>

Represents a flash partition.

Confirm partition is of the expected type

bool verify(Type type, uint8_t subtype) const

Strong C++ type value.

Parameters:
  • type – Expected partition type

  • subtype – Expected partition sub-type

Return values:

bool – true if type is OK, false if not. Logs debug messages on failure.

inline bool verify(uint8_t type, uint8_t subtype) const

Weak ‘type’ value.

template<typename T>
inline bool verify(T subType) const

Derive type from subtype, expressed as strong C++ enum.

Public Functions

bool read(storage_size_t offset, void *dst, size_t size)

Read data from the partition.

Parameters:
  • offset – Where to start reading, relative to start of partition

  • dst – Buffer to store data

  • size – Size of data to be read, in bytes.

Return values:

bool – true on success, false on error

bool write(storage_size_t offset, const void *src, size_t size)

Write data to the partition.

Note

Flash region must be erased first

Parameters:
  • offset – Where to start writing, relative to start of partition

  • src – Data to write

  • size – Size of data to be written, in bytes.

Return values:

bool – true on success, false on error

bool erase_range(storage_size_t offset, storage_size_t size)

Erase part of the partition.

Note

Both offset and size must be aligned to flash sector size (4Kbytes)

Parameters:
  • offset – Where to start erasing, relative to start of partition

  • size – Size of region to erase, in bytes

Return values:

bool – true on success, false on error

inline Partition::Type type() const

Obtain partition type.

inline uint8_t subType() const

Obtain partition sub-type.

inline FullType fullType() const

Obtain both type and subtype.

inline storage_size_t address() const

Obtain partition starting address.

Return values:

storage_size_tDevice address

inline storage_size_t lastAddress() const

Obtain address of last byte in this this partition.

Return values:

storage_size_tDevice address

inline storage_size_t size() const

Obtain partition size.

Return values:

storage_size_t – Size in bytes

inline String name() const

Get partition name.

inline Flags flags() const

Get partition flags.

inline bool isEncrypted() const

Check state of partition encrypted flag.

inline bool isReadOnly() const

Check state of partition readOnly flag.

bool getDeviceAddress(storage_size_t &address, storage_size_t size) const

Get corresponding storage device address for a given partition offset.

Parameters:
  • address – IN: Zero-based offset within partition, OUT: Device address

  • size – Size of data to be accessed

Return values:

bool – true on success, false on failure Fails if the given offset/size combination is out of range, or the partition is undefined.

String getDeviceName() const

Get name of storage device for this partition.

Return values:

String

inline bool contains(storage_size_t addr) const

Determine if given address contained within this partition.

size_t getBlockSize() const

Obtain smallest allocation unit for erase operations.

uint16_t getSectorSize() const

Get sector size for block-addressable devices.

inline storage_size_t getSectorCount() const

Obtain total number of sectors in this partition.

bool sync()

Flush any pending writes to the physical media.

See also

See Storage::Device::sync

inline const Disk::DiskPart *diskpart() const

If this is a disk partition, return pointer to the additional information.

Public Static Functions

static inline SubType::App apptypeOta(uint8_t i)

Convenience function to get SubType value for the i-th OTA partition.

struct FullType
#include <Partition.h>

Express both partition type and subtype together.

struct Info : public LinkedObjectTemplate<Info>, public Printable
#include <Partition.h>

Partition information.

Subclassed by Storage::Disk::PartInfo

struct SubType
#include <Partition.h>

Public Types

enum class App : uint8_t

Application partition type.

Values:

enumerator partitionType
enumerator XX
enumerator ota_min
enumerator ota_max
enumerator any
enum class Data : uint8_t

Data partition type.

Values:

enumerator partitionType
enumerator XX
enumerator any
struct esp_partition_info_t
#include <partition_info.h>

Internal structure describing the binary layout of a partition table entry.

Public Members

uint16_t magic

Fixed value to identify valid entry, appears as 0xFFFF at end of table.

Partition::Type type

Main type of partition.

uint8_t subtype

Sub-type for partition (interpretation dependent upon type)

uint32_t offset

Start offset.

uint32_t size

Size of partition in bytes.

Storage::Partition::Name name

Unique identifier for entry.

Storage::Partition::Flags flags

Various option flags.

class PartitionStream : public ReadWriteStream
#include <PartitionStream.h>

Stream operating directory on a Storage partition.

To support write operations, the target region must be erased first.

Public Functions

inline PartitionStream(Partition partition, storage_size_t offset, size_t size, bool blockErase)

Access part of a partition using a stream.

If blockErase is false then region must be pre-erased before writing.

Deprecated:

Use mode parameter instead of blockErase

Parameters:
  • partition

  • offset – Limit access to this starting offset

  • size – Limit access to this number of bytes from starting offset

  • blockErase – Set to true to erase blocks before writing

inline PartitionStream(Partition partition, bool blockErase)

Access entire partition using stream.

If blockErase is false then partition must be pre-erased before writing.

Deprecated:

Use mode parameter instead of blockErase

Parameters:
  • partition

  • blockErase – Set to true to erase blocks before writing

inline PartitionStream(Partition partition, storage_size_t offset, size_t size, Mode mode = Mode::ReadOnly)

Access part of a partition using a stream.

Note

When writing in Mode::BlockErase, block erasure is only performed at the start of each block. Therefore if offset is not a block boundary then the corresponding block will not be erased first.

Parameters:
  • partition

  • offset – Limit access to this starting offset

  • size – Limit access to this number of bytes from starting offset

  • mode

inline PartitionStream(Partition partition, Mode mode = Mode::ReadOnly)

Access entire partition using stream.

Parameters:
  • partition

  • mode – If blockErase is false then partition must be pre-erased before writing.

inline virtual int available() override

Return the total length of the stream.

Return values:

int – -1 is returned when the size cannot be determined

virtual uint16_t readMemoryBlock(char *data, int bufSize) override

Read a block of memory.

Todo:

Should IDataSourceStream::readMemoryBlock return same data type as its bufSize param?

Parameters:
  • data – Pointer to the data to be read

  • bufSize – Quantity of chars to read

Return values:

uint16_t – Quantity of chars read

virtual int seekFrom(int offset, SeekOrigin origin) override

Change position in stream.

Note

This method is implemented by streams which support random seeking, such as files and memory streams.

Parameters:
  • offset

  • origin

Return values:

New – position, < 0 on error

virtual size_t write(const uint8_t *buffer, size_t size) override

Write chars to stream.

Note

Although this is defined in the Print class, ReadWriteStream uses this as the core output method so descendants are required to implement it

Parameters:
  • buffer – Pointer to buffer to write to the stream

  • size – Quantity of chars to write

Return values:

size_t – Quantity of chars written to stream

inline virtual bool isFinished() override

Check if all data has been read.

Return values:

bool – True on success.

class PartitionTable
#include <PartitionTable.h>

Subclassed by Storage::ProgMem::ProgMemPartitionTable, Storage::SysMem::SysMemPartitionTable

inline Iterator find(Partition::Type type = Partition::Type::any, uint8_t subType = Partition::SubType::any) const

Find partitions based on one or more parameters.

Parameters:
Return values:

Iterator – Forward-iterator for matching partitions

Public Functions

inline Partition find(const String &name) const

Find partition by name.

Parameters:

Name – Name to search for, case-sensitive

Return values:

Partition – Names are unique so at most only one match

inline Partition find(uint32_t address) const

Find partition containing the given address.

Parameters:

address – Address to search for

Return values:

Partition

inline Partition findOta(uint8_t index) const

Find the n’th OTA partition.

inline Partition add(const Partition::Info *info)

Add new partition using given Info.

Parameters:

info – Must be allocated using new: Device will take ownership

Return values:

Partition – Reference to the partition

class ProgMem : public Storage::Device
#include <ProgMem.h>

Storage device to access PROGMEM using flash API.

Public Functions

inline virtual String getName() const override

Obtain unique device name.

inline virtual size_t getBlockSize() const override

Obtain smallest allocation unit for erase operations.

inline virtual storage_size_t getSize() const override

Obtain addressable size of this device.

Return values:

storage_size_t – Must be at least as large as the value declared in the hardware configuration

inline virtual Type getType() const override

Obtain device type.

virtual bool read(storage_size_t address, void *dst, size_t size) override

Read data from the storage device.

Parameters:
  • address – Where to start reading

  • dst – Buffer to store data

  • size – Size of data to be read, in bytes.

Return values:

bool – true on success, false on error

inline virtual bool write(storage_size_t address, const void *src, size_t size) override

Write data to the storage device.

Parameters:
  • address – Where to start writing

  • src – Data to write

  • size – Size of data to be written, in bytes.

Return values:

bool – true on success, false on error

inline virtual bool erase_range(storage_size_t address, storage_size_t size) override

Erase a region of storage in preparation for writing.

Parameters:
  • address – Where to start erasing

  • size – Size of region to erase, in bytes

Return values:

bool – true on success, false on error

class ProgMemPartitionTable : public Storage::PartitionTable
#include <ProgMem.h>

Public Functions

Partition add(const String &name, const void *flashPtr, size_t size, Partition::FullType type)

Add partition entry for PROGMEM data access.

Parameters:
  • name – Name for partition

  • flashPtr – PROGMEM pointer

  • size – Size of PROGMEM data

  • typePartition type and subtype

Return values:

Partition – Invalid if data is not progmem

inline Partition add(const String &name, const FSTR::ObjectBase &fstr, Partition::FullType type)

Add partition entry for FlashString data access.

class SpiFlash : public Storage::Device
#include <SpiFlash.h>

Main flash storage device.

Public Functions

virtual String getName() const override

Obtain unique device name.

virtual size_t getBlockSize() const override

Obtain smallest allocation unit for erase operations.

virtual storage_size_t getSize() const override

Obtain addressable size of this device.

Return values:

storage_size_t – Must be at least as large as the value declared in the hardware configuration

inline virtual Type getType() const override

Obtain device type.

virtual uint32_t getId() const override

Obtain device ID.

Return values:

uint32_t – typically flash chip ID

virtual bool read(storage_size_t address, void *dst, size_t size) override

Read data from the storage device.

Parameters:
  • address – Where to start reading

  • dst – Buffer to store data

  • size – Size of data to be read, in bytes.

Return values:

bool – true on success, false on error

virtual bool write(storage_size_t address, const void *src, size_t size) override

Write data to the storage device.

Parameters:
  • address – Where to start writing

  • src – Data to write

  • size – Size of data to be written, in bytes.

Return values:

bool – true on success, false on error

virtual bool erase_range(storage_size_t address, storage_size_t size) override

Erase a region of storage in preparation for writing.

Parameters:
  • address – Where to start erasing

  • size – Size of region to erase, in bytes

Return values:

bool – true on success, false on error

class StreamDevice : public Storage::Device
#include <StreamDevice.h>

Read-only partition on a stream object.

Note

Writes not possible as streams always append data, cannot do random writes

Public Functions

inline virtual Type getType() const override

Obtain device type.

inline virtual bool read(storage_size_t address, void *buffer, size_t len) override

Read data from the storage device.

Parameters:
  • address – Where to start reading

  • dst – Buffer to store data

  • size – Size of data to be read, in bytes.

Return values:

bool – true on success, false on error

inline virtual bool write(storage_size_t address, const void *data, size_t len) override

Write data to the storage device.

Parameters:
  • address – Where to start writing

  • src – Data to write

  • size – Size of data to be written, in bytes.

Return values:

bool – true on success, false on error

inline virtual bool erase_range(storage_size_t address, storage_size_t len) override

Erase a region of storage in preparation for writing.

Parameters:
  • address – Where to start erasing

  • size – Size of region to erase, in bytes

Return values:

bool – true on success, false on error

class SysMem : public Storage::Device
#include <SysMem.h>

Storage device to access system memory, e.g. RAM.

Public Functions

inline virtual String getName() const override

Obtain unique device name.

inline virtual size_t getBlockSize() const override

Obtain smallest allocation unit for erase operations.

inline virtual storage_size_t getSize() const override

Obtain addressable size of this device.

Return values:

storage_size_t – Must be at least as large as the value declared in the hardware configuration

inline virtual Type getType() const override

Obtain device type.

inline virtual bool read(storage_size_t address, void *buffer, size_t len) override

Read data from the storage device.

Parameters:
  • address – Where to start reading

  • dst – Buffer to store data

  • size – Size of data to be read, in bytes.

Return values:

bool – true on success, false on error

inline virtual bool write(storage_size_t address, const void *data, size_t len) override

Write data to the storage device.

Parameters:
  • address – Where to start writing

  • src – Data to write

  • size – Size of data to be written, in bytes.

Return values:

bool – true on success, false on error

inline virtual bool erase_range(storage_size_t address, storage_size_t len) override

Erase a region of storage in preparation for writing.

Parameters:
  • address – Where to start erasing

  • size – Size of region to erase, in bytes

Return values:

bool – true on success, false on error

class SysMemPartitionTable : public Storage::PartitionTable
#include <SysMem.h>

Public Functions

inline Partition add(const String &name, const FSTR::ObjectBase &fstr, Partition::FullType type)

Add partition entry for FlashString data access.

inline Partition add(const Partition::Info *info)

Add new partition using given Info.

Parameters:

info – Must be allocated using new: Device will take ownership

Return values:

Partition – Reference to the partition

namespace Debug

Functions

void listPartitions(Print &out)
void listPartitions(Print &out, const Device &device)
void listDevices(Print &out, bool fullPartitionInfo = true)
namespace Disk

Typedefs

using SysTypes = BitSet<uint8_t, SysType>

Enums

enum class Error

Values:

enumerator XX
enum class SysType : uint8_t

Identifies exact disk volume type.

Values:

enumerator unknown

Partition type not recognised.

enumerator fat12
enumerator fat16
enumerator fat32
enumerator exfat
enum SysIndicator

MBR partition system type indicator values.

Values:

enumerator SI_FAT12
enumerator SI_FAT16

FAT16 with fewer than 65536 sectors.

enumerator SI_FAT16B

FAT16B with 65536 or more sectors.

enumerator SI_IFS
enumerator SI_EXFAT
enumerator SI_FAT32X

FAT32 with LBA.

Functions

template<typename T>
T align_up(T value, uint32_t align)
template<typename T>
auto getBlockCount(T byteCount, uint32_t blockSize)
uint32_t crc32_byte(uint32_t crc, uint8_t d)
uint32_t crc32(uint32_t bcc, const void *data, size_t length)
inline uint32_t crc32(const void *data, size_t length)
inline bool operator!(Error err)
Error formatDisk(BlockDevice &device, GPT::PartitionTable &table, const Uuid &diskGuid = {})

Partition a device using the GPT scheme.

Parameters:
  • device

  • table – Partitions to create

Return values:

Error

Error formatDisk(BlockDevice &device, MBR::PartitionTable &table)

Partition a device using the MBR scheme.

Parameters:
  • device

  • table – Partitions to create

Return values:

Error

inline SysType getSysTypeFromIndicator(SysIndicator si)
Error validate(BasePartitionTable &table, storage_size_t firstAvailableBlock, storage_size_t totalAvailableBlocks, uint32_t blockSize)

Validate partition table entries.

  • If size <= 100 then the actual size is calculated as a percentage and updated

  • If offset = 0 then a suitable location is found and the offset updated

On success, partition entries are ordered by position.

Parameters:
  • firstAvailableBlock – First block number which may be allocated to a partition

  • totalAvailableBlocks – Number of blocks available for partition allocation

  • blockSize – Size of a block

Return values:

Error – For each partition:

bool scanPartitions(Device &device)

Variables

constexpr uint32_t PARTITION_ALIGN = {0x100000U}
static constexpr SysTypes fatTypes = SysType::fat12 | SysType::fat16 | SysType::fat32 | SysType::exfat
class BlockDevice : public Storage::Device
#include <BlockDevice.h>

Base class for sector-addressable (block) devices.

Inherited classes must set the sectorCount value, and implement the four raw_xxx methods.

This class supports byte-level access using internal buffering, which if required must be enabled by the application via the allocateBuffers method. Without buffering, read/writes must always be sector-aligned. Rrase must always be sector-aligned.

For power-loss resiliency it is important to call sync() at appropriate times. Filing system implementations should do this after closing a file, for example. Applications should consider this if leaving files open for extended periods, and explicitly call ‘flush’ on the filing system or ‘sync’ on the partition or device if necessary.

Subclassed by Storage::Disk::HostFileDevice, Storage::SD::Card, USB::MSC::LogicalUnit

Public Functions

virtual bool read(storage_size_t address, void *dst, size_t size) override

Read data from the storage device.

Parameters:
  • address – Where to start reading

  • dstBuffer to store data

  • size – Size of data to be read, in bytes.

Return values:

bool – true on success, false on error

virtual bool write(storage_size_t address, const void *src, size_t size) override

Write data to the storage device.

Parameters:
  • address – Where to start writing

  • src – Data to write

  • size – Size of data to be written, in bytes.

Return values:

bool – true on success, false on error

virtual bool erase_range(storage_size_t address, storage_size_t size) override

Erase a region of storage in preparation for writing.

Parameters:
  • address – Where to start erasing

  • size – Size of region to erase, in bytes

Return values:

bool – true on success, false on error

inline virtual size_t getBlockSize() const override

Obtain smallest allocation unit for erase operations.

inline virtual storage_size_t getSize() const override

Obtain addressable size of this device.

Return values:

storage_size_t – Must be at least as large as the value declared in the hardware configuration

inline virtual storage_size_t getSectorCount() const override

Obtain total number of sectors on this device.

virtual bool sync() override

Flush any pending writes to the physical media.

Devices with intermediate buffering should implement this method.

Return values:

bool – Return false if sync operation failed.

bool allocateBuffers(unsigned numBuffers)

Set number of sector buffers to use.

Required to support byte-level read/write operations on block devices. Buffering can improve performance, with diminishing returns above around 4 sectors.

Parameters:

numBuffers – Number of buffers to allocate 1,2,4,8,etc. Pass 0 to deallocate/disable buffering.

Return values:

bool – false on memory allocation error, or if failed to flush existing buffers to disk

struct Stat
#include <BlockDevice.h>

Public Members

Func func[3]

Read, Write, Erase.

std::map<uint32_t, Func> sectors

By sector.

struct Func
#include <BlockDevice.h>
struct Buffer : public std::unique_ptr<uint8_t[]>
#include <Buffer.h>
class BufferList
#include <Buffer.h>
class HostFileDevice : public Storage::Disk::BlockDevice
#include <HostFileDevice.h>

Create custom storage device using backing file.

Public Functions

HostFileDevice(const String &name, const String &filename, storage_size_t size)

Construct a file device with custom size.

Parameters:
  • name – Name of device

  • filename – Path to file

  • size – Size of device in bytes

HostFileDevice(const String &name, const String &filename)

Construct a device using existing file.

Device will match size of existing file

Parameters:
  • name – Name of device

  • filename – Path to file

inline virtual String getName() const override

Obtain unique device name.

inline virtual Type getType() const override

Obtain device type.

struct DiskPart
#include <PartInfo.h>

Adds information specific to MBR/GPT disk partitions.

Subclassed by Storage::Disk::PartInfo

Public Functions

size_t printTo(Print &p) const

Print full contents of this structure.

Public Members

Uuid typeGuid

GPT type GUID.

Uuid uniqueGuid

GPT partition unique GUID.

SysType systype = {}

Identifies volume filing system type.

SysIndicator sysind = {}

Partition sys value.

struct PartInfo : public Storage::Partition::Info, public Storage::Disk::DiskPart
#include <PartInfo.h>

In-memory partition information.

A disk Storage::Partition refers to this instance.

Public Functions

inline virtual const Disk::DiskPart *diskpart() const override

Obtain additional disk information.

Accessed via Partition::diskpart() method

virtual size_t printTo(Print &p) const override

Print important fields only.

class BasePartitionTable : public OwnedLinkedObjectListTemplate<PartInfo>
#include <PartInfo.h>

Common type for MBR/GPT partition table.

Subclassed by Storage::Disk::GPT::PartitionTable, Storage::Disk::MBR::PartitionTable

class Scanner
#include <Scanner.h>

Class to iterate through disk partition tables.

Supports MBR and GPT partitioning schemes.

Public Functions

std::unique_ptr<PartInfo> next()

Obtains the next partition entry (if any)

class SectorBuffer : public std::unique_ptr<uint8_t[]>
#include <SectorBuffer.h>

Buffer for working with disk sectors.

namespace EXFAT
namespace FAT
namespace GPT

Functions

String getTypeName(const Uuid &typeGuid)

Get string for known GPT type GUIDs.

struct SmingTypeGuid : public Uuid
#include <GPT.h>
class PartitionTable : public Storage::Disk::BasePartitionTable
#include <GPT.h>

Public Functions

inline bool add(const String &name, SysType sysType, storage_size_t offset, storage_size_t size, const Uuid &uniqueGuid = {}, const Uuid &typeGuid = {}, Partition::Flags flags = 0)

Add a new standard GPT partition definition.

Parameters:
  • namePartition name

  • sysType – Intended content for this partition

  • offset – Start offset, or 0 to have position calculated

  • size – Size of partition (in bytes), or percentage (0-100) of total partitionable disk space

  • uniqueGuid – Unique partition identifier (optional: will be generated if not provided)

  • typeGuidPartition type GUID (default is BASIC_DATA)

  • flags

Return values:

bool – true on success

inline bool add(const String &name, Partition::FullType type, storage_size_t offset, storage_size_t size, const Uuid &uniqueGuid = {}, Partition::Flags flags = 0)

Add a new GPT partition for a regular Sming filing system.

Note

These partitions use a custom type GUID and won’t be recognised by external software.

Parameters:
  • namePartition name

  • type – Sming partition type/subtype

  • offset – Start offset, or 0 to have position calculated

  • size – Size of partition (in bytes), or percentage (0-100) of total partitionable disk space

  • uniqueGuid – Unique partition identifier (optional: will be generated if not provided)

  • flags

Return values:

bool – true on success

namespace MBR
class PartitionTable : public Storage::Disk::BasePartitionTable
#include <MBR.h>

Public Functions

inline bool add(SysType sysType, SysIndicator sysIndicator, storage_size_t offset, storage_size_t size, Partition::Flags flags = 0)

Add a new MBR partition definition.

Note

MBR does not have partition name field; this will appear as ‘mbr1’, ‘mbr2’, etc.

Parameters:
  • sysType – Intended content for this partition (or ‘unknown’)

  • SysIndicator – Appropriate system code SI_xxx

  • offset – Start offset, or 0 to have position calculated

  • size – Size of partition (in bytes), or percentage (0-100) of total partitionable disk space

  • flags

Return values:

bool – true on success

namespace SD
class Card : public Storage::Disk::BlockDevice
#include <Card.h>

Public Functions

bool begin(uint8_t chipSelect, uint32_t freq = 0)

Initialise the card.

Parameters:
  • chipSelect

  • freq – SPI frequency in Hz, use 0 for maximum supported frequency

inline virtual String getName() const override

Obtain unique device name.

inline virtual uint32_t getId() const

Obtain device ID.

Return values:

uint32_t – typically flash chip ID

inline virtual Type getType() const

Obtain device type.

inline virtual size_t getBlockSize() const override

Obtain smallest allocation unit for erase operations.

struct CID
#include <CID.h>

Public Members

uint8_t mid

Manufacturer ID.

char oid[2]

OEM / Application ID.

char pnm[5]

Product name.

uint8_t prv

Product revision.

uint32_t psn

Product serial number.

uint16_t mdt

Manufacturing date.

uint8_t not_used

Always 1.

uint8_t crc

7-bit checksum

struct CSD
#include <CSD.h>

Subclassed by Storage::SD::CSD1, Storage::SD::CSD2, Storage::SD::CSD3

struct CSD1 : public Storage::SD::CSD
#include <CSD.h>
struct CSD2 : public Storage::SD::CSD
#include <CSD.h>
struct CSD3 : public Storage::SD::CSD
#include <CSD.h>
References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Arch Driver

This is an internal Component to pull together common headers and code used at a low-level by more than once Arch.

This is to:

  • Ease maintenance

  • Avoid code duplication

  • Provide a consistent API for the framework to use

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

AXTLS 8266

SSL support using the AXTLS library

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: axtls-8266

This is an ESP8266 port of axTLS library, currently based on axTLS 2.1.4 (SVN version 277).

This library supports TLS 1.2, and the following cipher suites:

Cipher suite name (RFC)

OpenSSL name

Key exchange

Encryption

Hash

TLS_RSA_WITH_AES_128_CBC_SHA

AES128-SHA

RSA

AES-128

SHA-1

TLS_RSA_WITH_AES_256_CBC_SHA

AES256-SHA

RSA

AES-256

SHA-1

TLS_RSA_WITH_AES_128_CBC_SHA256

AES128-SHA256

RSA

AES-128

SHA-256

TLS_RSA_WITH_AES_256_CBC_SHA256

AES256-SHA256

RSA

AES-256

SHA-256

Using the library

This is not a self-sufficient library. In addition to the standard C library functions, application has to provide the following functions:

ax_port_read
ax_port_write
ax_port_open
ax_port_close
ax_get_file
phy_get_rand  (provided by the IoT SDK)
ets_printf    (in ESP8266 ROM)
ets_putc      (in ESP8266 ROM)

For use with LwIP raw TCP API, see compat/README.md

Building .. image:: https://travis-ci.org/igrr/axtls-8266.svg

target:

https://travis-ci.org/igrr/axtls-8266

alt:

Build status


To build, add xtensa toolchain to your path, and run make. The library will be built in bin/ directory.

Credits and license

axTLS is written and maintained by Cameron Rich.

Other people have contributed to this port; see git logs for a full list.

See LICENSE file for axTLS license.

Bear SSL

SSL support using Bear SSL for ESP8266.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: bearssl
Cryptographic Support
Introduction

Contains basic cryptographic support classes for Sming.

This provides a strongly typed and flexible C++ library for commonly used routines, such as MD5, SHA hashes and HMAC.

Architecture-specific ROM or SDK routines are used where appropriate to reduce code size and improve performance.

The intention is that this library will provide the optimal implementations for any given architecture but maintain a consistent interface and allow it to be easily extended.

Hashes

You must #include the appropriate header for the hash family, for example:

#include <Crypto/Sha1.h>

All hash operations are then performed via the Crypto::Sha1 class.

Here’s a basic example showing how to calculate a SHA1 hash on a C string:

#include <Crypto/Sha1.h>

void sha1Test(const char* buffer)
{
   // Returns a Crypto::Sha1::Hash object
   auto hash = Crypto::Sha1().calculate(buffer, strlen(buffer));
   Serial.print("SHA1: ");
   Serial.println(Crypto::toString(hash));
}

If your data has multiple chunks, use the longer form:

#include <Crypto/Sha2.h>

void sha256Test(const String& s1, const String& s2)
{
   Crypto::Sha256 ctx;
   ctx.update(s1);
   ctx.update(s2);
   Serial.print("SHA256: ");
   Serial.println(Crypto::toString(ctx.getHash()));
}

sha256Test(F("This is some text to be hashed"), F("Hello"));

Some hashes have additional optional parameters, for example:

#include <Crypto/Blake2s.h>

void blake2sTest(const String& key, const String& content)
{
   Crypto::Blake2s256 ctx(key);
   ctx.update(content);
   Serial.print("BLAKE2S-256: ");
   Serial.println(Crypto::toString(ctx.getHash()));
}
HMAC

The HMAC algorithm is commonly used for verifying both the integrity and authenticity of a message. It can be used with any defined hash, commonly MD5 or SHA1.

For example, an MD5 HMAC (as used with CRAM-MD5) may be done like this:

#include <Crypto/Md5.h>

void printHmacMd5(const String& key, const String& data)
{
   auto hash = Crypto::HmacMd5(key).calculate(data);
   Serial.print("HMAC.MD5 = ");
   Serial.println(Crypto::toString(hash));
}
‘C’ API

The library also defines a standard ‘C’ api so it can be used from within existing code, such as AXTLS 8266 and Bear SSL. These definitions may be found in Crypto/HashApi.

API Documentation
namespace Crypto

Typedefs

template<size_t hashsize>
using Blake2s = HashContext<Blake2sEngine<hashsize>>
using Blake2s256 = Blake2s<32>
using Blake2s128 = Blake2s<16>
template<size_t hashsize>
using HmacBlake2s = HmacContext<Blake2s<hashsize>>
using HmacBlake2s256 = HmacBlake2s<32>
using HmacBlake2s128 = HmacBlake2s<16>
using Secret = Blob

Identifies data which should be treated with care.

template<size_t size_>
using ByteArray = std::array<uint8_t, size_>

Class template for fixed byte array.

Note

Until C++17 (and GCC > 5.5) inheriting from std::array<> breaks aggregate initialization.

using Md5 = HashContext<Md5Engine>
using HmacMd5 = HmacContext<Md5>
using Sha1 = HashContext<Sha1Engine>
using HmacSha1 = HmacContext<Sha1>
using Sha224 = HashContext<Sha224Engine>
using Sha256 = HashContext<Sha256Engine>
using Sha384 = HashContext<Sha384Engine>
using Sha512 = HashContext<Sha512Engine>
using HmacSha224 = HmacContext<Sha224>
using HmacSha256 = HmacContext<Sha256>
using HmacSha384 = HmacContext<Sha384>
using HmacSha512 = HmacContext<Sha512>

Functions

template<size_t size_>
String toString(const ByteArray<size_> &array, char separator = '\0')
CRYPTO_HASH_ENGINE_STD(Md5, md5, MD5_SIZE, MD5_STATESIZE, MD5_BLOCKSIZE)
CRYPTO_HASH_ENGINE_STD(Sha1, sha1, SHA1_SIZE, SHA1_STATESIZE, SHA1_BLOCKSIZE)
CRYPTO_HASH_ENGINE_STD(Sha224, sha224, SHA224_SIZE, SHA224_STATESIZE, SHA224_BLOCKSIZE)
CRYPTO_HASH_ENGINE_STD(Sha256, sha256, SHA256_SIZE, SHA256_STATESIZE, SHA256_BLOCKSIZE)
CRYPTO_HASH_ENGINE_STD(Sha384, sha384, SHA384_SIZE, SHA384_STATESIZE, SHA384_BLOCKSIZE)
CRYPTO_HASH_ENGINE_STD(Sha512, sha512, SHA512_SIZE, SHA512_STATESIZE, SHA512_BLOCKSIZE)
class Blob
#include <Blob.h>

Wraps a pointer to some data with size.

template<class Engine_>
class HashContext
#include <HashContext.h>

Class template for a Hash implementation ‘Context’.

Template Parameters:

Engine – The HashEngine implementation

Subclassed by OtaUpgrade::ChecksumVerifier

Update hash over a given block of data

inline HashContext &update(const Blob &blob)

Data from Blob.

inline HashContext &update(const FSTR::ObjectBase &obj)

Data from flash object.

inline HashContext &update(const void *data, size_t size)

Pointer to data + size.

Parameters:
  • data – Data block

  • size – Length of data in bytes

template<size_t size_>
inline HashContext &update(const ByteArray<size_> &array)

Data in ByteArray.

Public Functions

template<typename ...EngineArgs>
inline HashContext &reset(EngineArgs&&... engineArgs)

Reset the context for a new calculation.

template<typename ...Ts>
inline Hash calculate(Ts&&... args)

Calculate hash on some data.

Parameters:

args – See update() methods

Return values:

Hash

inline Hash getHash()

Finalise and return the final hash value.

Return values:

Hash

inline State getState()

Get intermediate hash state.

Note

This method is only required for core hashes, used by Bear SSL

Parameters:

state – OUT: current state

Return values:

uint64_t – Number of bytes processed so far

inline void setState(const State &state)

Restore intermediate hash state.

Parameter values obtained via previous getState() call

Note

This method is only required for core hashes, used by Bear SSL

Parameters:
  • state

  • count

struct State
#include <HashContext.h>
template<class HashContext>
class HmacContext
#include <HmacContext.h>

HMAC class template.

Implements the HMAC algorithm using any defined hash context

Public Functions

HmacContext() = default

Default HMAC constructor.

Must call init() first.

inline HmacContext(const Secret &key)

Initialise HMAC context with key.

inline HmacContext &init(const Secret &key)

Initialise HMAC with key.

Return values:

Reference – to enable method chaining

template<typename ...Ts>
inline HmacContext &update(Ts&&... args)

Update HMAC with some message content.

Parameters:

args – See HashContext update() methods

Return values:

Reference – to enable method chaining

template<typename ...Ts>
inline Hash calculate(Ts&&... args)

Calculate hash for some data.

Use like this:

    auto hash = Crypto::HmacMd5(mySecret).calculate(myData);

Parameters:

args – See HashContext update() methods

Return values:

Hash

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Esptool

This Component provides Espressif’s tool for reading and writing firmware and other data to hardware.

Options
SPI_SPEED

[read-only] Set by Hardware configuration.

Clock speed for flash memory (20, 26, 40 or 80). Default is 40.

SPI_MODE

[read-only] Set by Hardware configuration.

Flash memory operating mode (quot, dio, dout, qio). Default is qio.

SPI_SIZE

[read-only] Set by Hardware configuration.

Size of flash memory chip (256KB, 512KB, 1MB, 2MB, 4MB). Default is 512K bytes.

The default hardware profile standard sets this to 1MB. You can set HWCONFIG=standard-4m to increase it or create a custom Hardware configuration for your project.

ESPTOOL

Full path of esptool.py

COM_PORT_ESPTOOL

Port to use for flashing device. Default is COM_PORT.

COM_SPEED_ESPTOOL

Baud rate to use for flashing device. Default is COM_SPEED.

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

Submodule: esptool
esptool.py

A Python-based, open-source, platform-independent utility to communicate with the ROM bootloader in Espressif chips.

Test esptool Build esptool
Documentation

Visit the documentation or run esptool.py -h.

Contribute

If you’re interested in contributing to esptool.py, please check the contributions guide.

About

esptool.py was initially created by Fredrik Ahlberg (@themadinventor), and later maintained by Angus Gratton (@projectgus). It is now supported by Espressif Systems. It has also received improvements from many members of the community.

License

This document and the attached source code are released as Free Software under GNU General Public License Version 2 or later. See the accompanying LICENSE file for a copy.

HTTP Parser

http-parser is **not** actively maintained. New projects and projects looking to migrate should consider llhttp.

Build Status

This is a parser for HTTP messages written in C. It parses both requests and responses. The parser is designed to be used in performance HTTP applications. It does not make any syscalls nor allocations, it does not buffer data, it can be interrupted at anytime. Depending on your architecture, it only requires about 40 bytes of data per message stream (in a web server that is per connection).

Features:

  • No dependencies

  • Handles persistent streams (keep-alive).

  • Decodes chunked encoding.

  • Upgrade support

  • Defends against buffer overflow attacks.

The parser extracts the following information from HTTP messages:

  • Header fields and values

  • Content-Length

  • Request method

  • Response status code

  • Transfer-Encoding

  • HTTP version

  • Request URL

  • Message body

Usage

One http_parser object is used per TCP connection. Initialize the struct using http_parser_init() and set the callbacks. That might look something like this for a request parser:

http_parser_settings settings;
settings.on_url = my_url_callback;
settings.on_header_field = my_header_field_callback;
/* ... */

http_parser *parser = malloc(sizeof(http_parser));
http_parser_init(parser, HTTP_REQUEST);
parser->data = my_socket;

When data is received on the socket execute the parser and check for errors.

size_t len = 80*1024, nparsed;
char buf[len];
ssize_t recved;

recved = recv(fd, buf, len, 0);

if (recved < 0) {
  /* Handle error. */
}

/* Start up / continue the parser.
 * Note we pass recved==0 to signal that EOF has been received.
 */
nparsed = http_parser_execute(parser, &settings, buf, recved);

if (parser->upgrade) {
  /* handle new protocol */
} else if (nparsed != recved) {
  /* Handle error. Usually just close the connection. */
}

http_parser needs to know where the end of the stream is. For example, sometimes servers send responses without Content-Length and expect the client to consume input (for the body) until EOF. To tell http_parser about EOF, give 0 as the fourth parameter to http_parser_execute(). Callbacks and errors can still be encountered during an EOF, so one must still be prepared to receive them.

Scalar valued message information such as status_code, method, and the HTTP version are stored in the parser structure. This data is only temporally stored in http_parser and gets reset on each new message. If this information is needed later, copy it out of the structure during the headers_complete callback.

The parser decodes the transfer-encoding for both requests and responses transparently. That is, a chunked encoding is decoded before being sent to the on_body callback.

The Special Problem of Upgrade

http_parser supports upgrading the connection to a different protocol. An increasingly common example of this is the WebSocket protocol which sends a request like

GET /demo HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
WebSocket-Protocol: sample

followed by non-HTTP data.

(See RFC6455 for more information the WebSocket protocol.)

To support this, the parser will treat this as a normal HTTP message without a body, issuing both on_headers_complete and on_message_complete callbacks. However http_parser_execute() will stop parsing at the end of the headers and return.

The user is expected to check if parser->upgrade has been set to 1 after http_parser_execute() returns. Non-HTTP data begins at the buffer supplied offset by the return value of http_parser_execute().

Callbacks

During the http_parser_execute() call, the callbacks set in http_parser_settings will be executed. The parser maintains state and never looks behind, so buffering the data is not necessary. If you need to save certain data for later usage, you can do that from the callbacks.

There are two types of callbacks:

  • notification typedef int (*http_cb) (http_parser*);

    Callbacks: on_message_begin, on_headers_complete, on_message_complete.

  • data typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);

    Callbacks: (requests only) on_url,

    (common) on_header_field, on_header_value, on_body;
    

Callbacks must return 0 on success. Returning a non-zero value indicates error to the parser, making it exit immediately.

For cases where it is necessary to pass local information to/from a callback, the http_parser object’s data field can be used. An example of such a case is when using threads to handle a socket connection, parse a request, and then give a response over that socket. By instantiation of a thread-local struct containing relevant data (e.g. accepted socket, allocated memory for callbacks to write into, etc), a parser’s callbacks are able to communicate data between the scope of the thread and the scope of the callback in a threadsafe manner. This allows http_parser to be used in multi-threaded contexts.

Example:

 typedef struct {
  socket_t sock;
  void* buffer;
  int buf_len;
 } custom_data_t;


int my_url_callback(http_parser* parser, const char *at, size_t length) {
  /* access to thread local custom_data_t struct.
  Use this access save parsed data for later use into thread local
  buffer, or communicate over socket
  */
  parser->data;
  ...
  return 0;
}

...

void http_parser_thread(socket_t sock) {
 int nparsed = 0;
 /* allocate memory for user data */
 custom_data_t *my_data = malloc(sizeof(custom_data_t));

 /* some information for use by callbacks.
 * achieves thread -> callback information flow */
 my_data->sock = sock;

 /* instantiate a thread-local parser */
 http_parser *parser = malloc(sizeof(http_parser));
 http_parser_init(parser, HTTP_REQUEST); /* initialise parser */
 /* this custom data reference is accessible through the reference to the
 parser supplied to callback functions */
 parser->data = my_data;

 http_parser_settings settings; /* set up callbacks */
 settings.on_url = my_url_callback;

 /* execute parser */
 nparsed = http_parser_execute(parser, &settings, buf, recved);

 ...
 /* parsed information copied from callback.
 can now perform action on data copied into thread-local memory from callbacks.
 achieves callback -> thread information flow */
 my_data->buffer;
 ...
}

In case you parse HTTP message in chunks (i.e. read() request line from socket, parse, read half headers, parse, etc) your data callbacks may be called more than once. http_parser guarantees that data pointer is only valid for the lifetime of callback. You can also read() into a heap allocated buffer to avoid copying memory around if this fits your application.

Reading headers may be a tricky task if you read/parse headers partially. Basically, you need to remember whether last header callback was field or value and apply the following logic:

(on_header_field and on_header_value shortened to on_h_*)
 ------------------------ ------------ --------------------------------------------
| State (prev. callback) | Callback   | Description/action                         |
 ------------------------ ------------ --------------------------------------------
| nothing (first call)   | on_h_field | Allocate new buffer and copy callback data |
|                        |            | into it                                    |
 ------------------------ ------------ --------------------------------------------
| value                  | on_h_field | New header started.                        |
|                        |            | Copy current name,value buffers to headers |
|                        |            | list and allocate new buffer for new name  |
 ------------------------ ------------ --------------------------------------------
| field                  | on_h_field | Previous name continues. Reallocate name   |
|                        |            | buffer and append callback data to it      |
 ------------------------ ------------ --------------------------------------------
| field                  | on_h_value | Value for current header started. Allocate |
|                        |            | new buffer and copy callback data to it    |
 ------------------------ ------------ --------------------------------------------
| value                  | on_h_value | Value continues. Reallocate value buffer   |
|                        |            | and append callback data to it             |
 ------------------------ ------------ --------------------------------------------
Parsing URLs

A simplistic zero-copy URL parser is provided as http_parser_parse_url(). Users of this library may wish to use it to parse URLs constructed from consecutive on_url callbacks.

See examples of reading in headers:

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

b64: Base64 Encoding/Decoding Routines
Overview:

libb64 is a library of ANSI C routines for fast encoding/decoding data into and from a base64-encoded format. C++ wrappers are included, as well as the source code for standalone encoding and decoding executables.

base64 consists of ASCII text, and is therefore a useful encoding for storing binary data in a text file, such as xml, or sending binary data over text-only email.

References:
Why?

I did this because I need an implementation of base64 encoding and decoding, without any licensing problems. Most OS implementations are released under either the GNU/GPL, or a BSD-variant, which is not what I require.

Also, the chance to actually use the co-routine implementation in code is rare, and its use here is fitting. I couldn’t pass up the chance. For more information on this technique, see “Coroutines in C”, by Simon Tatham, which can be found online here: http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html

So then, under which license do I release this code? On to the next section…

License:

This work is released under into the Public Domain. It basically boils down to this: I put this work in the public domain, and you can take it and do whatever you want with it.

An example of this “license” is the Creative Commons Public Domain License, a copy of which can be found in the LICENSE file, and also online at http://creativecommons.org/licenses/publicdomain/

Commandline Use:

There is a new executable available, it is simply called base64. It can encode and decode files, as instructed by the user.

To encode a file: $ ./base64 -e filea fileb fileb will now be the base64-encoded version of filea.

To decode a file: $ ./base64 -d fileb filec filec will now be identical to filea.

Programming:

Some C++ wrappers are provided as well, so you don’t have to get your hands dirty. Encoding from standard input to standard output is as simple as

#include <b64/encode.h>
#include <iostream>
int main()
{
    base64::encoder E;
    E.encode(std::cin, std::cout);
    return 0;
}

Both standalone executables and a static library is provided in the package,

Example code:

The ‘examples’ directory contains some simple example C code, that demonstrates how to use the C interface of the library.

Implementation:

It is DAMN fast, if I may say so myself. The C code uses a little trick which has been used to implement coroutines, of which one can say that this implementation is an example.

(To see how the libb64 codebase compares with some other BASE64 implementations available, see the BENCHMARKS file)

The trick involves the fact that a switch-statement may legally cross into sub-blocks. A very thorough and enlightening essay on co-routines in C, using this method, can be found in the above mentioned “Coroutines in C”, by Simon Tatham: http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html

For example, an RLE decompressing routine, adapted from the article: 1 static int STATE = 0; 2 static int len, c; 3 switch (STATE) 4 { 5 while (1) 6 { 7 c = getchar(); 8 if (c == EOF) return EOF; 9 if (c == 0xFF) { 10 len = getchar(); 11 c = getchar(); 12 while (len–) 13 { 14 STATE = 0; 15 return c; 16 case 0: 17 } 18 } else 19 STATE = 1; 20 return c; 21 case 1: 22 } 23 } 24 }

As can be seen from this example, a coroutine depends on a state variable, which it sets directly before exiting (lines 14 and 119). The next time the routine is entered, the switch moves control to the specific point directly after the previous exit (lines 16 and 21).hands

(As an aside, in the mentioned article the combination of the top-level switch, the various setting of the state, the return of a value, and the labelling of the exit point is wrapped in #define macros, making the structure of the routine even clearer.)

The obvious problem with any such routine is the static keyword. Any static variables in a function spell doom for multithreaded applications. Also, in situations where this coroutine is used by more than one other coroutines, the consistency is disturbed.

What is needed is a structure for storing these variabled, which is passed to the routine separately. This obviously breaks the modularity of the function, since now the caller has to worry about and care for the internal state of the routine (the callee). This allows for a fast, multithreading-enabled implementation, which may (obviously) be wrapped in a C++ object for ease of use.

The base64 encoding and decoding functionality in this package is implemented in exactly this way, providing both a high-speed high-maintanence C interface, and a wrapped C++ which is low-maintanence and only slightly less performant.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

libyuarel
Build Status

Very simple and well tested C library for parsing URLs with zero-copy and no mallocs. The library does not do any validation on the URL, neither before nor after it is parsed. The different parts are parsed by searching for special characters like : and /. For a URL should be able to be parsed by yuarel, it has to be constructed in one of the following formats:

Absolute URL: scheme “:” [ “//” ] [ username “:” password “@” ] host [ “:” port ] [ “/” ] [ path ] [ “?” query ] [ “#” fragment ]

Relative URL: path [ “?” query ] [ “#” fragment ]

Parts within [ and ] are optional. A minimal URL could look like this:

a:b or /

Due to the fact that the library isn’t copying any strings and instead points to the parts in the URL string, the first / in the path will be replaced with a null terminator. Therefore, the first slash will be missing in the path.

To build
$ make && make check && sudo make install
Try it

Compile the example in examples/:

$ make examples

Run the example program:

$ ./simple
The structs

The struct that holds the parsed URL looks like this:

struct yuarel {
    char *scheme;   /* scheme, without ":" and "//" */
    char *username; /* username, default: NULL */
    char *password; /* password, default: NULL */
    char *host; /* hostname or IP address */
    int port;   /* port, default: 0 */
    char *path; /* path, without leading "/", default: NULL */
    char *query;    /* query, default: NULL */
    char *fragment; /* fragment, default: NULL */
};

The struct that holds a parsed query string parameter looks like this:

struct yuarel_param {
    char *key;
    char *val;
};
Library functions
Parse a URL to a struct
int yuarel_parse(struct yuarel *url, char *url_str)

struct yuarel *url: a pointer to the struct where to store the parsed values. char *url_str: a pointer to the url to be parsed (null terminated).

Note that the url string will be modified by the function.

Returns 0 on success, otherwise -1.

Split a path into several strings
int yuarel_split_path(char *path, char **parts, int max_parts)

No data is copied, the slashed are used as null terminators and then pointers to each path part will be stored in parts.

char *path: the path to split. The string will be modified. char **parts: a pointer to an array of (char *) where to store the result. int max_parts: max number of parts to parse.

Note that the path string will be modified by the function.

Returns the number of parsed items. -1 on error.

Parse a query string
int yuarel_parse_query(char *query, char delimiter, struct yuarel_param *params, int max_params)

char *query: the query string to parse. The string will be modified. char delimiter: the character that separates the key/value pairs from eachother. struct yuarel_param *params: an array of (struct yuarel_param) where to store the result. int max_values: max number of parameters to parse.

The query string should be a null terminated string of parameters separated by a delimiter. Each parameter are checked for the equal sign character. If it appears in the parameter, it will be used as a null terminator and the part that comes after it will be the value of the parameter.

No data are copied, the equal sign and delimiters are used as null terminators and then pointers to each parameter key and value will be stored in the yuarel_param struct.

Note that the query string will be modified by the function.

Returns the number of parsed items. -1 on error.

How to use it:

Compile with -lyuarel.

#include <stdlib.h>
#include <stdio.h>
#include <yuarel.h>

int main(void)
{
    int p;
    struct yuarel url;
    char *parts[3];
    char url_string[] = "http://localhost:8989/path/to/test?query=yes#frag=1";

    if (-1 == yuarel_parse(&url, url_string)) {
        fprintf(stderr, "Could not parse url!\n");
        return 1;
    }

    printf("scheme:\t%s\n", url.scheme);
    printf("host:\t%s\n", url.host);
    printf("port:\t%d\n", url.port);
    printf("path:\t%s\n", url.path);
    printf("query:\t%s\n", url.query);
    printf("fragment:\t%s\n", url.fragment);

    if (3 != yuarel_split_path(url.path, parts, 3)) {
        fprintf(stderr, "Could not split path!\n");
        return 1;
    }

    printf("path parts: %s, %s, %s\n", parts[0], parts[1], parts[2]);

    printf("Query string parameters:\n");

    p = yuarel_parse_query(url.query, '&', params, 3);
    while (p-- > 0) {
        printf("\t%s: %s\n", params[p].key, params[p].val);
    }
}
References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

LWIP
Introduction

Uses LWIP version 2 to enable networking for Sming running on a Windows or Linux Host system.

Note

Network support is enabled by default. If you don’t need it, use the --nonet command-line option.

Build Variables
ENABLE_LWIPDEBUG
0 (default)

Standard build

1

Enable debugging output

ENABLE_CUSTOM_LWIP

2 (default)

Setting this to any other value will cause a build error.

Linux

Support is provided via TAP network interface (a virtual network layer operating at the ethernet frame level). A TAP interface must be created first, and requires root privilege:

sudo ip tuntap add dev tap0 mode tap user `whoami`
sudo ip a a dev tap0 192.168.13.1/24
sudo ifconfig tap0 up

This creates the tap0 interface. The emulator will automatically select the first tap interface found. To override this, use the --ifname option. An IP address will be assigned, but can be changed using the --ipaddr option.

If your application needs to access the internet, additional setup is required:

sudo sysctl net.ipv4.ip_forward=1
sudo sysctl net.ipv6.conf.default.forwarding=1
sudo sysctl net.ipv6.conf.all.forwarding=1

export INTERNET_IF=wlan0 # <!--- Make sure to replace wlan0 with the network interface connected to Internet

sudo iptables -t nat -A POSTROUTING -o $INTERNET_IF -j MASQUERADE
sudo iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i tap0 -o $INTERNET_IF -j ACCEPT
Windows

Requires NPCAP library to be installed. Provided with current (3.0.2) version of Wireshark.

By default, the first valid network adapter will be used, with address assigned via DHCP.

If the adapter is wrong, get a list thus:

out\Host\Windows\debug\firmware\app --ifname=?

or

make run HOST_NETWORK_OPTIONS=--ifname=?

produces a listing:

Available adapters:
- 0: {ACC6BFB2-A15B-4CF8-B93A-8D97644D0AAC} - Oracle
        192.168.56.1 / 255.255.255.0
- 1: {A12D4DD0-0EA8-435D-985E-A1F96F781EF0} - NdisWan Adapter
- 2: {3D66A354-39DD-4C6A-B9C4-14EE223FC3D1} - MS NDIS 6.0 LoopBack Driver
        0.0.0.0 / 255.0.0.0
- 3: {BC53D919-339E-4D70-8573-9D7A8AE303C7} - NdisWan Adapter
- 4: {3CFD43EA-9CC7-44A7-83D4-EB04DD029FE7} - NdisWan Adapter
- 5: {530640FF-A9C3-436B-9EA2-65102C788119} - Realtek PCIe GBE Family Controller
        192.168.1.70 / 255.255.255.0
- 6: {0F649280-BAC2-4515-9CE3-F7DFBB6A1BF8} - Kaspersky Security Data Escort Adapter
        10.102.37.150 / 255.255.255.252

Then use the appropriate number (or GUID), with the gateway IP address - an address will be assigned via DHCP:

make run HOST_NETWORK_OPTIONS="--ifname=5 --gateway=192.168.1.254"

You can find gateway addresses using the ipconfig command.

If you want to use a specific IP address, the appropriate adapter will be selected but you still need to specify the gateway address:

make run HOST_NETWORK_OPTIONS="--ipaddr=192.168.1.10 --gateway=192.168.1.254"
References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: lwip
INTRODUCTION

lwIP is a small independent implementation of the TCP/IP protocol suite.

The focus of the lwIP TCP/IP implementation is to reduce the RAM usage while still having a full scale TCP. This making lwIP suitable for use in embedded systems with tens of kilobytes of free RAM and room for around 40 kilobytes of code ROM.

lwIP was originally developed by Adam Dunkels at the Computer and Networks Architectures (CNA) lab at the Swedish Institute of Computer Science (SICS) and is now developed and maintained by a worldwide network of developers.

FEATURES
  • IP (Internet Protocol, IPv4 and IPv6) including packet forwarding over multiple network interfaces

  • ICMP (Internet Control Message Protocol) for network maintenance and debugging

  • IGMP (Internet Group Management Protocol) for multicast traffic management

  • MLD (Multicast listener discovery for IPv6). Aims to be compliant with RFC 2710. No support for MLDv2

  • ND (Neighbor discovery and stateless address autoconfiguration for IPv6). Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862 (Address autoconfiguration)

  • DHCP, AutoIP/APIPA (Zeroconf), ACD (Address Conflict Detection) and (stateless) DHCPv6

  • UDP (User Datagram Protocol) including experimental UDP-lite extensions

  • TCP (Transmission Control Protocol) with congestion control, RTT estimation fast recovery/fast retransmit and sending SACKs

  • raw/native API for enhanced performance

  • Optional Berkeley-like socket API

  • TLS: optional layered TCP (“altcp”) for nearly transparent TLS for any TCP-based protocol (ported to mbedTLS) (see changelog for more info)

  • PPPoS and PPPoE (Point-to-point protocol over Serial/Ethernet)

  • DNS (Domain name resolver incl. mDNS)

  • 6LoWPAN (via IEEE 802.15.4, BLE or ZEP)

APPLICATIONS
  • HTTP server with SSI and CGI (HTTPS via altcp)

  • SNMPv2c agent with MIB compiler (Simple Network Management Protocol), v3 via altcp

  • SNTP (Simple network time protocol)

  • NetBIOS name service responder

  • MDNS (Multicast DNS) responder

  • iPerf server implementation

  • MQTT client (TLS support via altcp)

LICENSE

lwIP is freely available under a BSD license.

DEVELOPMENT

lwIP has grown into an excellent TCP/IP stack for embedded devices, and developers using the stack often submit bug fixes, improvements, and additions to the stack to further increase its usefulness.

Development of lwIP is hosted on Savannah, a central point for software development, maintenance and distribution. Everyone can help improve lwIP by use of Savannah’s interface, Git and the mailing list. A core team of developers will commit changes to the Git source tree.

The lwIP TCP/IP stack is maintained in the ‘lwip’ Git module and contributions (such as platform ports) are in the ‘contrib’ Git module.

See doc/savannah.txt for details on Git server access for users and developers.

The current Git trees are web-browsable: http://git.savannah.gnu.org/cgit/lwip.git http://git.savannah.gnu.org/cgit/lwip/lwip-contrib.git

Submit patches and bugs via the lwIP project page: http://savannah.nongnu.org/projects/lwip/

Continuous integration builds (GCC, clang): https://travis-ci.org/lwip-tcpip/lwip

DOCUMENTATION

Self documentation of the source code is regularly extracted from the current Git sources and is available from this web page: http://www.nongnu.org/lwip/

Also, there are mailing lists you can subscribe at http://savannah.nongnu.org/mail/?group=lwip plus searchable archives: http://lists.nongnu.org/archive/html/lwip-users/ http://lists.nongnu.org/archive/html/lwip-devel/

There is a wiki about lwIP at http://lwip.wikia.com/wiki/LwIP_Wiki You might get questions answered there, but unfortunately, it is not as well maintained as it should be.

lwIP was originally written by Adam Dunkels: http://dunkels.com/adam/

Reading Adam’s papers, the files in docs/, browsing the source code documentation and browsing the mailing list archives is a good way to become familiar with the design of lwIP.

Adam Dunkels adam@sics.se Leon Woestenberg leon.woestenberg@gmx.net

malloc_count

This Component is a modified version of the original code, intended to provide basic heap monitoring for the Sming Host Emulator.

The following is the original README.

Introduction

malloc_count provides a set of source code tools to measure the amount of allocated memory of a program at run-time. The code library provides facilities to

  • measure the current and peak heap memory allocation, and

  • write a memory profile for plotting, see the figure on the right.

  • Furthermore, separate stack_count function can measure stack usage.

The code tool works by intercepting the standard malloc(), free(), etc functions. Thus no changes are necessary to the inspected source code.

See http://panthema.net/2013/malloc_count for the current version.

Intercepting Heap Allocation Functions

The source code of malloc_count.[ch] intercepts the standard heap allocation functions malloc(), free(), realloc() and calloc() and adds simple counting statistics to each call. Thus the program must be relinked for malloc_count to work. Each call to malloc() and others is passed on to lower levels, and the regular malloc() is used for heap allocation.

Of course, malloc_count can also be used with C++ programs and maybe even script languages like Python and Perl, because the new operator and most script interpreters allocations all are based on malloc.

The tools are usable under Linux and probably also with Cygwin and MinGW, as they too support the standard Linux dynamic link loading mechanisms.

Memory Profile and stack_count

The malloc_count source is accompanied by two further memory analysis tools: stack_count and a C++ header called memprofile.h.

In stack_count.[ch] two simple functions are provided that can measure the maximum stack usage between two points in a program.

Maybe the most useful application of malloc_count is to create a memory/heap profile of a program (while it is running). This profile can also be created using the well-known valgrind tool “massif”, however, massif is really slow. The overhead of malloc_count is much smaller, and using memprofile.h a statistic file can be produced, which is directly usable with Gnuplot.

The source code archive contains two example applications: one which queries malloc_count for current heap usage, and a second which creates the memory profile in the figure on the right. See the STX B+ Tree library for another, more complex example of a memory profile.

Downloads

See http://panthema.net/2013/malloc_count for the current version.

The source code is published under the MIT License (MIT), which is also found in the header of all source files.

Short Usage Guide

Compile malloc_count.c and link it with your program. The source file malloc_count.o should be located towards the end of the .o file sequence. You must also add “-ldl“ to the list of libraries.

Run your program and observe that when terminating, it outputs a line like

malloc_count ### exiting, total: 12,582,912, peak: 4,194,304, current: 0

If desired, increase verbosity

  1. by setting log_operations = 1 at the top of malloc_count.c and adapting log_operations_threshold to output only large allocations, or

  2. by including malloc_count.h in your program and using the user-functions define therein to output memory usage at specific checkpoints. See the directory test-malloc_count/ in the source code for an example.

Tip: Set the locale environment variable LC_NUMERIC=en_GB or similar to get comma-separation of thousands in the printed numbers.

The directory test-memprofile/ contains a simple program, which fills a std::vector and std::set with integers. The memory usage of these containers is profiled using the facilities of memprofile.h, which are described verbosely in the source.

Thread Safety

The current statistic methods in malloc_count.c are not thread-safe. However, the general mechanism (as described below) is per-se thread-safe. The only non-safe parts are adding and subtracting from the counters in inc_count() and dec_count().

The malloc_count.c code contains a #define THREAD_SAFE_GCC_INTRINSICS, which enables use of gcc’s intrinsics for atomic counting operations. If you use gcc, enable this option to make the malloc_count tool thread-safe.

The functions in memprofile.h are not thread-safe. stack_count can also be used on local thread stacks.

Technicalities of Intercepting libc Function Calls

The method used in malloc_count to hook the standard heap allocation calls is to provide a source file exporting the symbols “malloc“, “free“, etc. These override the libc symbols and thus the functions in malloc_count are used instead.

However, malloc_count does not implement a heap allocator. It loads the symbols “malloc“, “free“, etc. directly using the dynamic link loader “dl“ from the chain of shared libraries. Calls to the overriding “malloc“ functions are forwarded to the usual libc allocator.

To keep track of the size of each allocated memory area, malloc_count uses a trick: it prepends each allocation pointer with two additional bookkeeping variables: the allocation size and a sentinel value. Thus when allocating n bytes, in truth n + c bytes are requested from the libc malloc() to save the size (c is by default 16, but can be adapted to fix alignment problems). The sentinel only serves as a check that your program has not overwritten the size information.

Closing Credits

The idea for this augmenting interception method is not my own, it was borrowed from Jeremy Kerr http://ozlabs.org/~jk/code/.

Written 2013-01-21, 2013-03-16, and 2014-09-10 by Timo Bingmann tb@panthema.net

API Documentation
namespace MallocCount

Typedefs

using Callback = std::function<void(size_t current)>

Callback function type.

Param current:

Current allocated bytes

Functions

size_t getCurrent(void)

Get the currently allocated amount of memory.

size_t getPeak(void)

Get the peak memory allocation.

void resetPeak(void)

Reset the peak memory allocation to current.

size_t getTotal(void)

Get the total cumulative memory allocation.

void resetTotal(void)

Reset the total cumulative memory allocation to zero.

size_t getAllocCount(void)

Get the total number of allocations.

void setAllocLimit(size_t maxBytes)

Set an allocation limit.

Parameters:

maxBytes – Specify 0 for no limit

void setCallback(Callback callback)

Set a callback function that is invoked on each change of the current allocation.

Note

The callback function must not use malloc()/realloc()/free() or it will go into an endless recursive loop!

void enableLogging(bool enable)

Enable/disable logging.

void setLogThreshold(size_t threshold)

Set minimum allocation size for log output (when enabled)

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

mqtt-protocol-c

Zero-copy, interruptible MQTT protocol parser and serialiser written in C. Based initially on Deoxxa’s code and extended to support full client and server parsing and serialization.

Overview

mqtt-protocol-c is designed for use in resource-constrained environments. To that end, it avoids making any memory allocations or assumptions about the underlying system. It handles only the binary parsing/serialising part of the MQTT protocol, leaving all the logic and networking up to the user.

Examples

Take a look at test_serialiser and test_parser in the bin/test.c file.

License

BSD-3 Clause license. You can read the full license from here.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

rBoot
Introduction

rBoot is a second-stage bootloader that allows booting application images from several pre-configured flash memory addresses, called “slots”. Sming supports up to three slots.

Note

With Sming 4.3 partitions are used to manage flash storage. A “slot” refers to a specific application partition, typically rom0, rom1 or rom2.

The location or size of these partitions is determined by the Hardware configuration.

The bootloader has been modified to use the partition table as reference, identifying slots by the partition sub-type.

Where systems are to be updated Over the Air (OTA) at least two application partitions are required. The bootloader identifies these by their partition subtype: slot #0 -> App/Ota_0, slot #1 -> App/Ota_1, etc.

Fixed applications without OTA capability use a single application image. This must be the App/Factory partition type, and corresponds to slot #0.

At startup, the bootloader will use the partition table to locate the application image. It will also ensure that the ROM slot information in the boot configuration is consistent, and update it if necessary.

Attention

Make sure that your slots do not extend beyond a 1MB boundary and do not overlap with each other, the file system (if enabled) or the RFcal/system parameters area! Sming currently has no means of detecting a misconfigured memory layout.

Slot 0

This is the default slot (rom0, the primary application partition) which is always used.

RBOOT_ROM0_ADDR

[read-only]

This is the start address for slot 0.

Except for the use case described in Slot2 below, you should not need to change this.

Slot 1
RBOOT_ROM1_ADDR

[read-only] default: disabled

The start address of slot 1.

If your application includes any kind of Over-the-Air (OTA) firmware update functionality, you will need a second memory slot to store the received update image while the update routines execute from the first slot.

Note

The spiffs-two-roms configuration can be used for this purpose.

Upon successful completion of the update, the second slot is activated, such that on next reset rBoot boots into the uploaded application. While now running from slot 1, the next update will be stored to slot 0 again, i.e. the roles of slot 0 and slot 1 are flipped with every update.

For devices with more than 1MB of flash memory, it is advisable to choose an address for rom1 with the same offset within its 1MB block as rom0.

Slot 2 (GPIO slot)

rBoot supports booting into a third slot upon explicit user request, e.g. by pressing a button during reset/power up. This is especially useful for implementing some sort of recovery mechanism.

To enable slot 2, set these values:

RBOOT_GPIO_ENABLED

Disabled by default. Set to 1 to enable slot 2.

RBOOT_ROM2_ADDR

[read-only]

Address for slot 2. You must create a custom Hardware configuration for your project with a definition for rom2.

{
   ...
   "partitions": {
      "rom2": {
         "address": "0x100000",
         "size": "512K",
         "type": "app",
         "subtype": "ota_2"
      }
   }
}

Note

At present, this will only configure rBoot. Sming will not create an application image for slot 2.

You can, however, use a second Sming project to build a recovery application image as follows:

  • Create a new Sming project for your recovery application. This will be a simple single-slot project. Create a new Hardware configuration and configure the rom0 start address and size to the same as the rom2 partition of the main project.

option (a)

  • Build and flash the recovery project as usual by typing make flash. This will install the recovery ROM (into slot 2 of the main project) and a temporary bootloader, which will be overwritten in the next step.

  • Go back to your main project. Build and flash it with make flash. This will install the main application (into slot 0) and the final bootloader. You are now all set for booting into the recovery image if the need arises.

option (b)

  • Run a normal make for your recovery project

  • Locate the firmware image file, typically out/Esp8266/release/firmware/rom0.bin (adjust accordingly if using a debug build). Copy this image file as rom2.bin into your main project directory.

  • Add an additional property to the rom2 partition entry in your main project:

    "filename": "rom2.bin"
    

    When you run make flash in this will get written along with the other partitions.

Automatically derived settings

The RBOOT_BIG_FLASH and RBOOT_TWO_ROMS settings are now read-only as their values are derived automatically.

In earlier versions of Sming these had to be set manually.

Big Flash Mode

The ESP8266 can map only 1MB of flash memory into its internal address space at a time. As you might expect, the first megabyte is mapped by default. This is fine if your image(s) reside(s) in this range. Otherwise, rBoot has to inject a piece of code into the application startup routine to set up the flash controller with the correct mapping.

RBOOT_BIG_FLASH

READONLY Set when RBOOT_ROM0_ADDR or RBOOT_ROM1_ADDR >= 0x100000.

See also Big flash support.

Single vs. Dual ROMs

Since every 1MB range of flash memory is mapped to an identical internal address range, the same ROM image can be used for slots 0 and 1 if (and only if!) both slots have the same address offsets within their 1MB blocks, i.e. (RBOOT_ROM0_ADDR & 0xFFFFF) == (RBOOT_ROM1_ADDR & 0xFFFFF).

Consequently, for such configurations, the Sming build system generates only one ROM image.

In all other cases, two distinct application images must be linked with different addresses for the ‘irom0_0_seg’ memory region. You should use the two-rom-mode Hardware configuration for this. The Sming build system will handle everything else, including linker script generation.

RBOOT_TWO_ROMS

READONLY Determines if rBoot needs to generate distinct firmware images.

Further Configuration Settings
RBOOT_SILENT

Default: 0 (verbose)

At system restart rBoot outputs debug information to the serial port. Set to 1 to disable.

RBOOT_LD_TEMPLATE

Path to the linker script template. The actual script is output to the application build directory (e.g. rom0.ld), replacing the irom0_0_seg entry according to the configured build settings.

RBOOT_ROM_0

Base name for firmware image #0. Default is rom0.

RBOOT_ROM_1

Base name for firmware image #1. Default is rom1.

ESPTOOL2

READONLY Defines the path to the esptool2 tool which rBoot uses to manipulate ROM images. Use $(ESPTOOL2) if you need it within your own projects.

API Documentation
API Documentation
rboot-api.h

Functions

rboot_config rboot_get_config(void)

Read rBoot configuration from flash.

Note

Returns rboot_config (defined in rboot.h) allowing you to modify any values in it, including the ROM layout.

Return values:

rboot_config – Copy of the rBoot configuration

bool rboot_set_config(rboot_config *conf)

Write rBoot configuration to flash memory.

Note

Saves the rboot_config structure back to configuration sector (BOOT_CONFIG_SECTOR) of the flash, while maintaining the contents of the rest of the sector. You can use the rest of this sector for your app settings, as long as you protect this structure when you do so.

Parameters:

conf – pointer to a rboot_config structure containing configuration to save

Return values:

bool – True on success

uint8_t rboot_get_current_rom(void)

Get index of current ROM.

Note

Get the currently selected boot ROM (this will be the currently running ROM, as long as you haven’t changed it since boot or rBoot booted the rom in temporary boot mode, see rboot_get_last_boot_rom).

Return values:

uint8_t – Index of the current ROM

bool rboot_set_current_rom(uint8_t rom)

Set the index of current ROM.

Note

Set the current boot ROM, which will be used when next restarted.

Note

This function re-writes the whole configuration to flash memory (not just the current ROM index)

Parameters:

rom – The index of the ROM to use on next boot

Return values:

bool – True on success

rboot_write_status rboot_write_init(uint32_t start_addr)

Initialise flash write process.

Note

Call once before starting to pass data to write to flash memory with rboot_write_flash function. start_addr is the address on the SPI flash to write from. Returns a status structure which must be passed back on each write. The contents of the structure should not be modified by the calling code.

Parameters:

start_addr – Address on the SPI flash to begin write to

bool rboot_write_end(rboot_write_status *status)

Complete flash write process.

Note

Call at the completion of flash writing. This ensures any outstanding bytes are written (if data so far hasn’t been a multiple of 4 bytes there will be a few bytes unwritten, until you call this function).

Parameters:

status – Pointer to rboot_write_status structure defining the write status

bool rboot_write_flash(rboot_write_status *status, const uint8_t *data, uint16_t len)

Write data to flash memory.

Note

Call repeatedly to write data to the flash, starting at the address specified on the prior call to rboot_write_init. Current write position is tracked automatically. This method is likely to be called each time a packet of OTA data is received over the network.

Note

Call rboot_write_init before calling this function to get the rboot_write_status structure

Parameters:
  • status – Pointer to rboot_write_status structure defining the write status

  • data – Pointer to a block of uint8_t data elements to be written to flash

  • len – Quantity of uint8_t data elements to write to flash

struct rboot_write_status
#include <rboot-api.h>

Structure defining flash write status.

Note

The user application should not modify the contents of this structure.

Public Members

uint32_t start_addr
uint32_t start_sector
int32_t last_sector_erased
uint8_t extra_count
uint8_t extra_bytes[4]
rboot.h

Defines

CHKSUM_INIT
SECTOR_SIZE
BOOT_CONFIG_SECTOR
BOOT_CONFIG_MAGIC
BOOT_CONFIG_VERSION
MODE_STANDARD
MODE_GPIO_ROM
MODE_TEMP_ROM
MODE_GPIO_ERASES_SDKCONFIG
MODE_GPIO_SKIP
RBOOT_RTC_MAGIC
RBOOT_RTC_READ
RBOOT_RTC_WRITE
RBOOT_RTC_ADDR
BOOT_GPIO_NUM
MAX_ROMS
struct rboot_config
#include <rboot.h>

Structure containing rBoot configuration.

Note

ROM addresses must be multiples of 0x1000 (flash sector aligned). Without BOOT_BIG_FLASH only the first 8Mbit (1MB) of the chip will be memory mapped so ROM slots containing .irom0.text sections must remain below 0x100000. Slots beyond this will only be accessible via spi read calls, so use these for stored resources, not code. With BOOT_BIG_FLASH the flash will be mapped in chunks of 8MBit (1MB), so ROMs can be anywhere, but must not straddle two 8MBit (1MB) blocks.

Public Members

uint8_t magic

Our magic, identifies rBoot configuration - should be BOOT_CONFIG_MAGIC.

uint8_t version

Version of configuration structure - should be BOOT_CONFIG_VERSION.

uint8_t mode

Boot loader mode (MODE_STANDARD | MODE_GPIO_ROM | MODE_GPIO_SKIP)

uint8_t current_rom

Currently selected ROM (will be used for next standard boot)

uint8_t gpio_rom

ROM to use for GPIO boot (hardware switch) with mode set to MODE_GPIO_ROM.

uint8_t count

Quantity of ROMs available to boot.

uint8_t unused[2]

Padding (not used)

uint32_t roms[MAX_ROMS]

Flash addresses of each ROM.

ESP8266 Cache_Read_Enable

12th June 2015 Richard

Since I haven’t seen this documented anywhere, here is my attempt to explain the Cache_Read_Enable function. Valid values and what they do (at a register level) are from decompiling the code. The outcome of those values is based on my own experimentation so my descriptions and explanations may be silly but they currently fit the observed results.

void Cache_Read_Enable(uint8 odd_even, uint8 mb_count, unt8 no_idea);

Valid values for odd_even

0 – clears bits 24 & 25 of control register 0x3FF0000C 1 – clears bit 24, sets bit 25 other – clears bit 25, sets bit 24

Function of odd_even

0 – allows access to even numbered mb 1 – allow access to odd numbered mb other – appears to do the same as 1, there must be a difference but I haven’t worked out what it is

Valid values for mb_count

0-7 – set bits 16, 17 & 18 of control register 0x3FF0000C

Function of mb_count
Which odd or even bank to map (according to odd_even option)

e.g. mb_count = 0, odd_even = 0 -> map first 8Mbit of flash e.g. mb_count = 0, odd_even = 1 -> map second 8Mbit of flash e.g. mb_count = 1, odd_even = 0 -> map third 8Mbit of flash e.g. mb_count = 1, odd_even = 1 -> map fourth 8Mbit of flash

Valid values for no_idea

0 – sets bit 3 of 0x3FF00024 1 – sets bit 26 of 0x3FF0000C and sets bits 3 & 4 of 0x3FF00024

Function of no_idea

The clue is in the name, I can’t work out what this does from my experiments, but the SDK always sets this to 1.

Source: https://richard.burtons.org/2015/06/12/esp8266-cache_read_enable/

13/11/2019 @author mikee47 UPDATE

Ref. RTOS SDK source bootloader_support/bootloader_utility.c, function bootloader_utility_load_image():

extern void Cache_Read_Enable(uint8_t map, uint8_t p, uint8_t v);
...
Cache_Read_Enable(map, 0, SOC_CACHE_SIZE);

Where SOC_CACHE_SIZE is defined as:

#ifdef CONFIG_SOC_FULL_ICACHE
#define SOC_CACHE_SIZE 1 // 32KB
#else
#define SOC_CACHE_SIZE 0 // 16KB
#endif
06/04/2021 @author mikee47 UPDATE

RTOS SDK code has changed, now see usage in esp_fast_boot.c. Call looks like this:

Cache_Read_Enable(sub_region, region, SOC_CACHE_SIZE);

See esp_fast_boot_restart(). Code (rearranged) looks like this:

extern void pm_goto_rf_on(void);
extern void clockgate_watchdog(int on);

int esp_fast_boot_restart(void)
{
   const esp_partition_t* to_boot = esp_ota_get_boot_partition();
   if (!to_boot) {
      ESP_LOGI(TAG, "no OTA boot partition");
      to_boot = esp_ota_get_running_partition();
      if (!to_boot) {
            ESP_LOGE(TAG, "ERROR: Fail to get running partition");
            return -EINVAL;
      }
   }

   uint32_t image_start = to_boot->address;
   uint32_t image_size = to_boot->size - 4;

   esp_image_header_t image;
   int ret = spi_flash_read(image_start, &image, sizeof(esp_image_header_t));
   if (ret != ESP_OK) {
      ESP_LOGE(TAG, "ERROR: Fail to read image head from spi flash error=%d", ret);
      return -EIO;
   }

   uint32_t image_entry = image.entry_addr;
   uint8_t region;
   if (image_start < 0x200000) {
      region = 0;
   } else if (image_start < 0x400000) {
      region = 1;
   } else if (image_start < 0x600000) {
      region = 2;
   } else if (image_start < 0x800000) {
      region = 3;
   } else {
      ESP_LOGE(TAG, "ERROR: App bin error, start_addr 0x%08x image_len %d\n", image_start, image_size);
      return -EINVAL;
   }

   uint8_t sub_region;
   uint32_t image_mask =  image_start & 0x1fffff;
   if (image_mask < 0x100000) {
      sub_region = 0;
   } else {
      sub_region = 1;
   }

   pm_goto_rf_on();
   clockgate_watchdog(0);
   REG_WRITE(0x3ff00018, 0xffff00ff);
   SET_PERI_REG_MASK(0x60000D48, BIT1);
   CLEAR_PERI_REG_MASK(0x60000D48, BIT1);

   REG_WRITE(INT_ENA_WDEV, 0);
   _xt_isr_mask(UINT32_MAX);

   const uint32_t sp = DRAM_BASE + DRAM_SIZE - 16;

   Cache_Read_Disable();
   Cache_Read_Enable(sub_region, region, SOC_CACHE_SIZE);

   __asm__ __volatile__(
      "mov    a1, %0\n"
      : : "a"(sp) : "memory"
   );

   void (*user_start)(size_t start_addr);
   user_start = (void *)entry_addr;
   user_start(image_start);
}
References
Used by
Environment Variables
SoC support
  • esp8266

  • host

Submodule: rboot
rBoot - An open source boot loader for the ESP8266

by Richard A Burton, richardaburton@gmail.com http://richard.burtons.org/

March 2021: Forked for use with Sming and partition tables.

rBoot is designed to be a flexible open source boot loader, a replacement for the binary blob supplied with the SDK. It has the following advantages over the Espressif loader:

  • Open source (written in C).

  • Supports up to 256 roms.

  • Roms can be variable size.

  • Able to test multiple roms to find a valid backup (without resetting).

  • Flash layout can be changed on the fly (with care and appropriately linked rom images).

  • GPIO support for rom selection.

  • Wastes no stack space (SDK boot loader uses 144 bytes).

  • Documented config structure to allow easy editing from user code.

  • Can validate .irom0.text section with checksum.

  • Temporary next-boot rom selection.

Limitations

The ESP8266 can only map 8Mbits (1MB) of flash to memory, but which 8Mbits to map is selectable. This allows individual roms to be up to 1MB in size, so long as they do not straddle an 8Mbit boundary on the flash. This means you could have four 1MB roms or 8 512KB roms on a 32Mbit flash (such as on the ESP-12), or a combination. Note, however, that you could not have, for example, a 512KB rom followed immediately by a 1MB rom because the 2nd rom would then straddle an 8MBit boundary. By default support for using more than the first 8Mbit of the flash is disabled, because it requires several steps to get it working. See below for instructions.

Building

A Makefile is included, which should work with the gcc xtensa cross compiler. There are two source files, the first is compiled and included as data in the second. When run this code is copied to memory and executed (there is a good reason for this, see my blog for an explanation). The make file will handle this for you, but you’ll need my esptool2 (see github).

To use the Makefile set SDK_BASE to point to the root of the Espressif SDK and either set XTENSA_BINDIR to the gcc xtensa bin directory or include it in your PATH. These can be set as environment variables or by editing the Makefile.

Two small assembler stub functions allow the bootloader to launch the user code without reserving any space on the stack (while the SDK boot loader uses 144 bytes). This compiles fine with GCC, but if you use another compiler and it will not compile/work for you then uncomment the #define BOOT_NO_ASM in rboot.h to use a C version of these functions (this uses 32 bytes).

Tested with SDK v2.2 and GCC v4.8.5.

Installation

Simply write rboot.bin to the first sector of the flash. Remember to set your flash size correctly with your chosen flash tool (e.g. for esptool.py use the -fs option). When run rBoot will create it’s own config at the start of sector two for a simple two rom system. You can can then write your two roms to flash addresses 0x2000 and (half chip size + 0x2000). E.g. for 8Mbit flash: esptool.py write_flash -fs 8m 0x0000 rboot.bin 0x2000 user1.bin 0x82000 user2.bin

Note: your device may need other options specified. E.g. The nodemcu devkit v1.0 (commonly, but incorrectly, sold as v2) also needs the -fm dio option.

For more interesting rom layouts you’ll need to write an rBoot config sector manually, see next step.

The two testload bin files can be flashed in place of normal user roms for testing rBoot. You do not need these for normal use.

rBoot Config
typedef struct {
    uint8_t magic;           // our magic
    uint8_t version;         // config struct version
    uint8_t mode;            // boot loader mode
    uint8_t current_rom;     // currently selected rom
    uint8_t gpio_rom;        // rom to use for gpio boot
    uint8_t count;           // number of roms in use
    uint8_t unused[2];       // padding
    uint32_t roms[MAX_ROMS]; // flash addresses of the roms
#ifdef BOOT_CONFIG_CHKSUM
    uint8_t chksum;          // boot config chksum
#endif
} rboot_config;

Write a config structure as above to address 0x1000 on the flash. If you want more than 4 roms (default) just increase MAX_ROMS when you compile rBoot. Think about how you intend to layout your flash before you start! Rom addresses must be sector aligned i.e start on a multiple of 4096.

  • magic should have value 0xe1 (defined as BOOT_CONFIG_MAGIC).

  • version is used in case the config structure changes after deployment. It is defined as 0x01 (BOOT_CONFIG_VERSION). I don’t intend to increase this, but you should if you choose to reflash the bootloader after deployment and the config structure has changed.

  • mode can be 0x00 (MODE_STANDARD) or 0x01 (MODE_GPIO_ROM). See below for an explanation of MODE_GPIO_ROM. There is also an optional extra mode flag 0x04 (MODE_GPIO_ERASES_SDKCONFIG), see below for details.

  • current_rom is the rom to boot, numbered 0 to count-1.

  • gpio_rom is the rom to boot when the GPIO is triggered at boot.

  • count is the number of roms available (may be less than MAX_ROMS, but not more).

  • unused[2] is padding so the uint32_t rom addresses are 4 bytes aligned.

  • roms is the array of flash address for the roms. The default generated config will contain two entries: 0x00002000 and 0x00082000.

  • chksum (if enabled, not by default) should be the xor of 0xef followed by each of the bytes of the config structure up to (but obviously not including) the chksum byte itself.

Default config

A default config sector will be created on boot if one does not exists, or if an existing config is corrupted, and the default rom will be set to rom 0. If you want to have a very customised config for which the default would not be suitable, you can override the implementation in the rboot.h header file. See the comments and example code in rboot.h for more information.

GPIO boot mode
RBOOT_GPIO_ENABLED

If rBoot is compiled with BOOT_GPIO_ENABLED set in rboot.h (or RBOOT_GPIO_ENABLED set in the Makefile), then GPIO boot functionality will be included in the rBoot binary. The feature can then be enabled by setting the rboot_config mode field to MODE_GPIO_ROM. You must also set gpio_rom in the config to indicate which rom to boot when the GPIO is activated at boot.

If the GPIO input pin reads high at boot then rBoot will start the currently selected normal or temp rom (as appropriate). However if the GPIO is pulled low then the rom indicated in config option gpio_rom is started instead.

The default GPIO is 16, but this can be overridden in the Makefile (RBOOT_GPIO_NUMBER) or rboot.h (BOOT_GPIO_NUM). If GPIOs other than 16 are used, the internal pullup resistor is enabled before the pin is read and disabled immediately afterwards. For pins that default on reset to configuration other than GPIO input, the pin mode is changed to input when reading but changed back before rboot continues.

After a GPIO boot the current_rom field will be updated in the config, so the GPIO booted rom should change this again if required.

GPIO boot skip mode
RBOOT_GPIO_SKIP_ENABLED

If rBoot is compiled with BOOT_GPIO_SKIP_ENABLED set in rboot.h (or RBOOT_GPIO_SKIP_ENABLED set in the Makefile), then a GPIO can be used to skip to the next rom at boot. The feature must then be enabled by setting the rboot_config ‘mode’ field to MODE_GPIO_SKIP. This means you do not need to have a dedicated GPIO boot rom. If you have a rom that is technically good (valid checksum, etc.) but has operational problems, e.g. wifi doesn’t work or it crashes on boot, rBoot will not be able to detect that and switch rom automatically. In this scenario rebooting the device while pulling the GPIO low will force rBoot to skip this rom and try the next one instead. In a simple two rom setup this simply toggles booting of the other rom.

RBOOT_GPIO_SKIP_ENABLED and RBOOT_GPIO_ENABLED cannot be used at the same time. BOOT_GPIO_NUM is used to select the GPIO pin, as with RBOOT_GPIO_ENABLED.

Erasing SDK configuration on GPIO boot (rom or skip mode)

If you set the MODE_GPIO_ERASES_SDKCONFIG flag in the configuration like this: conf.mode = MODE_GPIO_ROM|MODE_GPIO_ERASES_SDKCONFIG; then a GPIO boot will also the erase the Espressif SDK persistent settings store in the final 16KB of flash. This includes removing calibration constants, saved SSIDs, etc.

Note that MODE_GPIO_ERASES_SDKCONFIG is a flag, so it has to be set as well as MODE_GPIO_ROM to take effect.

Linking user code

Each rom will need to be linked with an appropriate linker file, specifying where it will reside on the flash. If you are only flashing one rom to multiple places on the flash it must be linked multiple times to produce the set of rom images. This is the same as with the SDK loader.

Because there are endless possibilities for layout with this loader I don’t supply sample linker files. Instead I’ll tell you how to make them.

For each rom slot on the flash take a copy of the eagle.app.v6.ld linker script from the sdk. You then need to modify just one line in it for each rom: irom0_0_seg :                         org = 0x40240000, len = 0x3C000

Change the org address to be 0x40200000 (base memory mapped location of the flash) + flash address + 0x10 (offset of data after the header). The logical place for your first rom is the third sector, address 0x2000. 0x40200000 + 0x2000 + 0x10 = 0x40202010 If you use the default generated config the loader will expect to find the second rom at flash address half-chip-size + 0x2000 (e.g. 0x82000 on an 8MBit flash) so the irom0_0_seg should be: 0x40200000 + 0x82000 + 0x10 = 0x40282010 Due to the limitation of mapped flash (max 8MBit) if you use a larger chip and do not have big flash support enabled the second rom in the default config will still be placed at 0x082000, not truly half-chip-size + 0x2000. Ideally you should also adjust the len to help detect over sized sections at link time, but more important is the overall size of the rom which you need to ensure fits in the space you have allocated for it in your flash layout plan.

Then simply compile and link as you would normally for OTA updates with the SDK boot loader, except using the linker scripts you’ve just prepared rather than the ones supplied with the SDK. Remember when building roms to create them as ‘new’ type roms (for use with SDK boot loader v1.2+). Or if using my esptool2 use the -boot2 option. Note: the test loads included with rBoot are built with -boot0 because they do not contain a .irom0.text section (and so the value of irom0_0_seg in the linker file is irrelevant to them) but ‘normal’ user apps always do.

irom checksum

The SDK boot loader checksum only covers sections loaded into ram (data and some code). Most of the SDK and user code remains on the flash and that is not included in the checksum. This means you could attempt to boot a corrupt rom and, because it looks ok to the boot loader, there will be no attempt to switch to a backup rom. rBoot improves on this by allowing the .irom0.text section to be included in the checksum. To enable this uncomment #define BOOT_IROM_CHKSUM in rboot.h and build your roms with esptool2 using the -iromchksum option.

Big flash support

This only needs to be enabled if you wish to be able to memory map more than the first 8MBit of the flash. Note you can still only map 8Mbit at a time. Use this if you want to have multiple 1MB roms, or more smaller roms than will fit in 8Mbits. If you have a large flash but only need, for example, two 512KB roms you do not need to enable this mode.

Support in rBoot is enabled by uncommenting the #define BOOT_BIG_FLASH in rboot.h.

Thinking about your linker files is either simpler or more complicated, depending on your usage of the flash. If you intend to use multiple 1MB roms you will only need one linker file and you only need to link once for OTA updates. Although when you perform an OTA update the rom will be written to a different position on the flash, each 8Mbit of flash is mapped (separately) to 0x40200000. So when any given rom is run the code will appear at the same place in memory regardless of where it is on the flash. Your base address for the linker would be 0x40202010. (Actually all but the first rom could base at 0x40200010 (because they don’t need to leave space for rBoot and config) but then you’re just making it more complicated again!)

If you wanted eight 512KB roms you would need two linker files - one for the first half of any given 8Mbits of flash and another for the second half. Just remember you are really laying out within a single 8MBit area, which can then be replicated multiple times on the flash.

Now the clever bit - rBoot needs to hijack the memory mapping code to select which 8Mbits gets mapped. There is no API for this, but we can override the SDK function. First we need to slightly modify the SDK library libmain.a, like so:

xtensa-lx106-elf-objcopy -W Cache_Read_Enable_New libmain.a libmain2.a

This produces a version of libmain with a ‘weakened’ Cache_Read_Enable_New function, which we can then override with our own. Modify your Makefile to link against the library main2 instead of main.

Next add rboot-bigflash.c (from the appcode directory) & rboot.h to your project - this adds the replacement Cache_Read_Enable_New to your code.

Getting gcc to apply the override correctly can be slightly tricky (I’m not sure why, it shouldn’t be). One option is to add -u Cache_Read_Enable_New to your LD_FLAGS and change the order of objects on the LD command so your objects/.a file is before the libraries. Another way that seems easier was to #include rboot-bigflash.c into the main .c file, rather than compiling it to a separate object file. I can’t make any sense of that, but I suggest you uncomment the message in the Cache_Read_Enable_New function when you first build with it, to make sure you are getting your version into the rom.

Now when rBoot starts your rom, the SDK code linked in it that normally performs the memory mapping will delegate part of that task to rBoot code (linked in your rom, not in rBoot itself) to choose which part of the flash to map.

Temporary boot option and rBoot<–>app communication
RBOOT_RTC_ENABLED

To enable communication between rBoot and your app you should enable the BOOT_RTC_ENABLED option in rboot.h. rBoot will then use the RTC data area to pass a structure with boot information which can be read by the app. This will allow the app to determine the boot mode (normal, temporary or GPIO) and the booted rom (even if it is a tempoary boot). Your app can also update this structure to communicate with rBoot when the device is next rebooted, e.g. to instruct it to temporarily boot a different rom to the one saved in the config. See the api documentation and/or the rBoot sample project for more details. Note: the message “don’t use rtc mem data”, commonly seen on startup, comes from the sdk and is not related to this rBoot feature.

Simple RPC

Sming wrapper for the Arduino simpleRPC library. This provides a way to export functions as remote procedure calls.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: simpleRPC
Simple RPC implementation for Arduino.
https://img.shields.io/github/last-commit/jfjlaros/simpleRPC.svg https://github.com/jfjlaros/simpleRPC/actions/workflows/arduino-package.yml/badge.svg https://readthedocs.org/projects/simplerpc/badge/?version=latest https://img.shields.io/github/release-date/jfjlaros/simpleRPC.svg https://img.shields.io/github/release/jfjlaros/simpleRPC.svg https://www.ardu-badge.com/badge/simpleRPC.svg https://img.shields.io/github/languages/code-size/jfjlaros/simpleRPC.svg https://img.shields.io/github/languages/count/jfjlaros/simpleRPC.svg https://img.shields.io/github/languages/top/jfjlaros/simpleRPC.svg https://img.shields.io/github/license/jfjlaros/simpleRPC.svg

This library provides a simple way to export Arduino functions as remote procedure calls. The exported method definitions are communicated to the host, which is then able to generate an API interface.

Features:

  • For each method, only one line of code is needed for exporting.

  • Automatic parameter- and return type inference.

  • Support for all native C types and strings.

  • Support for arbitrary functions and class methods.

  • Optional function and parameter naming and documentation.

  • Support for PROGMEM’s F() macro to reduce memory footprint.

  • Support for compound data structures like Tuples, Objects (Tuples with internal structure), Vectors and arbitrary combinations of these.

  • Support for reading multidimensional C arrays (e.g., int**).

  • Support for different types of I/O interfaces via plugins, e.g.,

    • Bluetooth.

    • Ethernet (untested).

    • Hardware serial.

    • RS485 serial.

    • Software serial (untested).

    • USB serial.

    • WiFi.

    • Wire (untested).

  • Support for using multiple interfaces at the same time.

The Arduino library is independent of any host implementation, a Python API client library is provided as a reference implementation.

Please see ReadTheDocs for the latest documentation.

Quick start

Export any function e.g., digitalRead() and digitalWrite() using the interface() function.

#include <simpleRPC.h>

void setup(void) {
  Serial.begin(9600);
}

void loop(void) {
  interface(Serial, digitalRead, "", digitalWrite, "");
}

These functions are now available on the host under names method0() and method1().

The documentation string can be used to name and describe the method.

interface(
  Serial,
  digitalRead,
    "digital_read: Read digital pin. @pin: Pin number. @return: Pin value.",
  digitalWrite,
    "digital_write: Write to a digital pin. @pin: Pin number. @value: Pin value.");

This is reflected on the host, where the methods are now named digital_read() and digital_write() and where the provided API documentation is also available. In the client reference implementation documentation, contains an example on how this works.

Further reading

Please read usage for more information about exporting normal functions, class member functions and documentation conventions.

If you want to create your own host library implementation for other programming languages, the section protocol should help you on your way.

SSL: Secure Sockets Layer

https://en.m.wikipedia.org/wiki/Transport_Layer_Security

Sming supports multiple SSL implementations, currently with adapters for:

If you want to use SSL then take a look at the Basic SSL sample for creating SSL clients, and HttpServer Config Network for SSL servers.

Configuration Variables
ENABLE_SSL
  • 0 (default): SSL requires lots of RAM and some intensive processing, so to conserve resources it is disabled by default.

  • 1: to enable the default SSL adapter. At the moment that is Axtls.

  • Axtls: to enable SSL support using the AXTLS 8266 component.

  • Bearssl: to enable SSL support using the Bear SSL component.

API Documentation
SSL: Upgrading
Introduction

Sming v4.1 introduced some major changes in the SSL architecture to support multiple adapters.

The default adapter is still based on axTLS, and it can be enabled in your application by providing the ENABLE_SSL directive either in your component.mk file or during compilation.

Migration

The access to the SSL connection from the TcpConnection is simplified and fetching information about SSL certificate and session id is easier than before.

The old code was looking like this:

SSL* ssl = connection.getSsl();
if(ssl) {
  const char* common_name = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME);
  if(common_name) {
    debugf("Common Name:\t\t\t%s\n", common_name);
  }
  displayCipher(ssl);
  displaySessionId(ssl);
}

Now it should be migrated to the following shorter version:

auto ssl = connection.getSsl();
if(ssl != nullptr) {
  ssl->printTo(Serial);
}

SSL initialisation in TCP clients or servers is done using an Ssl::Session::InitDelegate callback.

Old code looking like this:

MqttClient* getMqttClient()
{
  if(mqtt == nullptr) {
    mqtt = new MqttClient();
    mqtt->addSslOptions(SSL_SERVER_VERIFY_LATER); // << this is where we were setting SSL options
    Url url;

Has to be migrated to the following code:

void sslInit(Ssl::Session& session)
{
  session.options.verifyLater = true;
}

MqttClient* getMqttClient()
{
  if(mqtt == nullptr) {
    mqtt = new MqttClient();
    mqtt->setSslInitHandler(sslInit); // << this is where the sslInit callback is set
    Url url;

It is possible to create an SSL enabled server. The excerpt below demonstrates this and it is part of the HttpServer Config Network sample. Pay attention to the security considerations and limitations using this on a microcontroller with limited RAM:

void startWebServer()
{
#ifdef ENABLE_SSL
  server.setSslInitHandler([](Ssl::Session& session) {
    debug_i("SSL Init handler: setting server keyCert");
    session.keyCert.assign(serverKey, serverCert);
  });
  server.listen(443, true);
#else
  server.listen(80);
#endif
  server.paths.set("/", onIndex);
  server.paths.set("/ipconfig", onIpConfig);
  server.paths.set("/ajax/get-networks", onAjaxNetworkList);
  server.paths.set("/ajax/connect", onAjaxConnect);
  server.paths.setDefault(onFile);
}

Setting client certificates, ssl options and pinning for a HttpRequest is done using onSslInit callback. If you look at the Basic SSL sample you will see that the old way of setting them was as shown below:

HttpRequest* request = new HttpRequest(F("https://www.grc.com/fingerprints.htm"));
request->setSslOptions(SSL_SERVER_VERIFY_LATER);
request->pinCertificate(fingerprints);

The new one is using the following sequence of commands:

auto request = new HttpRequest(F("https://www.grc.com/fingerprints.htm"));
request->onSslInit(grcSslInit);

A sample callback is given below. In the callback the developer has access to the current SSL session and HTTP request and can modify them accordingly:

void grcSslInit(Ssl::Session& session, HttpRequest& request)
{
  static const Ssl::Fingerprint::Cert::Sha1 fingerprint PROGMEM = {  ... };

  session.validators.pin(fingerprint);

  // We're using validators, so don't attempt to validate full certificate
  session.options.verifyLater = true;

  // Go with maximum buffer sizes
  session.maxBufferSize = Ssl::MaxBufferSize::K16;
}

Note also that the Fingerprints class has been removed. Instead, we use methods of session.validators to:

  • Pin fingerprints;

  • Add one or more custom callback validators;

  • Implement custom validators by inheriting from Ssl::Validator.

Cryptographic support

Some basic class-based cryptographic support is provided via the Cryptographic Support library, organised within the Crypto namespace.

This is primarily for use with the SSL interface but does not require SSL to be enabled.

Alternatively, the cryptographic ‘C’ libraries themselves may be used directly by your application, regardless of which SSL adapter is in use, or even if SSL is disabled.

For example the following old code is using axTLS cryptographic functions:

char* loadPsk(int* keylen)
{
  SHA1_CTX sha_ctx;
  // ...
  SHA1_Init(&sha_ctx);
  SHA1_Update(&sha_ctx, (uint8_t*)buffer, strlen(buffer));
  SHA1_Final(digest, &sha_ctx);

For this code to work you should include the following header:

#include <axtls-8266/crypto/crypto.h>

And also make sure that your application component.mk file has the following line:

COMPONENT_DEPENDS += axtls-8266
SSL namespace

All SSL related classes and types are organized in a separate namespace called Ssl. For example you should use Ssl::KeyCertPair instead of SslKeyCertPair and Ssl::Fingerprints instead of SslFingerprints.

Comparison of SSL implementations
Memory usage

axTLS uses dynamic (heap) allocations which causes issues with large fragment sizes. If you run into memory problems, try setting ENABLE_CUSTOM_HEAP.

Bear SSL uses fixed buffers and does not suffer from this limitation.

{ todo }

Session
class Session

Handles all SSL activity for a TCP connection.

A session is created for every TCP connection where useSsl is specified. It is then passed to any registered session initialisation callbacks for customisation.

Public Functions

inline const SessionId *getSessionId() const

If available, return the current SSL Session ID.

Return values:

SessionId* – If connection hasn’t been established, may return Null

bool onAccept(TcpConnection *client, tcp_pcb *tcp)

Called when a client connection is made via server TCP socket.

Parameters:
  • client – The client TCP socket

  • tcp – The low-level TCP connection to use for reading and writing

Return values:

bool – true if the connection may proceed, false to abort

inline void setConnection(Connection *connection)

Called by TcpConnection to set the established SSL connection.

Parameters:

connection – The server connection

inline Connection *getConnection()

Get the currently active SSL connection object.

Return values:

Connection*

bool onConnect(tcp_pcb *tcp)

Handle connection event.

Parameters:

tcp

Return values:

bool – true on success, false to abort the connection

inline bool isConnected() const

Determine if an SSL connection has been fully established.

Return values:

boolConnection state

void close()

End the session.

SSL typically sends a closing handshake at this point

int read(InputBuffer &input, uint8_t *&output)

Read data from SSL connection.

Parameters:
  • input – Source encrypted data

  • output – Points to decrypted content

Return values:

int – Size of decrypted data returned, or negative on error

int write(const uint8_t *data, size_t length)

Write data to SSL connection.

Parameters:
  • data

  • length

Return values:

int – Quantity of bytes actually written, or tcp error code

bool validateCertificate()

Called by SSL adapter when certificate validation is required.

Note

SSL Internal method

Return values:

bool – true if validation is success, false to abort connection

void handshakeComplete(bool success)

Called by SSL adapter when handshake has been completed.

Note

SSL Internal method

Parameters:

success – Indicates if handshake was successful

size_t printTo(Print &p) const

For debugging.

Public Members

String hostName

Used for SNI https://en.wikipedia.org/wiki/Server_Name_Indication.

KeyCertPair keyCert

Required for server, optional for client.

Options options

Various connection options.

MaxBufferSize maxBufferSize = MaxBufferSize::Default

Controls SSL RAM usage.

const CipherSuites::Array *cipherSuites = &CipherSuites::basic

Configure supported cipher suites. Default is basic.

int cacheSize = 10

Set session caching.

Server: Number of cached client sessions. Suggested value: 10.

Client: Number of cached session ids. Suggested value: 1.

ValidatorList validators

List of certificate validators used by Client.

class SessionId

Manages buffer to store SSL Session ID.

Public Functions

inline String toString() const

Return a string representation of the session ID.

struct Options

Configurable options.

Public Members

bool sessionResume

Keep a note of session ID for later re-use.

bool verifyLater

Allow handshake to complete before verifying certificate.

enum class Ssl::MaxBufferSize

Indicate to SSL how much memory (approximately) to commit for buffers.

A remote SSL server may require data transfers in large (16K) fragments, so restricting buffer sizes may cause connections to such servers to fail.

This must be balanced against other requirements for RAM by the application, therefore this setting can be used to restrict RAM usage.

Note

The ordinal value of this enumeration corresponds to SSL fragment size as defined in Maximum Fragment Length Negotiation https://tools.ietf.org/html/rfc6066

Values:

enumerator Default

Let SSL implementation decide.

enumerator B512

512 bytes

enumerator K1

1024 bytes

enumerator K2
enumerator K4
enumerator K8
enumerator K16
Cipher Suites
enum class Ssl::CipherSuite : uint16_t

Cipher suite identifier.

The TLS standard specifies codes using two 8-bit values. We combine these into a single 16-bit value in MSB-LSB order.

For example:

TLS_RSA_WITH_AES_128_CBC_SHA = { 0x00, 0x2F } = 0x002F

See also

Refer to CipherSuite.h for defined values.

Values:

enumerator XX
namespace CipherSuites

Standard cipher suite options The actual suites are implementation-specific.

Standard cipher suites lists

DECLARE_CIPHER_SUITES(basic)

Supported by all adapters.

DECLARE_CIPHER_SUITES(full)

Adapter-specific.

Typedefs

using Array = FSTR::Array<Ssl::CipherSuite>
Certificates
class Certificate

Implemented by SSL adapter to handle certificate operations.

Public Types

enum class DN

Distinguished Name type.

Values:

enumerator ISSUER
enumerator SUBJECT
enum class RDN

Relative Distinguished Name type identifying a name component.

Values:

enumerator XX
enumerator MAX

Public Functions

virtual bool getFingerprint(Fingerprint::Type type, Fingerprint &fingerprint) const = 0

Obtain certificate fingerprint.

Parameters:
  • type – Which type of fingerprint to return

  • fingerprint – On success, returned fingerprint

Return values:

bool – true on success, false if fingerprint not available

virtual String getName(DN dn, RDN rdn) const = 0

Retrieve an X.509 distinguished name component.

Parameters:
  • dn – The desired Distinguished Name

  • rdn – The component to return

Return values:

String – The requested Distinguished Name component

size_t printTo(Print &p) const

Debugging print support.

class ValidatorList : public Vector<Validator>

Performs certificate validation.

Validators are created in the application’s session initialisation callback. When the certificate has been received, it is checked against each registered validator in turn until successful. All validators are destroyed during this process.

If there are no validators in the list then the certificate will not be checked and the connection accepted.

Public Functions

inline bool add(Validator *validator)

Add a validator to the list.

Parameters:

validator – Must be allocated on the heap

template<class T>
inline bool pin(const T &fingerprint)

Pin a fingerprint.

Creates and adds a fingerprint validator to the list

inline bool add(ValidatorCallback callback, void *data = nullptr)

Register a custom validator callback.

Parameters:
  • callback

  • data – User-provided data (optional)

bool validate(const Certificate *certificate)

Validate certificate via registered validators.

We only need one match for a successful result, but we free all the validators. This method must be called no more than ONCE.

Note

Called by SSL framework.

Parameters:

certificate – When called with nullptr will free all validators, then fail

Return values:

bool – true on success, false on failure

Public Members

Fingerprint::Types fingerprintTypes

Contains a list of registered fingerprint types.

Allows implementations to avoid calculating fingerprint values which are not required, as this is computationally expensive.

class Validator

Base validator class.

Validation is performed by invoking each validator in turn until a successful result is obtained.

Custom validators may either override this class, or use a callback.

Subclassed by Ssl::CallbackValidator, Ssl::FingerprintValidator< FP >

union Fingerprint
#include <Fingerprints.h>

Various types of fingerprint.

Applications should use the appropriate type to define a fingerprint, for example:

    static const Fingerprint::Cert::Sha1 fingerprint PROGMEM = { ... };

Public Types

enum class Type

SSL Certificate fingerprint type.

Values:

enumerator CertSha1

SHA1 Fingerprint of entire certificate.

enumerator CertSha256

SHA256 Fingerprint of entire certificate.

enumerator PkiSha256

SHA256 Fingerprint of Public Key Information.

Public Members

Cert cert
Pki pki
union Cert
#include <Fingerprints.h>

Fingerprints for the entire Certificate.

Public Members

Sha1 sha1
Sha256 sha256
struct Sha1
#include <Fingerprints.h>

Fingerprint based on the SHA1 value of the certificate.

The SHA1 hash of the entire certificate. This changes on each certificate renewal so needs to be updated every time the remote server updates its certificate.

Advantages: Takes less time to verify than SHA256

Disadvantages: Likely to change periodically

Public Members

Crypto::Sha1::Hash hash

Public Static Attributes

static constexpr Type type = Type::CertSha1
struct Sha256
#include <Fingerprints.h>

Fingerprint based on the SHA256 value of the certificate.

Typically displayed in browser certificate information

Public Members

Crypto::Sha256::Hash hash

Public Static Attributes

static constexpr Type type = Type::CertSha256
union Pki
#include <Fingerprints.h>

@Fingerprints for the Public Key only

Public Members

Sha256 sha256
struct Sha256
#include <Fingerprints.h>

Fingerprint based on the SHA256 value of the Public Key Subject in the certificate.

For HTTP public key pinning (RFC7469), the SHA-256 hash of the Subject Public Key Info (which usually only changes when the public key changes) is used.

Advantages: Doesn’t change frequently

Disadvantages: Takes more time (in ms) to verify.

Public Members

Crypto::Sha256::Hash hash

Public Static Attributes

static constexpr Type type = Type::PkiSha256
class Types
#include <Fingerprints.h>

Maintains a set of fingerprint types.

Public Functions

inline void add(Type type)
inline void remove(Type type)
inline bool contains(Type type) const

Private Members

uint32_t mask = 0
class KeyCertPair

Class to manage an SSL key certificate with optional password.

Unnamed Group

bool assign(const uint8_t *newKey, unsigned newKeyLength, const uint8_t *newCertificate, unsigned newCertificateLength, const char *newKeyPassword = nullptr)

Create certificate using provided values.

Note

We take a new copy of the certificate

Parameters:
  • newKey

  • newKeyLength

  • newCertificate

  • newCertificateLength

  • newKeyPassword

Return values:

bool – false on memory allocation failure

Public Functions

inline bool assign(const KeyCertPair &keyCert)

Assign another certificate to this structure.

Note

We take a new copy of the certificate

Parameters:

keyCert

Return values:

bool – false on memory allocation failure

using Ssl::ValidatorCallback = Delegate<bool(const Certificate *certificate, void *data)>

Validator callback function.

Note

Callback must ALWAYS release any allocated memory before returning. If called with certificate = NULL then just release memory and return false.

Param ssl:

Contains certificate to validate (may be NULL)

Param data:

Data for the callback to use

Retval bool:

true if validation succeeded

SSL Adapter API

These classes provide the interface between a Ssl::Session and an appropriate adapter.

Error codes

Error codes are implementation specific, however 0 always indicates success and < 0 for error.

To obtain a description for an error code, use Ssl::Connection::getErrorString().

SSL Alerts are reported via error codes. To obtain the alert code call Ssl::Connection::getAlert() which returns an Ssl::Alert code. If the error code is not an alert then Alert::INVALID is returned.

enum class Ssl::Alert

Alert codes defined by the standard.

Values:

enumerator Invalid

Not an alert code.

enumerator XX
Classes
class Factory

Implemented by SSL adapter.

Public Functions

virtual Context *createContext(Session &session) = 0

Create SSL context that can be used to create new client or server connections.

Return values:

Context* – The constructed context, shouldn’t fail (except on OOM)

class Context

Implemented by SSL adapter to create and manage SSL connections.

Public Functions

virtual bool init() = 0

Initializer method that must be called after object creation and before the creation of server or client connections.

Return values:

bool – true on success

virtual Connection *createClient(tcp_pcb *tcp) = 0

Creates client SSL connection. Your SSL client use this call to create a client connection to remote server.

Return values:

Connection*

virtual Connection *createServer(tcp_pcb *tcp) = 0

Creates server SSL connection. Your SSL servers use this call to allow remote clients to connect to them and use SSL.

Return values:

Connection*

class Connection : public Printable

Implemented by SSL adapter to handle a connection.

Returned int error codes are 0 for success, or < 0 for error.

The error codes themselves are implementation-specific. Use getErrorString() to obtain the message. SSL Alerts are also reported via error codes and can be obtained using a call to getAlert().

Public Functions

virtual bool isHandshakeDone() const = 0

Checks if the handshake has finished.

Return values:

bool – true on success

virtual int read(InputBuffer &input, uint8_t *&output) = 0

Reads encrypted information and decrypts it.

Parameters:
  • input – Source encrypted data

  • output – Pointer to decrypted plaintext buffer

Return values:

0 – : handshake is still in progress > 0 : there is decrypted data < 0 : error

virtual int write(const uint8_t *data, size_t length) = 0

Converts and sends plaintext data.

Parameters:
  • data

  • length

Return values:

int – length of the data that was actually written < 0 on error

virtual CipherSuite getCipherSuite() const = 0

Gets the cipher suite that was used.

Return values:

CipherSuite – IDs as defined by SSL/TLS standard

virtual SessionId getSessionId() const = 0

Gets the current session id object. Should be called after handshake.

Return values:

SessionId

virtual const Certificate *getCertificate() const = 0

Gets the certificate object. That object MUST be owned by the Connection implementation and should not be freed outside of it.

Return values:

Certificate* – Returns NULL if there is no certificate available

virtual size_t printTo(Print &p) const override

For debugging.

virtual String getErrorString(int error) const = 0

Get string for error code.

virtual Alert getAlert(int error) const = 0

Get alert code from error.

Parameters:

error

Return values:

Alert – Alert::INVALID if not an alert

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Terminal
Introduction

This Component provides make targets to support a serial terminal for communicating with devices.

The default serial terminal is miniterm. If you don’t have it installed already, you can install it via pip using the following command:

make python-requirements

(You’ll need python, of course.)

Options
COM_PORT

Default port for device communications. Default value depends on the development platform being used.

COM_SPEED_SERIAL

Baud rate to use. Default is COM_SPEED.

COM_OPTS

Additional options to pass to the terminal.

TERMINAL

Command line to use when running make terminal. Redefine if you want to use a different terminal application.

KILL_TERM

Command line to use to kill the running terminal process If the terminal never runs in the background and the warnings annoy you, just clear it.

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

uzlib

Deflate/Zlib-compatible LZ77 compression/decompression library

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: uzlib
uzlib - Deflate/Zlib-compatible LZ77 compression/decompression library

uzlib is a library which can decompress any valid Deflate, Zlib, and Gzip (further called just “Deflate”) bitstream, and compress data to Deflate- compatible bitstream, albeit with lower compression ratio than Zlib Deflate algorithm (very basic LZ77 compression algorithm is used instead, static Deflate Huffman tree encoding is used for bitstream).

uzlib aims for minimal code size and runtime memory requirements, and thus suitable for (deeply) embedded systems.

uzlib is based on:

  • tinf library by Joergen Ibsen (Deflate decompression)

  • Deflate Static Huffman tree routines by Simon Tatham

  • LZ77 compressor by Paul Sokolovsky

Library integrated and maintained by Paul Sokolovsky.

  1. 2014-2020 Paul Sokolovsky

uzlib library is licensed under Zlib license.

Decompressor features

Handling of input (compressed) stream:

  • Can reside (fully) in memory.

  • Can be received, byte by byte, from an application-defined callback function (which e.g. can read it from file or another I/O device).

  • Combination of the above: a chunk of input is buffered in memory, when buffer is exhausted, the application callback is called to refill it.

Handling of output (decompressed) stream:

  • In-memory decompression, where output stream fully resides in memory.

  • Streaming decompression, which allows to process arbitrary-sized streams (longer than available memory), but requires in-memory buffer for Deflate dictionary window.

  • Application specifies number of output bytes it wants to decompress, which can be as high as UINT_MAX to decompress everything into memory at once, or as low as 1 to decompress byte by byte, or any other value to decompress a chunk of that size.

Note that in regard to input stream handling, uzlib employs callback-based, “pull-style” design. The control flow looks as follows:

  1. Application requests uzlib to decompress given number of bytes.

  2. uzlib performs decompression.

  3. If more input is needed to decompress given number of bytes, uzlib calls back into application to provide more input bytes. (An implication of this is that uzlib will always return given number of output bytes, unless end of stream (or error) happens).

The original Zlib library instead features “push-style” design:

  1. An application prepares arbitrary number of input bytes in a buffer, and free space in output buffer, and calls Zlib with these buffers.

  2. Zlib tries to decode as much as possible input and produce as much as possible output. It returns back to the application if input buffer is exhausted, or output buffer is full, whatever happens first.

Currently, uzlib doesn’t support push-style operation a-la Zlib.

Compressor features

Compressor uses very basic implementation of LZ77 algorithm using hash table to find repeating substrings. The size of the hash table (on which compression efficiency depends), pointer to the hashtable memory, and the size of LZ77 dictionary should be configured in struct uzlib_comp.

Currently, compressor doesn’t support streaming operation, both input and output must reside in memory. Neither it supports incremental operation, entire input buffer is compressed at once with a single call to uzlib.

API and configuration

The API is defined in the file uzlib.h and should be largely self-describing. There are also examples implementing gzip-compatible compression and decompression applications in examples/ for further reference. (You may also refer to the original tinf README below for additional information, but it’s mostly provided for historical reference, and uzlib largely evolved beyond it).

There are some compile-time options for the library, defined in the file uzlib_conf.h. They can be altered directly in the file, or passed as the compiler options (-DXXX=YYY) when building library.

Binary sizes

To give an impression of code/data sizes of uzlib, the following figures are provided. Numbers for *.o files are code sizes of individual components (tinflate.o is decompressor, genlz77.o and defl_static.o - compressor), and TINF_DATA is the size of the corresponding data structure. These numbers are provided for different architectures, with default uzlib configuration, and with compilers/their options as specified.

gcc -m32 -Os
gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
2891 src/tinflate.o
381 src/genlz77.o
1685 src/defl_static.o
1284 TINF_DATA

arm-none-eabi-gcc -mthumb -mcpu=cortex-m4 -Os
arm-none-eabi-gcc (GNU Tools for Arm Embedded Processors 9-2019-q4-major) 9.2.1 20191025 (release) [ARM/arm-9-branch revision 277599]
1620 src/tinflate.o
180 src/genlz77.o
1131 src/defl_static.o
1284 TINF_DATA

Original tinf library README

For historical reference and to provide proper credit, the original ``tinf`` library README follows. NOTE: Many parts no longer apply to ``uzlib``. In particular, API is different, features supported are largely extended, and during decompression, there are checks to avoid erroneous/undefined behavior on incorrect Deflate bitstreams.

tinf - tiny inflate library

Version 1.00

Copyright (c) 2003 Joergen Ibsen

http://www.ibsensoftware.com/

About

tinf is a small library implementing the decompression algorithm for the deflate compressed data format (called ‘inflate’). Deflate compression is used in e.g. zlib, gzip, zip and png.

I wrote it because I needed a small in-memory zlib decompressor for a self- extracting archive, and the zlib library added 15k to my program. The tinf code added only 2k.

Naturally the size difference is insignificant in most cases. Also, the zlib library has many more features, is more secure, and mostly faster. But if you have a project that calls for a small and simple deflate decompressor, give it a try :-)

While the implementation should be fairly compliant, it does assume it is given valid compressed data, and that there is sufficient space for the decompressed data.

Simple wrappers for decompressing zlib streams and gzip’ed data in memory are supplied.

tgunzip, an example command-line gzip decompressor in C, is included.

The inflate algorithm and data format are from ‘DEFLATE Compressed Data Format Specification version 1.3’ (RFC 1951).

The zlib data format is from ‘ZLIB Compressed Data Format Specification version 3.3’ (RFC 1950).

The gzip data format is from ‘GZIP file format specification version 4.3’ (RFC 1952).

Ideas for future versions:

  • the fixed Huffman trees could be built by tinf_decode_trees() using a small table

  • memory for the TINF_DATA object should be passed, to avoid using more than 1k of stack space

  • wrappers for unpacking zip archives and png images

  • implement more in x86 assembler

  • more sanity checks

  • in tinf_uncompress, the (entry value of) destLen and sourceLen are not used

  • blocking of some sort, so everything does not have to be in memory

  • optional table-based huffman decoder

Functionality
void tinf_init();

Initialise the global uninitialised data used by the decompression code. This function must be called once before any calls to the decompression functions.

int tinf_uncompress(void *dest,
                    unsigned int *destLen,
                    const void *source,
                    unsigned int sourceLen);

Decompress data in deflate compressed format from source[] to dest[]. destLen is set to the length of the decompressed data. Returns TINF_OK on success, and TINF_DATA_ERROR on error.

int tinf_gzip_uncompress(void *dest,
                         unsigned int *destLen,
                         const void *source,
                         unsigned int sourceLen);

Decompress data in gzip compressed format from source[] to dest[]. destLen is set to the length of the decompressed data. Returns TINF_OK on success, and TINF_DATA_ERROR on error.

int tinf_zlib_uncompress(void *dest,
                         unsigned int *destLen,
                         const void *source,
                         unsigned int sourceLen);

Decompress data in zlib compressed format from source[] to dest[]. destLen is set to the length of the decompressed data. Returns TINF_OK on success, and TINF_DATA_ERROR on error.

unsigned int tinf_adler32(const void *data,
                          unsigned int length);

Computes the Adler-32 checksum of length bytes starting at data. Used by tinf_zlib_uncompress().

unsigned int tinf_crc32(const void *data,
                        unsigned int length);

Computes the CRC32 checksum of length bytes starting at data. Used by tinf_gzip_uncompress().

Source Code

The source code is ANSI C, and assumes that int is 32-bit. It has been tested on the x86 platform under Windows and Linux.

The decompression functions should be endian-neutral, and also reentrant and thread-safe (not tested).

In src/nasm there are 32-bit x86 assembler (386+) versions of some of the files.

Makefiles (GNU Make style) for a number of compilers are included.

Frequently Asked Questions

Q: Is it really free? Can I use it in my commercial ExpenZip software?

A: It’s open-source software, available under the zlib license (see

later), which means you can use it for free – even in commercial products. If you do, please be kind enough to add an acknowledgement.

Q: Did you just strip stuff from the zlib source to make it smaller?

A: No, tinf was written from scratch, using the RFC documentation of

the formats it supports.

Q: What do you mean by: ‘the zlib library .. is more secure’?

A: The zlib decompression code checks the compressed data for validity

while decompressing, so even on corrupted data it will not crash. The tinf code assumes it is given valid compressed data.

Q: I’m a Delphi programmer, can I use tinf?

A: Sure, the object files produced by both Borland C and Watcom C should

be linkable with Delphi.

Q: Will tinf work on UltraSTRANGE machines running WhackOS?

A: I have no idea .. please try it out and let me know!

Q: Why are all the makefiles in GNU Make style?

A: I’m used to GNU Make, and it has a number of features that are missing

in some of the other Make utilities.

Q: This is the first release, how can there be frequently asked questions?

A: Ok, ok .. I made the questions up ;-)

License

tinf - tiny inflate library

Copyright (c) 2003 Joergen Ibsen

This software is provided ‘as-is’, without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.

Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.

  2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.

  3. This notice may not be removed or altered from any source distribution.

ws_parser

ws_parser is a streaming parser for the WebSocket protocol (RFC 6455).

This library is loosely inspired by joyent/http_parser and shares many of the same attributes: it has no dependencies, makes no allocations or syscalls, and only requires 16 bytes of memory to maintain its parse state.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

References
Environment Variables
SoC support

Sming Esp32 Architecture

Support building Sming for the Esp32 architecture.

Build variables
IDF_PATH

This contains the base directory for the ESP-IDF toolchain used to build the framework. This variable is required and must be set accordingly.

SDK_CUSTOM_CONFIG

Custom SDK settings for a project can be defined in a separate file and setting this value to the location, relative to the project source root directory.

These will be added to the default SDK settings.

To make the settings current, you must run make sdk-config-clean. This will discard any changes made via make sdk-menuconfig.

Requirements

Sming requires a slightly modified version of the Espressif SDK. You can find the SDK here https://github.com/mikee47/esp-idf/tree/sming/release/v5.2. See idf_versions below for further details.

Using the Sming installation scripts are the recommended way to install these SDK versions. See Getting Started.

You can find further details in the ESP-IDF documentation.

Building

Make sure that the IDF_PATH is set. Also make sure that the other ESP-IDF environmental variables are set.

In Linux this can be done using the following command:

source $SMING_HOME/Tools/export.sh

Build the framework and application as usual, specifying SMING_ARCH =Esp32. For example:

cd $SMING_HOME/../samples/Basic_Serial
make SMING_ARCH=Esp32

This builds the application. Once built the application needs to be flashed on a real Esp32 microcontroller to run. Flashing can be done using the following command:

make flash
SDK

Sming comes with pre-compiled libraries and configuration files. If needed you can re-configure ESP-IDF using the command below:

make SMING_ARCH=Esp32 sdk-menuconfig

A re-compilation is required after the change of the configuration. This can be done with the following command:

make SMING_ARCH=Esp32 Sming-build all

If you want to revert to using the default SDK settings then issue the following command:

make SMING_ARCH=Esp32 sdk-config-clean

You can also configure per-project custom settings via SDK_CUSTOM_CONFIG.

SoC variants

Sming leverages the ESP IDF HAL to support multiple processor variants.

This is still at an early stage of development however basic applications should build for the following variants:

  • esp32 (default)

  • esp32s2

  • esp32c3

  • esp32s3

  • esp32c2

You can change variants like this:

` make SMING_SOC=esp32c3 `

Each variant uses a different build directory, e.g. out/Esp32/esp32c3/... to avoid conflicts.

See Esp32 Core Component for further details.

IDF versions

Sming currently supports IDF versions 4.3, 4.4, 5.0 and 5.2.

The default installed IDF version is 4.4. This can be changed as follows:

INSTALL_IDF_VER=5.2 $SMING_HOME/../Tools/install.sh esp32

The installation script creates a soft-link in /opt/esp-idf pointing to the last version installed. Use the IDF_PATH environment variable or change the soft-link to select which one to use.

After switching versions, run make clean components-clean before re-compiling.

Note

Currently, switching from version 4.x to 5.0 or vice-versa requires an additional step as they use different versions of the ‘pyparsing’ Python library.

If moving from IDF 4.x to 5.0: python -m pip install --upgrade pyparsing Moving from IDF 5.0 to 4.x: python -m pip install 'pyparsing<2.4'

Components
Sming (Esp32)

This Component builds a library containing architecture-specific code, and defines dependencies for Sming to build for the Esp32.

Interactive debugging on the device
ENABLE_GDB

In order to be able to debug live directly on the ESP8266 microcontroller you should re-compile your application with ENABLE_GDB=1 directive.

undefined (default)

Compile normally

1

Compile with debugging support provided by Esp8266 GDBSTUB for Sming. See also the Live Debug sample.

References
Used by
Environment Variables
  • TARGET_BIN

  • TARGET_OUT

SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

Esp32 Drivers

Provides low-level peripheral drivers.

GPIO: General-Purpose I/O

SDK definitions for GPIO.

enum GPIO_INT_TYPE

Values:

enumerator GPIO_PIN_INTR_DISABLE

Interrupt disabled for this pin

enumerator GPIO_PIN_INTR_POSEDGE

Interrupt occurs on positive edge

enumerator GPIO_PIN_INTR_NEGEDGE

Interrupt occurs on negative edge

enumerator GPIO_PIN_INTR_ANYEDGE

Interrupt occurs on both positive and negative edge

enumerator GPIO_PIN_INTR_LOLEVEL

Interrupt occurs when GPIO low

enumerator GPIO_PIN_INTR_HILEVEL

Interrupt occurs when GPIO high

enumerator GPIO_PIN_INTR_DISABLE
enumerator GPIO_PIN_INTR_POSEDGE
enumerator GPIO_PIN_INTR_NEGEDGE
enumerator GPIO_PIN_INTR_ANYEDGE
enumerator GPIO_PIN_INTR_LOLEVEL
enumerator GPIO_PIN_INTR_HILEVEL
hw_timer: Hardware Timers

Driver for hardware timers.

Variables
USE_US_TIMER

0 (default): Use default /256 prescale for Timer2 1: Use /16 prescale

The following functions depend on Timer2: - NOW() return value, the Timer2 tick count - Software timers - System time

Software timers are driven by Timer2, which by default uses a /256 prescale providing a resolution of 3.2us and a range of 1’ 54”.

Enabling this setting increases the resolution to 200ns but reduces the maximum software timer to 7” 9.5s.

API Documentation
enum hw_timer_clkdiv_t

Values:

enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enum hw_timer_intr_type_t

Values:

enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enum hw_timer_source_type_t

Values:

enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enum hw_timer_clkdiv_t

Values:

enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enum hw_timer_intr_type_t

Values:

enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enum hw_timer_source_type_t

Values:

enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enum hw_timer_clkdiv_t

Values:

enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enum hw_timer_intr_type_t

Values:

enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enum hw_timer_source_type_t

Values:

enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
typedef void (*hw_timer_callback_t)(void *arg)
typedef void (*hw_timer_callback_t)(void *arg)
typedef void (*hw_timer_callback_t)(void *arg)
struct hw_timer_private_t hw_timer_private
void hw_timer1_attach_interrupt(hw_timer_source_type_t source_type, hw_timer_callback_t callback, void *arg)

Attach an interrupt for the timer.

Parameters:
  • source_type – Ignored, uses APB clock source

  • callback – Callback function invoked via timer interrupt

  • arg – Passed to callback function

  • source_type

  • callback – Callback function invoked via timer interrupt

  • arg – Passed to callback function

inline void hw_timer1_enable(hw_timer_clkdiv_t div, hw_timer_intr_type_t intr_type, bool auto_load)

Enable the timer.

Note: With one-shot timer application callback must stop the timer when it is no longer required. This is to reduce callback latency. If this is not done, timer will trigger again when timer counter wraps around to 0. For /16 divisor this is only 1.7s.

Parameters:
  • div

  • intr_type – Ignored, always level-triggered

  • auto_load

  • div

  • intr_type

  • auto_load – true for repeating timer, false for one-shot

  • div

  • intr_type

  • auto_load

void hw_timer1_write(uint32_t ticks)

Set the timer interval.

Set the timer interval and arm.

Parameters:

ticks

void hw_timer1_disable(void)

Disable the timer.

void hw_timer1_detach_interrupt(void)

Detach interrupt from the timer.

uint32_t hw_timer1_read(void)

Get timer1 count.

Return values:

uint32_t – Current count value, counts from initial value down to 0

static uint32_t hw_timer2_read(void)

Read current timer2 value.

Return values:

uint32_t

void hw_timer_init(void)

Initialise hardware timers.

Note

Called by startup code

void hw_timer2_set_alarm(uint32_t ticks)

Set timer2 alarm count value.

Note

For internal use ONLY; used by software timers

Parameters:

ticks

uint32_t hw_timer_ticks()

Fetch 32-bit microsecond count.

All timers reference from a single 64-bit counter. We use only the lower 32 bits here as it provides lowest latency and compatibility with existing API.

HW_TIMER1_GROUP
HW_TIMER1_INDEX
MAX_HW_TIMER1_INTERVAL

Maximum timer interval in ticks.

Note

The corresponding time interval depends on the prescaler in use:

    /1 - 26.84s
    /16 - 429.50s
    /256 - 6871.95s
ESP32 supports a wide range of prescalers and uses 54-bit counter value. Limiting the range 31 bits avoids issues with overflows and moving to 64-bit calculations.

MIN_HW_TIMER1_INTERVAL_US

Minimum hardware interval in microseconds.

Note

Attempting to use repetitive interrupts below this level can lead to system instabliity and lockups, due to the software overhead in servicing the interrupts.

NOW()
MAX_HW_TIMER1_INTERVAL

Maximum timer interval in ticks.

Note

The corresponding time interval depends on the prescaler in use:

    /1 - 0.1048s
    /16 - 1.677s
    /256 - 26.84s

MIN_HW_TIMER1_INTERVAL_US

Minimum hardware interval in microseconds.

Note

Attempting to use repetitive interrupts below this level can lead to system instabliity and lockups, due to the software overhead in servicing the interrupts.

HW_TIMER2_CLKDIV
HW_TIMER2_CLK
MAX_HW_TIMER1_INTERVAL

Maximum timer interval in ticks.

MIN_HW_TIMER1_INTERVAL_US

Minimum hardware interval in microseconds.

Note

Attempting to use repetitive interrupts below this level can lead to system instabliity and lockups, due to the software overhead in servicing the interrupts.

HW_TIMER2_CLK
struct hw_timer_private_t
#include <hw_timer.h>
I2S: Inter-IC Serial communications
Introduction

I2S was designed for transfer of digital audio data.

The ESP8266 has two I2S modules (one transmitter and one receiver), both with hardware DMA support, which means transfers from RAM to the hardware SPI FIFO can be handled directly in hardware without any CPU involvement.

Sming I2S support

The Sming driver deals with the complicated of setting up the hardware, using an API similar to that in the Espressif RTOS SDK. In addition, DMA buffers may be accessed directly to avoid double-buffering and the associated RAM and copy overhead.

Applications
Audio

Playing MIDI files, MP3 files, speech synthesis, etc. is all possible using the ESP8266, though many audio applications require considerable processing power. That means you may need to disable WiFi and set the processor to run at full 160MHz speed.

High-quality multi-channel audio requires an external I2S DAC, which is what the protocol was designed for in the first place. You may find problems with insufficient RAM, but you can always add external SPI RAM.

More realistic uses include generating simple tones, beeps, playing pre-recorded WAV audio, etc. to supplement existing projects. This can all be done in the background without disrupting the system’s main purpose, whatever that may be.

For such applications you can generate single-channel audio via the I2S OUT pin, using Pulse-density modulation.

See the Tone Generator library for a demonstration of this.

GPIO Expansion

Expand GPIO using low-cost shift registers. https://github.com/lhartmann/esp8266_reprap.

Pixel-strip control

Devices such as WS2812-based NeoPixels use a simple, single-wire protocol. I2S is ideal for this as it can be used to generate a precisely-timed bitstream with very low CPU loading.

API Documentation

Warning

doxygengroup: Cannot find group “i2s_driver” in doxygen xml output for project “api” from directory: ../api/xml/

OS Timer
using smg_timer_func_t = void (*)(void *arg)
void smg_timer_arm_ticks(os_timer_t *ptimer, uint32_t ticks, bool repeat_flag)

Set a software timer using the Timer2 tick value.

This function has been added to Sming for more efficient and flexible use of software timers. It can be used alongside the SDK os_timer_arm_new() function.

Parameters:
  • ptimerTimer structure

  • ticks – Tick count duration for the timer

  • repeat_flag – true if timer will automatically repeat

void smg_timer_setfn(os_timer_t *ptimer, os_timer_func_t pfunction, void *parg)
void smg_timer_arm_us(os_timer_t *ptimer, uint32_t time_us, bool repeat_flag)
void smg_timer_arm(os_timer_t *ptimer, uint32_t time_ms, bool repeat_flag)
void smg_timer_disarm(os_timer_t *ptimer)
void smg_timer_done(os_timer_t *ptimer)
static inline uint64_t smg_timer_expire(const os_timer_t *ptimer)
void os_timer_arm_ticks(os_timer_t *ptimer, uint32_t ticks, bool repeat_flag)

Set a software timer using the Timer2 tick value.

This function has been added to Sming for more efficient and flexible use of software timers. It can be used alongside the SDK os_timer_arm_new() function.

Parameters:
  • ptimerTimer structure

  • ticks – Tick count duration for the timer

  • repeat_flag – true if timer will automatically repeat

static inline bool os_timer_is_armed(const os_timer_t *ptimer)
static inline uint64_t os_timer_expire(const os_timer_t *ptimer)
static inline void os_timer_done(os_timer_t *ptimer)
os_timer_func_t
os_timer_t
os_timer_arm
os_timer_arm_us
os_timer_disarm
os_timer_setfn
os_timer_arm_ticks
os_timer_expire
os_timer_done
struct smg_timer_t
#include <os_timer.h>
UART: Universal Asynchronous Receive/Transmit

Custom asynchronous driver.

Warning

doxygengroup: Cannot find group “uart_driver” in doxygen xml output for project “api” from directory: ../api/xml/

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

Esp32 Core Component
Introduction

Contains startup code, crash handling and additional Esp32-specific support code, including the ESP-IDF SDK.

If you want to tune ESP-IDF to your needs you should run:

make SMING_ARCH=Esp32 sdk-menuconfig

Followed by:

make
Configuration variables

The following variables may need to be changed if tools are installed in a different location, or if multiple versions are installed. By default, the most current version will be used.

ESP32_COMPILER_PATH

Location of xtensa compiler toolchain

ESP32_ULP_PATH

Location of ULP compiler toolchain

ESP32_OPENOCD_PATH

Location of ESP32 version of Open OCD JTAG debugging tools.

ESP32_PYTHON_PATH

Location of ESP-IDF python.

CREATE_EVENT_TASK

default: disabled

Warning

This setting is provided for debugging purposes ONLY.

Sming uses a custom event loop to ensure that timer and task callbacks are all executed in the same thread context.

Sometimes this behaviour can cause issues with IDF code. Setting this to 1 will create the event loop in a separate thread, which is standard IDF behaviour.

Background

An empty ESP IDF project is built which generates a set of libraries and headers which the framework can then be built against.

The project is located in project/{SMING_SOC}.

The code for this project is copied from sdk/project.

The default configuration settings are obtained from sdk/config and written to project/{SMING_SOC}/sdkconfig.defaults.

When sdk-menuconfig is run, the project/{SMING_SOC}/sdkconfig is modified. This can be reset using make sdk-config-clean.

If custom settings are required for a project then place these in a separate file and set SDK_CUSTOM_CONFIG to the location, relative to the project source root directory.

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

Esp32 WiFi

All related libraries for WiFi support.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

GDB Stub for Esp32

This defines the command line to use when make gdb is run.

Esp32 debugging is handled via JTAG interface.

No additional code is required as serial debugging is not (currently) implemented.

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

Heap

This Component implements heap-related housekeeping functions. Heap usage is tracked using malloc_count. This also provides some validation (using sentinels to detect if memory blocks are overwritten).

ENABLE_MALLOC_COUNT

We require malloc_count to keep track of heap usage for system_get_free_heap_size(). It does this by hooking the memory allocation routines (malloc, free, etc.). If you wish to disable this behaviour, set ENABLE_MALLOC_COUNT=0. If using tools such as Valgrind, this will provide a cleaner trace.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

Esp32 LIBC Component

This Component accommodates the differences in runtime libraries for the various supported toolchains.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

Esp8266 SPI Flash Support

Provides functions for access to flash memory.

API Documentation
enum SPIFlashMode

Values:

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_FAST_READ
enumerator MODE_SLOW_READ
enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enum SPIFlashSpeed

Values:

enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enum SPIFlashSize

Values:

enumerator SIZE_1MBIT
enumerator SIZE_2MBIT
enumerator SIZE_4MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT

Not listed.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

enum SPIFlashMode

Values:

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_FAST_READ
enumerator MODE_SLOW_READ
enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enum SPIFlashSpeed

Values:

enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enum SPIFlashSize

Values:

enumerator SIZE_1MBIT
enumerator SIZE_2MBIT
enumerator SIZE_4MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT

Not listed.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

enum SPIFlashMode

Values:

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_FAST_READ
enumerator MODE_SLOW_READ
enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enum SPIFlashSpeed

Values:

enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enum SPIFlashSize

Values:

enumerator SIZE_1MBIT
enumerator SIZE_2MBIT
enumerator SIZE_4MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT

Not listed.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

static inline uint32_t flashmem_get_address(const void *memptr)

Obtain the flash memory address for a memory pointer.

Note

If memptr is not in valid flash memory it will return an offset which exceeds the internal flash memory size.

Note

The flash location is dependent on where rBoot has mapped the firmware.

Parameters:

memptr

Return values:

uint32_t – Offset from start of flash memory

uint32_t flashmem_write(const void *from, uint32_t toaddr, uint32_t size)

Write a block of data to flash.

Note

None of the parameters need to be aligned

Parameters:
  • from – Buffer to obtain data from

  • toaddr – Flash location to start writing

  • size – Number of bytes to write

Return values:

uint32_t – Number of bytes written

uint32_t flashmem_read(void *to, uint32_t fromaddr, uint32_t size)

Read a block of data from flash.

Note

none of the parameters need to be aligned

Parameters:
  • to – Buffer to store data

  • fromaddr – Flash location to start reading

  • size – Number of bytes to read

Return values:

uint32_t – Number of bytes written

bool flashmem_erase_sector(uint32_t sector_id)

Erase a single flash sector.

Parameters:

sector_id – the sector to erase

Return values:

true – on success

SPIFlashInfo flashmem_get_info()

Get flash memory information block.

Return values:

SPIFlashInfo – Information block

uint8_t flashmem_get_size_type()

Returns a number indicating the size of flash memory chip.

Return values:

uint8_t – See SpiFlashInfo.size field for possible values

uint32_t flashmem_get_size_bytes()

get the total flash memory size

Return values:

uint32_t – Size in bytes

uint16_t flashmem_get_size_sectors()

Get the total number of flash sectors.

Return values:

uint16_t – Sector count

uint32_t flashmem_find_sector(uint32_t address, uint32_t *pstart, uint32_t *pend)

Helper function: find the flash sector in which an address resides.

Note

Optional parameters may be null

Parameters:
  • address

  • pstart – OUT/OPTIONAL: Start of sector containing the given address

  • pend – OUT/OPTIONAL: Last address in sector

Return values:

uint32_t – Sector number for the given address

uint32_t flashmem_get_sector_of_address(uint32_t addr)

Get sector number containing the given address.

Parameters:

addr

Return values:

uint32_t – sector number

uint32_t spi_flash_get_id(void)
uint32_t flashmem_write_internal(const void *from, uint32_t toaddr, uint32_t size)

write to flash memory

Note

All parameters MUST be aligned to 4-byte word boundaries, including the RAM pointer

Parameters:
  • from – Buffer to read data from - MUST be word-aligned

  • toaddr – Flash address (offset) to write to - MUST be word-aligned

  • size – Number of bytes to write - MUST be word-aligned

Return values:

uint32_t – Number of bytes actually written

uint32_t flashmem_read_internal(void *to, uint32_t fromaddr, uint32_t size)

Read from flash memory.

Note

All parameters MUST be aligned to 4-byte word boundaries, including the RAM pointer

Parameters:
  • to – Buffer to store data - MUST be word-aligned

  • fromaddr – Flash address (offset) to read from - MUST be word-aligned

  • size – Number of bytes to read - MUST be word-aligned

Return values:

uint32_t – Number of bytes actually read

uint32_t flashmem_get_first_free_block_address()
void flashmem_sfdp_read(uint32_t addr, void *buffer, size_t count)
INTERNAL_FLASH_WRITE_UNIT_SIZE

Flash memory access must be aligned and in multiples of 4-byte words.

INTERNAL_FLASH_READ_UNIT_SIZE
FLASH_TOTAL_SEC_COUNT
SYS_PARAM_SEC_COUNT

Number of flash sectors reserved for system parameters at start.

FLASH_WORK_SEC_COUNT
INTERNAL_FLASH_SECTOR_SIZE
INTERNAL_FLASH_SIZE
INTERNAL_FLASH_WRITE_UNIT_SIZE

Flash memory access must be aligned and in multiples of 4-byte words.

INTERNAL_FLASH_READ_UNIT_SIZE
FLASH_TOTAL_SEC_COUNT
SYS_PARAM_SEC_COUNT

Number of flash sectors reserved for system parameters at start.

FLASH_WORK_SEC_COUNT
INTERNAL_FLASH_SECTOR_SIZE
INTERNAL_FLASH_SIZE
INTERNAL_FLASH_START_ADDRESS
FLASH_TOTAL_SEC_COUNT
SYS_PARAM_SEC_COUNT

Number of flash sectors reserved for system parameters at start.

FLASH_WORK_SEC_COUNT
INTERNAL_FLASH_SECTOR_SIZE
INTERNAL_FLASH_SIZE
INTERNAL_FLASH_START_ADDRESS
struct SPIFlashInfo
#include <esp_spi_flash.h>

SPI Flash memory information block. Copied from bootloader header. See esp_image_header_t.

SPI Flash memory information block. Stored at the beginning of flash memory.

struct STORE_TYPEDEF_ATTR
#include <esp_spi_flash.h>

SPI Flash memory information block. Stored at the beginning of flash memory.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

Sming Esp8266 Architecture

Support building Sming for the Esp8266 architecture.

See also ESP Quick Toolchain.

Build variables
ESP_HOME

This contains the base directory for the toolchain used to build the framework.

Components
Sming (Esp8266)

This Component builds a library containing architecture-specific code, and defines dependencies for Sming to build for the Esp8266.

SDK 3.0+

Default: OFF. In order to use SDK 3.0.0 or newer please follow the instructions here Esp8266 Core Component.

No-WiFi build

Note

This is an EXPERIMENTAL feature. Not all hardware functions may be available.

If a project does not require WiFi (or networking) then setting the DISABLE_WIFI variable will reduce code size and RAM usage significantly. It does this using an un-official Esp8266 No WiFi Component.

Custom LWIP

LWIP (LightWeight IP) is a small independent implementation of the TCP/IP protocol suite used widely in embedded systems. Sming supports several versions of this, controlled by the ENABLE_CUSTOM_LWIP setting.

ENABLE_CUSTOM_LWIP
0

Use binary Esp8266 LWIP (Espressif) stack.

1 (default)

Use custom compiled Esp8266 Open LWIP (version 1) stack. Compared with the Espressif stack, this uses less RAM but consumes FLASH (program) memory. All espconn_* functions are turned off by default, so if you require these add the ENABLE_ESPCONN =1 directive. The Basic Smart Config example sets this in its component.mk file.

2

Use Esp8266 LWIP Version 2 stack. This does not have support for espconn_* functions.

ENABLE_LWIP_DEBUG

By default, some debug information will be printed for critical errors and situations. Set this to 1 to enable printing of all debug information.

Interactive debugging on the device
ENABLE_GDB

In order to be able to debug live directly on the ESP8266 microcontroller you should re-compile your application with ENABLE_GDB=1 directive.

undefined (default)

Compile normally

1

Compile with debugging support provided by Esp8266 GDBSTUB for Sming. See also the Live Debug sample.

References
Used by
Environment Variables
SoC support
  • esp8266

Esp8266 Drivers

Provides low-level peripheral drivers.

GPIO: General-Purpose I/O

SDK definitions for GPIO.

enum GPIO_INT_TYPE

Values:

enumerator GPIO_PIN_INTR_DISABLE

Interrupt disabled for this pin

enumerator GPIO_PIN_INTR_POSEDGE

Interrupt occurs on positive edge

enumerator GPIO_PIN_INTR_NEGEDGE

Interrupt occurs on negative edge

enumerator GPIO_PIN_INTR_ANYEDGE

Interrupt occurs on both positive and negative edge

enumerator GPIO_PIN_INTR_LOLEVEL

Interrupt occurs when GPIO low

enumerator GPIO_PIN_INTR_HILEVEL

Interrupt occurs when GPIO high

enumerator GPIO_PIN_INTR_DISABLE
enumerator GPIO_PIN_INTR_POSEDGE
enumerator GPIO_PIN_INTR_NEGEDGE
enumerator GPIO_PIN_INTR_ANYEDGE
enumerator GPIO_PIN_INTR_LOLEVEL
enumerator GPIO_PIN_INTR_HILEVEL
hw_timer: Hardware Timers

Driver for hardware timers.

Variables
USE_US_TIMER

0 (default): Use default /256 prescale for Timer2 1: Use /16 prescale

The following functions depend on Timer2: - NOW() return value, the Timer2 tick count - Software timers - System time

Software timers are driven by Timer2, which by default uses a /256 prescale providing a resolution of 3.2us and a range of 1’ 54”.

Enabling this setting increases the resolution to 200ns but reduces the maximum software timer to 7” 9.5s.

API Documentation
enum hw_timer_clkdiv_t

Values:

enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enum hw_timer_intr_type_t

Values:

enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enum hw_timer_source_type_t

Values:

enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enum hw_timer_clkdiv_t

Values:

enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enum hw_timer_intr_type_t

Values:

enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enum hw_timer_source_type_t

Values:

enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enum hw_timer_clkdiv_t

Values:

enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enum hw_timer_intr_type_t

Values:

enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enum hw_timer_source_type_t

Values:

enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
typedef void (*hw_timer_callback_t)(void *arg)
typedef void (*hw_timer_callback_t)(void *arg)
typedef void (*hw_timer_callback_t)(void *arg)
struct hw_timer_private_t hw_timer_private
void hw_timer1_attach_interrupt(hw_timer_source_type_t source_type, hw_timer_callback_t callback, void *arg)

Attach an interrupt for the timer.

Parameters:
  • source_type – Ignored, uses APB clock source

  • callback – Callback function invoked via timer interrupt

  • arg – Passed to callback function

  • source_type

  • callback – Callback function invoked via timer interrupt

  • arg – Passed to callback function

inline void hw_timer1_enable(hw_timer_clkdiv_t div, hw_timer_intr_type_t intr_type, bool auto_load)

Enable the timer.

Note: With one-shot timer application callback must stop the timer when it is no longer required. This is to reduce callback latency. If this is not done, timer will trigger again when timer counter wraps around to 0. For /16 divisor this is only 1.7s.

Parameters:
  • div

  • intr_type – Ignored, always level-triggered

  • auto_load

  • div

  • intr_type

  • auto_load – true for repeating timer, false for one-shot

  • div

  • intr_type

  • auto_load

void hw_timer1_write(uint32_t ticks)

Set the timer interval.

Set the timer interval and arm.

Parameters:

ticks

void hw_timer1_disable(void)

Disable the timer.

void hw_timer1_detach_interrupt(void)

Detach interrupt from the timer.

uint32_t hw_timer1_read(void)

Get timer1 count.

Return values:

uint32_t – Current count value, counts from initial value down to 0

static uint32_t hw_timer2_read(void)

Read current timer2 value.

Return values:

uint32_t

void hw_timer_init(void)

Initialise hardware timers.

Note

Called by startup code

void hw_timer2_set_alarm(uint32_t ticks)

Set timer2 alarm count value.

Note

For internal use ONLY; used by software timers

Parameters:

ticks

uint32_t hw_timer_ticks()

Fetch 32-bit microsecond count.

All timers reference from a single 64-bit counter. We use only the lower 32 bits here as it provides lowest latency and compatibility with existing API.

HW_TIMER1_GROUP
HW_TIMER1_INDEX
MAX_HW_TIMER1_INTERVAL

Maximum timer interval in ticks.

Note

The corresponding time interval depends on the prescaler in use:

    /1 - 26.84s
    /16 - 429.50s
    /256 - 6871.95s
ESP32 supports a wide range of prescalers and uses 54-bit counter value. Limiting the range 31 bits avoids issues with overflows and moving to 64-bit calculations.

MIN_HW_TIMER1_INTERVAL_US

Minimum hardware interval in microseconds.

Note

Attempting to use repetitive interrupts below this level can lead to system instabliity and lockups, due to the software overhead in servicing the interrupts.

NOW()
MAX_HW_TIMER1_INTERVAL

Maximum timer interval in ticks.

Note

The corresponding time interval depends on the prescaler in use:

    /1 - 0.1048s
    /16 - 1.677s
    /256 - 26.84s

MIN_HW_TIMER1_INTERVAL_US

Minimum hardware interval in microseconds.

Note

Attempting to use repetitive interrupts below this level can lead to system instabliity and lockups, due to the software overhead in servicing the interrupts.

HW_TIMER2_CLKDIV
HW_TIMER2_CLK
MAX_HW_TIMER1_INTERVAL

Maximum timer interval in ticks.

MIN_HW_TIMER1_INTERVAL_US

Minimum hardware interval in microseconds.

Note

Attempting to use repetitive interrupts below this level can lead to system instabliity and lockups, due to the software overhead in servicing the interrupts.

HW_TIMER2_CLK
struct hw_timer_private_t
#include <hw_timer.h>
I2S: Inter-IC Serial communications
Introduction

I2S was designed for transfer of digital audio data.

The ESP8266 has two I2S modules (one transmitter and one receiver), both with hardware DMA support, which means transfers from RAM to the hardware SPI FIFO can be handled directly in hardware without any CPU involvement.

Sming I2S support

The Sming driver deals with the complicated of setting up the hardware, using an API similar to that in the Espressif RTOS SDK. In addition, DMA buffers may be accessed directly to avoid double-buffering and the associated RAM and copy overhead.

Applications
Audio

Playing MIDI files, MP3 files, speech synthesis, etc. is all possible using the ESP8266, though many audio applications require considerable processing power. That means you may need to disable WiFi and set the processor to run at full 160MHz speed.

High-quality multi-channel audio requires an external I2S DAC, which is what the protocol was designed for in the first place. You may find problems with insufficient RAM, but you can always add external SPI RAM.

More realistic uses include generating simple tones, beeps, playing pre-recorded WAV audio, etc. to supplement existing projects. This can all be done in the background without disrupting the system’s main purpose, whatever that may be.

For such applications you can generate single-channel audio via the I2S OUT pin, using Pulse-density modulation.

See the Tone Generator library for a demonstration of this.

GPIO Expansion

Expand GPIO using low-cost shift registers. https://github.com/lhartmann/esp8266_reprap.

Pixel-strip control

Devices such as WS2812-based NeoPixels use a simple, single-wire protocol. I2S is ideal for this as it can be used to generate a precisely-timed bitstream with very low CPU loading.

API Documentation

Warning

doxygengroup: Cannot find group “i2s_driver” in doxygen xml output for project “api” from directory: ../api/xml/

OS Timer
using smg_timer_func_t = void (*)(void *arg)
void smg_timer_arm_ticks(os_timer_t *ptimer, uint32_t ticks, bool repeat_flag)

Set a software timer using the Timer2 tick value.

This function has been added to Sming for more efficient and flexible use of software timers. It can be used alongside the SDK os_timer_arm_new() function.

Parameters:
  • ptimerTimer structure

  • ticks – Tick count duration for the timer

  • repeat_flag – true if timer will automatically repeat

void smg_timer_setfn(os_timer_t *ptimer, os_timer_func_t pfunction, void *parg)
void smg_timer_arm_us(os_timer_t *ptimer, uint32_t time_us, bool repeat_flag)
void smg_timer_arm(os_timer_t *ptimer, uint32_t time_ms, bool repeat_flag)
void smg_timer_disarm(os_timer_t *ptimer)
void smg_timer_done(os_timer_t *ptimer)
static inline uint64_t smg_timer_expire(const os_timer_t *ptimer)
void os_timer_arm_ticks(os_timer_t *ptimer, uint32_t ticks, bool repeat_flag)

Set a software timer using the Timer2 tick value.

This function has been added to Sming for more efficient and flexible use of software timers. It can be used alongside the SDK os_timer_arm_new() function.

Parameters:
  • ptimerTimer structure

  • ticks – Tick count duration for the timer

  • repeat_flag – true if timer will automatically repeat

static inline bool os_timer_is_armed(const os_timer_t *ptimer)
static inline uint64_t os_timer_expire(const os_timer_t *ptimer)
static inline void os_timer_done(os_timer_t *ptimer)
os_timer_func_t
os_timer_t
os_timer_arm
os_timer_arm_us
os_timer_disarm
os_timer_setfn
os_timer_arm_ticks
os_timer_expire
os_timer_done
struct smg_timer_t
#include <os_timer.h>
PWM: Pulse-Width Modulation

The driver interface is defined in the ESP8266 SDK.

Build variables
ENABLE_CUSTOM_PWM
undefined

use the Espressif PWM driver

1 (default)

Use the New PWM driver, a drop-in replacement for the version provided in the Espressif SDK.

API Documentation
void pwm_init(uint32_t period, uint32_t *duty, uint32_t pwm_channel_num, uint32_t (*pin_info_list)[3])

Initialize PWM function, including GPIO selection, period and duty cycle.

Example:

    uint32 ioInfo[][3] = {
        {PWM_0_OUT_IO_MUX, PWM_0_OUT_IO_FUNC, PWM_0_OUT_IO_NUM},
        {PWM_1_OUT_IO_MUX, PWM_1_OUT_IO_FUNC, PWM_1_OUT_IO_NUM},
        {PWM_2_OUT_IO_MUX, PWM_2_OUT_IO_FUNC, PWM_2_OUT_IO_NUM}
};

pwm_init(light_param.pwm_period, light_param.pwm_duty, 3, ioInfo);

Example:

    uint32 io_info[][3] = {
        {PWM_0_OUT_IO_MUX, PWM_0_OUT_IO_FUNC, PWM_0_OUT_IO_NUM},
        {PWM_1_OUT_IO_MUX, PWM_1_OUT_IO_FUNC, PWM_1_OUT_IO_NUM},
        {PWM_2_OUT_IO_MUX, PWM_2_OUT_IO_FUNC, PWM_2_OUT_IO_NUM}
};

pwm_init(light_param.pwm_period, light_param.pwm_duty, 3, io_info);

Note

This API can be called only once.

Note

This API can be called only once.

Parameters:
  • period – PWM period

  • duty – duty cycle of each output

  • pwm_channel_num – PWM channel number

  • pin_info_list – Array containing an entry for each channel giving

  • period – PWM period

  • duty – duty cycle of each output

  • pwm_channel_num – PWM channel number

  • pin_info_list – Array containing an entry for each channel giving

void pwm_start(void)

Starts PWM.

This function needs to be called after PWM configuration is changed.

void pwm_set_duty(uint32_t duty, uint8_t channel)

Sets duty cycle of a PWM output.

Set the time that high-level signal will last. The range of duty depends on PWM period. Its maximum value of which can be Period * 1000 / 45.

For example, for 1-KHz PWM, the duty range is 0 ~ 22222.

Parameters:
  • duty – The time that high-level single will last, duty cycle will be (duty*45)/(period*1000)

  • channel – PWM channel, which depends on how many PWM channels are used

uint32_t pwm_get_duty(uint8_t channel)

Get duty cycle of PWM output.

Duty cycle will be (duty*45) / (period*1000).

Parameters:

channel – PWM channel, which depends on how many PWM channels are used

Return values:

uint32 – Duty cycle of PWM output

void pwm_set_period(uint32_t period)

Set PWM period.

Parameters:

period – PWM period in us. For example, 1-KHz PWM period = 1000us.

uint32_t pwm_get_period(void)

Get PWM period.

Return values:

uint32 – Return PWM period in us.

uint32_t get_pwm_version(void)

Get version information of PWM.

Return values:

uint32 – PWM version

UART: Universal Asynchronous Receive/Transmit

Custom asynchronous driver.

Warning

doxygengroup: Cannot find group “uart_driver” in doxygen xml output for project “api” from directory: ../api/xml/

References
Used by
Environment Variables
SoC support
  • esp8266

Esp8266 LWIP (Espressif)

The Espressif SDK provides a default version of LWIP, enabled by using this Component.

See ENABLE_CUSTOM_LWIP.

References
SoC support
  • esp8266

Esp8266 Open LWIP (version 1)

This Component provides the default Sming LWIP stack, which uses less RAM than the Espressif version.

See ENABLE_CUSTOM_LWIP.

ENABLE_LWIPDEBUG
0 (default)

Standard build

1

Enable debugging output

You can increase debugging for certain areas by modifying debug options in esp-open-lwip/include/lwipopts.h.

ENABLE_ESPCONN

The Espressif SDK defines a network API consisting of functions which start with espconn_.

0 (default)

Disabled

1

Enable espconn_ functions

References
Environment Variables
SoC support
  • esp8266

Submodule: esp-open-lwip
Esp8266 Core Component

Contains startup code, crash handling and additional Esp8266-specific support code.

Sming uses libraries from the ESP8266 NON-OS SDK version 3, imported as a submodule. The header and linker files are provided by this Component.

References
Used by
Environment Variables
  • FLASH_INIT_DATA

  • FLASH_INIT_DATA_VCC

SoC support
  • esp8266

Submodule: sdk
Support Policy for ESP8266 NonOS

Starting from December 2019,

  • We will not add any new features to the ESP8266 NonOS SDK.

  • We will only fix critical bugs in the ESP8266 NonOS SDK.

  • We will only maintain the master branch of ESP8266 NonOS SDK, which is a continuously bug-fix version based on v3.0. This means:

    • All other released branches will not be updated.

    • All the future versions will be released from only the master branch mentioned above.

  • It is suggested that the ESP8266_RTOS_SDK, instead of ESP8266 NonOS SDK, be used for your projects.

The latest ESP8266_RTOS_SDK allows users to develop applications using an architecture that are compatible with the SDKs of all Espressif chips, including ESP8266 series, ESP32 series, and the upcoming new series of chips. Switching to ESP8266_RTOS_SDK will helps users to:

  • Eliminate the necessity to maintain more than one applications (for different chips), thus greatly reducing maintenance costs.

  • Easily switch to other Espressif chips in the future for enhanced flexibility, less dependency, and reduced time-to-market.

Thank you for your interest in Espressif products.

ESP8266 NonOS 支持政策

自 2019 年 12 月起,我们将:

  • 停止为 ESP8266 NonOS 新增任何功能。

  • 仅修复 ESP8266 NonOS 的关键 bug。

  • 所有更新仅在 master 分支进行,即基于 v3.0.0 的持续 bug 修复版本。这意味着:

    • 其他任何 release 分支均不再提供维护;

    • 所有更新均将通过上述 master 分支发布。

  • 建议客户使用新版 ESP8266_RTOS_SDK

简单来说,新版 ESP8266_RTOS_SDK 可帮助客户避免对单一 SDK 的依赖,允许客户应用程序同时兼容多款乐鑫芯片,包括 ESP8266 系列、ESP32 系列以及未来发布的新产品。使用 ESP8266_RTOS_SDK 允许客户:

  • 避免同时维护针对不同芯片的多套应用程序,从而降低维护成本。

  • 未来可轻松切换至其他乐鑫芯片,从而提高灵活性、降低对单一芯片的依赖,并缩短上市时间。

感谢大家对乐鑫的支持与关注。

ESP8266_NONOS_SDK

All documentations @ http://espressif.com/en/support/download/documents?keys=&field_type_tid%5B%5D=14

Notes

Please add user_pre_init() in your project, which will be called before user_init(). And you MUST call system_partition_table_regist() in user_pre_init to register your project partition table.

The following partition address CAN NOT be modified, and you MUST give the correct address. They are retated to the flash map, please refer to ESP8266 SDK Getting Started Guide or ESP8266 SDK 入门指南.

  • SYSTEM_PARTITION_BOOTLOADER

  • SYSTEM_PARTITION_OTA_1

  • SYSTEM_PARTITION_OTA_2

  • SYSTEM_PARTITION_SYSTEM_PARAMETER

If you donot use Non-FOTA bin, eagle.irom0.text.bin and irom0.text MUST be downloaded the fixed address, which also can be found in ESP8266 SDK Getting Started Guide or ESP8266 SDK 入门指南, and you can define their partition type after SYSTEM_PARTITION_CUSTOMER_BEGIN.

Esp8266 No WiFi
Background

Based on SDK 3.0.1 disassemblies with reference and thanks to:

The problem we have is to disentangle the wifi functions from the SDK within the app_main.o and user_interface.o modules from libmain.a. In particular, the user_start() code which mixes up wifi stuff with system code so it cannot be easily separated.

The SDKnoWiFi implements the startup code and some system functions but contains a lot of stuff which is provided by the SDK and in other parts of the Sming framework. We need to provide replacement functions to interoperate correctly with the remaining SDK code.

Advantages
  • Reduces code image size when networking/WiFi is not required

  • Eliminates need for SDK-specific partitions (rf_cal, phy_init, sys_param)

Process

This is the approach used to create this Component.

  1. Remove user_interface from libmain:

    ar d libmain.a user_interface.o
    
  2. Implement call_user_start_local. The actual entry point is call_user_start, in vector.o, which is required. (Note: This symbol was -u in the linker script but this is un-necessary and has been removed.)

  3. Build and implement missing functions.

Where functions can be passed directly to the ROM code (e.g. system_os_task -> ets_task) these are defined in the no.wifi.ld linker script.

Other code is located in a .c file named according to the module it replaces in libmain.a.

There are various options for integrating this into Sming, but I’ve decided the most useful way is to have it as a link-time option. We either link esp_wifi or esp_no_wifi as required, no rebuild to the framework is necessary. This is controlled by the DISABLE_WIFI setting.

This means that if there are any WiFi routines used in the project the link step will fail.

An alternate approach is to separate out the Network/WiFi stuff in the framework into separate Components which are themselves only included. However, some of the network code could be useful in a non-networked application. There may even be an alternate physical network layer implemented (e.g. Ethernet over SPI) so it’s simplest to just leave the framework intact.

Library Disassembly

You can create a disassembly of the relevant SDK libraries for inspection by running this command from a project directory:

make sdk-disassemble

If you want to disassemble other SDK libraries, do this:

make sdk-disassemble SDK_LIBLIST="crypto net80211"
Known issues

Further work is required to implement the following (list incomplete):

  • Sleep/power saving modes

  • Partition tables

  • Analogue reading

References
SoC support
  • esp8266

Esp8266 WiFi

All related libraries for WiFi support. Definitions are provided in the Espressif Non-OS SDK.

References
Used by
SoC support
  • esp8266

Esp8266 GDBSTUB for Sming
Background

This is a rewrite of gdbstub based on the esp8266 Arduino project.

To use the GNU Debugger (GDB) with Sming requires your application to include some code (gdbstub) which communicates via the serial port. On the ESP8266 only UART0 may be used for this as UART1 is transmit-only.

The gdbstub code will only be built if you specify ENABLE_GDB =1 when compiling your application. At startup, before your init() function is called, it will claim UART0 so your application will be unable to use it directly. Therefore, the default port for Serial is changed to UART2.

UART2 is a ‘virtual’ serial port to enable serial communications to work correctly when GDB-enabled. Read/write calls and serial callbacks are handled via gdbstub. Baud rate changes affect UART0 directly.

Note that target refers to the application being debugged, and host the development system running the GDB program.

Refer to the official GDB documentation for further details.

GDB

This is the application which runs on your development system and talks to gdbstub.

  • Linux: A version of this should be available in $ESP_HOME/xtensa-lx106-elf/bin/xtensa-lx106-elf-gdb

  • Windows: At time of writing, UDK doesn’t provide a GDB application - Download and run the executable installer at SysProgs

    • Copy the C:\SysGCC\esp8266\opt\xtensa-lx106-elf\bin\xtensa-lx106-elf-gdb.exe to a suitable location.

  • Mac: ?

Usage
  • Configure gdbstub by editing gdbstub-cfg.h as required. You can also configure the options by setting :USER_CFLAGS in your project’s component.mk file. e.g USER_CFLAGS=-DGDBSTUB_BREAK_ON_INIT=0.

  • Optional: Add gdb_do_break() statements to your application.

  • Run make clean, then make ENABLE_GDB=1 flash to build and flash the application with debugging enabled

  • Run gdb, depending on your configuration immediately after resetting the board or after it has run into an exception. The easiest way to do it is to use the provided script: make gdb.

To run manually, see the following variables which you can inspect using make list-config.

GDB_CMDLINE

Command line used to run GDB.

GDBSTUB_DIR

Location of the GDB stub component, and the gdbcmds file.

COM_PORT_GDB

Defaults to COM_PORT, but if necessary you can change it to a different value.

COM_SPEED_GDB

Same as COM_SPEED_SERIAL, which is the value compiled into the gdbstub code.

GDB_UART_SWAP

If you need to use alternate serial pins, enable this option GDB_UART_SWAP=1

GDB

Path to the GDB executable being used.

Useful GDB commands

c Continue execution

q Quit and detach

where Display current stopped location

bt Show stack backtrace

disass Disassemble, disass/m to mix with source code

print expr Display a variable or other value

print func() Call a function, display result, or call func() to discard result

tui enable Provides a windowed interface within the console (only seems to work in Linux)

x/32xw $sp Display contents of stack

info reg Display register values

info break Display details of currently set breakpoints

delete Delete all breakpoints

br Set a breakpoint at the given address or function name

hbr Set a hardware breakpoint

watch Set a hardware watchpoint to detect when the value of a variable changes

These commands require GDBSTUB_ENABLE_HOSTIO to be enabled:

remote get targetfile hostfile Read a file from SPIFFS (on the target)

remote put hostfile targetfile Write a file to SPIFFS

remote delete targetfile Delete a file from SPIFFS

Eclipse

Windows:

  • Ensure Use external console for inferior is checked.

  • In connection settings, specify COM port like with leading /, e.g. /COM4

Problems connecting?

  • Switch to the debug perspective before connecting

  • Ensure serial baud rate matches your application

  • Remove or disable all breakpoints before attaching. Eclipse will attempt to set these on connection, and if any are invalid it will hang and timeout.

  • Check connectivity using command-line GDB

GDB System Calls

Applications may interact with GDB directly using system calls, for example reading input from the GDB command prompt. See the Live Debug sample for a demonstration.

Note that system calls are disabled in the default configuration, so set GDBSTUB_ENABLE_SYSCALL =1 to use this feature with your application.

Known Issues and Limitations
  • Unable to set requested break/watch points
    • Cause: Due to hardware limitations, only one hardware breakpount and one hardware watchpoint are available

    • Solution: None (hardware limitation)

  • System crashes if debugger is paused for too long
    • Cause: The WiFi hardware is designed to be serviced by software periodically. It has some buffers so it will behave OK when some data comes in while the processor is busy, but these buffers are not infinite. If the WiFi hardware receives lots of data while the debugger has stopped the CPU, it is bound to crash. This will happen mostly when working with UDP and/or ICMP; TCP-connections in general will not send much more data when the other side doesn’t send any ACKs.

    • Solution: In such situations avoid pausing the debugger for extended periods

  • Software breakpoints/watchpoints (‘break’ and ‘watch’) don’t work on flash code
    • Cause: GDB handles these by replacing code with a debugging instruction, therefore the code must be in RAM.

    • Solution: Use hardware breakpoint (‘hbreak’) or use GDB_IRAM_ATTR for code which requires testing

  • If hardware breakpoint is set, single-stepping won’t work unless code is in RAM.
    • Cause: GDB reverts to software breakpoints if no hardware breakpoints are available

    • Solution: Delete hardware breakpoint before single-stepping

  • Crash occurs when setting breakpoint in HardwareTimer callback routine
    • Cause: By default, HardwareTimer uses Non-maskable Interrupts (NMI) which keep running when the debugger is paused

    • Solution: Use the timer in non-maskable mode, or enable GDBSTUB_PAUSE_HARDWARE_TIMER option

  • If gdbstub isn’t initialised then UART2 won’t work, though initialisation will succeed
    • Cause: By design, uart callbacks can be registered for UART2 at any time, before or after initialisation

    • Solution: Not really an issue, just something to be aware of

  • Error reported, “packet reply is too long”
    • Cause: Mismatch between GDB version and stub code

    • Solution: Set GDBSTUB_GDB_PATCHED =1 or use an unpatched version of GDB

  • Whilst GDB is attached, input cannot be passed to application
    • Cause: GDB buffers keystrokes and replays them only when the target is interrupted (e.g. via ctrl+C), rather than passing them via serial connection.

    • Solution: Application may use gdb_syscall interface to communicate with debugger. See $(SMING_HOME)/system/gdb_syscall.h, and Live Debug sample.

  • No apparent way to have second ‘console’ (windows terminology) separate from GDB interface
    • Cause: Unknown

    • Solution: Is this possible with remote targets?

  • GDB (in Windows) doesn’t respond at all to Ctrl+C
    • Cause: Unknown

    • Solution: Press Ctrl+Break to ‘hard kill’ GDB. You’ll probably need to do the next step as well to get it back

  • When GDB is running under windows, appears to hang when target reset or restarted
    • Cause: Unknown, may not happen on all devboards but presents with NodeMCU

    • Solution
      • quit GDB quit

      • Start terminal make terminal

      • reset board

      • quit terminal

      • run GDB again make gdb

  • Debug messages don’t appear in Eclipse
    • Cause: Unknown

    • Solution: Use command-line GDB, or a better visual debugger

Configuration

Defines

ENABLE_EXCEPTION_DUMP

When enabled, an exception or crash dumps a stack trace to debug output Default is ON for debug builds, OFF for release builds

Note: Not dependent upon ENABLE_GDB

ENABLE_CRASH_DUMP

When enabled, an unexpected reset (i.e. system crash) dumps a stack trace to debug output Default is ON for debug builds, OFF for release builds

Note: Not dependent upon ENABLE_GDB

GDBSTUB_ENABLE_DEBUG

When defined, GDB communications are echoed to UART1 for testing GDB stub operation.

0: No debug output 1: Show decoded commands and responses 2: Show packet content 3: Show debug output for internal routines

GDBSTUB_GDB_PATCHED

Espressif provide a patched version of GDB which emits only those registered present in the lx106. Set to 0 if an unpatched version of GDB is used.

GDBSTUB_USE_OWN_STACK

Enable this to make the exception and debugging handlers switch to a private stack. This will use up 1K of RAM, but may be useful if you’re debugging stack or stack pointer corruption problems. It’s normally disabled because not many situations need it. If for some reason the GDB communication stops when you run into an error in your code, try enabling this.

GDBSTUB_STACK_SIZE
GDBSTUB_BREAK_ON_EXCEPTION

Enable this to cause the program to pause and wait for gdb to be connected when an exception is encountered.

GDBSTUB_BREAK_ON_RESTART

Enable this to cause the program to pause and wait for gdb to be connected when an unexpected system restart occurs.

GDBSTUB_CTRLC_BREAK

If this is defined, gdbstub will break the program when you press Ctrl-C in gdb. It does this by monitoring for the ‘x03’ character in the serial receive routine. Any preceding characters are passed through to the application via UART2. If your application uses the serial port for terminal (text) communications you should be OK, but binary transfers are likely to cause problems and this option should probably be disabled. Instead, use GDBSTUB_BREAK_ON_INIT, or call gdb_do_break() in your application.

Specify: 0 to disable Ctrl+C break checking completely 1 to allow Ctrl+C break only when debugger is attached 2 to allow Ctrl+C break at any time. Ensure you have set remote interrupt-on-connect on in GDB command file, so it will send a Ctrl+C sequence when attempting to connect

GDBSTUB_ENABLE_UART2

The GDB stub has exclusive access to UART0, so applications cannot use it directly and attempts to open it will fail.

If this option is enabled, the default serial port will be changed to UART2 to allow debug output (from m_printf, debug_*, os_printf, etc.) to show up in your GDB session.

Outside of GDB terminal applications should work as normal, with the following caveats:

    If GDBSTUB_BREAK_ON_INIT is defined, then at startup your application will display `$T05#b9` and stop.
    A similar thing will happen if GDBSTUB_CTRLC_BREAK=2 and you type Ctrl+C.
    Continue by typing `$D#44` (without the quotes), or exit the terminal and start GDB.
See GDB remote serial protocol for further details.

Disabling this option releases some IRAM. You may be instead able to use UART1 for debug output, adding Serial.setPort(UART_ID_1); in your application’s init() function.

GDBSTUB_ENABLE_SYSCALL

Enable gdb_syscall_* functions for use by application. If undefined, calls will do nothing and return -1.

GDBSTUB_ENABLE_HOSTIO

Enable Host I/O capability, where files may be accessed via GDB command prompt using remote get, remote put and remote delete commands.

GDBSTUB_BREAK_ON_INIT

Enable this if you want the GDB stub to wait for you to attach GDB before running. It does this by breaking in the init routine; use the gdb ‘c’ command (continue) to start the program.

GDBSTUB_CMDENABLE_P

Some commands are not required by GDB, so if necessary can be disabled to save memory.

GDBSTUB_CMDENABLE_X
GDBSTUB_UART_READ_TIMEOUT

Specify a timeout (in milliseconds) when stub is reading from serial port. Set to 0 to wait indefinitely.

GDBSTUB_FORCE_IRAM

Wherever possible gdbstub code is placed in flash memory. This is fine for most cases, but if debugging whilst flash is disabled or busy (eg during SPI operations or flash write/erase) then you will need to enable this option to move stub code into IRAM.

References
Used by
Environment Variables
SoC support
  • esp8266

Heap
Configuration variables
ENABLE_CUSTOM_HEAP

If your application is experiencing heap fragmentation then you can try the alternative heap.

undefined (default)

OFF, use standard heap

1

Use UMM Malloc.

Warning

Do not enable custom heap allocation and -mforce-l32 compiler flag at the same time.

UMM_FUNC_IRAM

Default: 1 (enabled)

Custom heap functions are stored in IRAM by default for performance reasons.

If you need the IRAM (about 1.5K bytes) then disable this option:

make ENABLE_CUSTOM_HEAP=1 UMM_FUNC_IRAM=0
References
Used by
Environment Variables
SoC support
  • esp8266

Submodule: umm_malloc
umm_malloc - Memory Manager For Small(ish) Microprocessors

This is a memory management library specifically designed to work with the ARM7 embedded processor, but it should work on many other 32 bit processors, as well as 16 and 8 bit devices.

You can even use it on a bigger project where a single process might want to manage a large number of smaller objects, and using the system heap might get expensive.

Acknowledgements

Joerg Wunsch and the avr-libc provided the first malloc() implementation that I examined in detail.

http://www.nongnu.org/avr-libc

Doug Lea’s paper on malloc() was another excellent reference and provides a lot of detail on advanced memory management techniques such as binning.

http://g.oswego.edu/dl/html/malloc.html

Bill Dittman provided excellent suggestions, including macros to support using these functions in critical sections, and for optimizing realloc() further by checking to see if the previous block was free and could be used for the new block size. This can help to reduce heap fragmentation significantly.

Yaniv Ankin suggested that a way to dump the current heap condition might be useful. I combined this with an idea from plarroy to also allow checking a free pointer to make sure it’s valid.

Dimitry Frank contributed many helpful additions to make things more robust including a user specified config file and a method of testing the integrity of the data structures.

Usage

Copy the umm_malloc_cfg_example.h file to umm_malloc_cfg.h and make the changes required to support your application.

The following #defines must be set to something useful for the library to work at all

  • UMM_MALLOC_CFG_HEAP_ADDR must be set to the symbol representing the starting address of the heap. The heap must be aligned on the natural boundary size of the processor.

  • UMM_MALLOC_CFG_HEAP_SIZE must be set to the size of the heap. The heap size must be a multiple of the natural boundary size of the processor.

The fit algorithm is defined as either:

  • UMM_BEST_FIT which scans the entire free list and looks

    for either an exact fit or the smallest block that will satisfy the request. This is the default fit method.

  • UMM_FIRST_FIT which scans the entire free list and looks

    for the first block that satisfies the request.

The following #defines are disabled by default and should remain disabled for production use. They are helpful when testing allocation errors (which are normally due to bugs in the application code) or for running the test suite when making changes to the code.

You can define them in your compiler command line or uncomment the corresponding entries is umm_malloc_cfg.h:

  • UMM_INFO is used to include code that allows dumping the entire heap structure (helpful when there’s a problem).

  • UMM_INTEGRITY_CHECK is used to include code that performs an integrity check on the heap structure. It’s up to you to call the umm_integrity_check() function.

  • UMM_POISON_CHECK is used to include code that adds some bytes around the memory being allocated that are filled with known data. If the data is not intact when the block is checked, then somone has written outside of the memory block they have been allocated. It is up to you to call the umm_poison_check() function.

API

The following functions are available for your application:

  • void *umm_malloc( size_t size );

  • void *umm_calloc( size_t num, size_t size );

  • void *umm_realloc( void *ptr, size_t size );

  • void  umm_free( void *ptr );

They have exactly the same semantics as the corresponding standard library functions.

Background

The memory manager assumes the following things:

  1. The standard POSIX compliant malloc/calloc/realloc/free semantics are used

  2. All memory used by the manager is allocated at link time, it is aligned on a 32 bit boundary, it is contiguous, and its extent (start and end address) is filled in by the linker.

  3. All memory used by the manager is initialized to 0 as part of the runtime startup routine. No other initialization is required.

The fastest linked list implementations use doubly linked lists so that its possible to insert and delete blocks in constant time. This memory manager keeps track of both free and used blocks in a doubly linked list.

Most memory managers use a list structure made up of pointers to keep track of used - and sometimes free - blocks of memory. In an embedded system, this can get pretty expensive as each pointer can use up to 32 bits.

In most embedded systems there is no need for managing a large quantity of memory block dynamically, so a full 32 bit pointer based data structure for the free and used block lists is wasteful. A block of memory on the free list would use 16 bytes just for the pointers!

This memory management library sees the heap as an array of blocks, and uses block numbers to keep track of locations. The block numbers are 15 bits - which allows for up to 32767 blocks of memory. The high order bit marks a block as being either free or in use, which will be explained later.

The result is that a block of memory on the free list uses just 8 bytes instead of 16.

In fact, we go even one step futher when we realize that the free block index values are available to store data when the block is allocated.

The overhead of an allocated block is therefore just 4 bytes.

Each memory block holds 8 bytes, and there are up to 32767 blocks available, for about 256K of heap space. If that’s not enough, you can always add more data bytes to the body of the memory block at the expense of free block size overhead.

There are a lot of little features and optimizations in this memory management system that makes it especially suited to small systems, and the best way to appreciate them is to review the data structures and algorithms used, so let’s get started.

Detailed Description

We have a general notation for a block that we’ll use to describe the different scenarios that our memory allocation algorithm must deal with:

   +----+----+----+----+
c  |* n |  p | nf | pf |
   +----+----+----+----+

Where:

  • c is the index of this block

    • is the indicator for a free block

  • n is the index of the next block in the heap

  • p is the index of the previous block in the heap

  • nf is the index of the next block in the free list

  • pf is the index of the previous block in the free list

The fact that we have forward and backward links in the block descriptors means that malloc() and free() operations can be very fast. It’s easy to either allocate the whole free item to a new block or to allocate part of the free item and leave the rest on the free list without traversing the list from front to back first.

The entire block of memory used by the heap is assumed to be initialized to 0. The very first block in the heap is special - it’t the head of the free block list. It is never assimilated with a free block (more on this later).

Once a block has been allocated to the application, it looks like this:

  +----+----+----+----+
c |  n |  p |   ...   |
  +----+----+----+----+

Where:

  • c is the index of this block

  • n is the index of the next block in the heap

  • p is the index of the previous block in the heap

Note that the free list information is gone because it’s now being used to store actual data for the application. If we had even 500 items in use, that would be 2,000 bytes for free list information. We simply can’t afford to waste that much.

The address of the ... area is what is returned to the application for data storage.

The following sections describe the scenarios encountered during the operation of the library. There are two additional notation conventions:

?? inside a pointer block means that the data is irrelevant. We don’t care about it because we don’t read or modify it in the scenario being described.

... between memory blocks indicates zero or more additional blocks are allocated for use by the upper block.

While we’re talking about “upper” and “lower” blocks, we should make a comment about adresses. In the diagrams, a block higher up in the picture is at a lower address. And the blocks grow downwards their block index increases as does their physical address.

Finally, there’s one very important characteristic of the individual blocks that make up the heap - there can never be two consecutive free memory blocks, but there can be consecutive used memory blocks.

The reason is that we always want to have a short free list of the largest possible block sizes. By always assimilating a newly freed block with adjacent free blocks, we maximize the size of each free memory area.

Operation of malloc right after system startup

As part of the system startup code, all of the heap has been cleared.

During the very first malloc operation, we start traversing the free list starting at index 0. The index of the next free block is 0, which means we’re at the end of the list!

At this point, the malloc has a special test that checks if the current block index is 0, which it is. This special case initializes the free list to point at block index 1 and then points block 1 to the last block (lf) on the heap.

   BEFORE                             AFTER

   +----+----+----+----+              +----+----+----+----+
0  |  0 |  0 |  0 |  0 |           0  |  1 |  0 |  1 |  1 |
   +----+----+----+----+              +----+----+----+----+
                                      +----+----+----+----+
                                   1  |*lf |  0 |  0 |  0 |
                                      +----+----+----+----+
                                               ...
                                      +----+----+----+----+
                                   lf |  0 |  1 |  0 |  0 |
                                      +----+----+----+----+

The heap is now ready to complete the first malloc operation.

Operation of malloc when we have reached the end of the free list and there is no block large enough to accommodate the request.

This happens at the very first malloc operation, or any time the free list is traversed and no free block large enough for the request is found.

The current block pointer will be at the end of the free list, and we know we’re at the end of the list because the nf index is 0, like this:

   BEFORE                             AFTER

   +----+----+----+----+              +----+----+----+----+
pf |*?? | ?? | cf | ?? |           pf |*?? | ?? | lf | ?? |
   +----+----+----+----+              +----+----+----+----+
            ...                                ...
   +----+----+----+----+              +----+----+----+----+
 p | cf | ?? |   ...   |            p | cf | ?? |   ...   |
   +----+----+----+----+              +----+----+----+----+
   +----+----+----+----+              +----+----+----+----+
cf |  0 |  p |  0 | pf |            c | lf |  p |   ...   |
   +----+----+----+----+              +----+----+----+----+
                                      +----+----+----+----+
                                   lf |  0 | cf |  0 | pf |
                                      +----+----+----+----+

As we walk the free list looking for a block of size b or larger, we get to cf, which is the last item in the free list. We know this because the next index is 0.

So we’re going to turn cf into the new block of memory, and then create a new block that represents the last free entry (lf) and adjust the prev index of lf to point at the block we just created. We also need to adjust the next index of the new block (c) to point to the last free block.

Note that the next free index of the pf block must point to the new lf because cf is no longer a free block!

Operation of malloc when we have found a block (cf) that will fit the current request of b units exactly

This one is pretty easy, just clear the free list bit in the current block and unhook it from the free list.

   BEFORE                             AFTER

   +----+----+----+----+              +----+----+----+----+
pf |*?? | ?? | cf | ?? |           pf |*?? | ?? | nf | ?? |
   +----+----+----+----+              +----+----+----+----+
            ...                                ...
   +----+----+----+----+              +----+----+----+----+
 p | cf | ?? |   ...   |            p | cf | ?? |   ...   |
   +----+----+----+----+              +----+----+----+----+
   +----+----+----+----+              +----+----+----+----+  Clear the free
cf |* n |  p | nf | pf |           cf |  n |  p |   ..    |  list bit here
   +----+----+----+----+              +----+----+----+----+
   +----+----+----+----+              +----+----+----+----+
 n | ?? | cf |   ...   |            n | ?? | cf |   ...   |
   +----+----+----+----+              +----+----+----+----+
            ...                                ...
   +----+----+----+----+              +----+----+----+----+
nf |*?? | ?? | ?? | cf |           nf | ?? | ?? | ?? | pf |
   +----+----+----+----+              +----+----+----+----+

Unhooking from the free list is accomplished by adjusting the next and prev free list index values in the pf and nf blocks.

Operation of malloc when we have found a block that will fit the current request of b units with some left over

We’ll allocate the new block at the END of the current free block so we don’t have to change ANY free list pointers.

   BEFORE                             AFTER

   +----+----+----+----+              +----+----+----+----+
pf |*?? | ?? | cf | ?? |           pf |*?? | ?? | cf | ?? |
   +----+----+----+----+              +----+----+----+----+
            ...                                ...
   +----+----+----+----+              +----+----+----+----+
 p | cf | ?? |   ...   |            p | cf | ?? |   ...   |
   +----+----+----+----+              +----+----+----+----+
   +----+----+----+----+              +----+----+----+----+
cf |* n |  p | nf | pf |           cf |* c |  p | nf | pf |
   +----+----+----+----+              +----+----+----+----+
                                      +----+----+----+----+ This is the new
                                    c |  n | cf |   ..    | block at cf+b
                                      +----+----+----+----+
   +----+----+----+----+              +----+----+----+----+
 n | ?? | cf |   ...   |            n | ?? |  c |   ...   |
   +----+----+----+----+              +----+----+----+----+
            ...                                ...
   +----+----+----+----+              +----+----+----+----+
nf |*?? | ?? | ?? | cf |           nf | ?? | ?? | ?? | pf |
   +----+----+----+----+              +----+----+----+----+

This one is prety easy too, except we don’t need to mess with the free list indexes at all becasue we’ll allocate the new block at the end of the current free block. We do, however have to adjust the indexes in cf, c, and n.

That covers the initialization and all possible malloc scenarios, so now we need to cover the free operation possibilities…

Free Scenarios

The operation of free depends on the position of the current block being freed relative to free list items immediately above or below it. The code works like this:

if next block is free
    assimilate with next block already on free list
if prev block is free
    assimilate with prev block already on free list
else
    put current block at head of free list

Step 1 of the free operation checks if the next block is free, and if it is assimilate the next block with this one.

Note that c is the block we are freeing up, cf is the free block that follows it.

   BEFORE                             AFTER

   +----+----+----+----+              +----+----+----+----+
pf |*?? | ?? | cf | ?? |           pf |*?? | ?? | nf | ?? |
   +----+----+----+----+              +----+----+----+----+
            ...                                ...
   +----+----+----+----+              +----+----+----+----+
 p |  c | ?? |   ...   |            p |  c | ?? |   ...   |
   +----+----+----+----+              +----+----+----+----+
   +----+----+----+----+              +----+----+----+----+ This block is
 c | cf |  p |   ...   |            c | nn |  p |   ...   | disconnected
   +----+----+----+----+              +----+----+----+----+ from free list,
   +----+----+----+----+                                    assimilated with
cf |*nn |  c | nf | pf |                                    the next, and
   +----+----+----+----+                                    ready for step 2
   +----+----+----+----+              +----+----+----+----+
nn | ?? | cf | ?? | ?? |           nn | ?? |  c |   ...   |
   +----+----+----+----+              +----+----+----+----+
            ...                                ...
   +----+----+----+----+              +----+----+----+----+
nf |*?? | ?? | ?? | cf |           nf |*?? | ?? | ?? | pf |
   +----+----+----+----+              +----+----+----+----+

Take special note that the newly assimilated block (c) is completely disconnected from the free list, and it does not have its free list bit set. This is important as we move on to step 2 of the procedure…

Step 2 of the free operation checks if the prev block is free, and if it is then assimilate it with this block.

Note that c is the block we are freeing up, pf is the free block that precedes it.

   BEFORE                             AFTER

   +----+----+----+----+              +----+----+----+----+ This block has
pf |* c | ?? | nf | ?? |           pf |* n | ?? | nf | ?? | assimilated the
   +----+----+----+----+              +----+----+----+----+ current block
   +----+----+----+----+
 c |  n | pf |   ...   |
   +----+----+----+----+
   +----+----+----+----+              +----+----+----+----+
 n | ?? |  c |   ...   |            n | ?? | pf | ?? | ?? |
   +----+----+----+----+              +----+----+----+----+
            ...                                ...
   +----+----+----+----+              +----+----+----+----+
nf |*?? | ?? | ?? | pf |           nf |*?? | ?? | ?? | pf |
   +----+----+----+----+              +----+----+----+----+

Nothing magic here, except that when we’re done, the current block (c) is gone since it’s been absorbed into the previous free block. Note that the previous step guarantees that the next block (n) is not free.

Step 3 of the free operation only runs if the previous block is not free. it just inserts the current block to the head of the free list.

Remember, 0 is always the first block in the memory heap, and it’s always head of the free list!

   BEFORE                             AFTER

   +----+----+----+----+              +----+----+----+----+
 0 | ?? | ?? | nf |  0 |            0 | ?? | ?? |  c |  0 |
   +----+----+----+----+              +----+----+----+----+
            ...                                ...
   +----+----+----+----+              +----+----+----+----+
 p |  c | ?? |   ...   |            p |  c | ?? |   ...   |
   +----+----+----+----+              +----+----+----+----+
   +----+----+----+----+              +----+----+----+----+
 c |  n |  p |   ..    |            c |* n |  p | nf |  0 |
   +----+----+----+----+              +----+----+----+----+
   +----+----+----+----+              +----+----+----+----+
 n | ?? |  c |   ...   |            n | ?? |  c |   ...   |
   +----+----+----+----+              +----+----+----+----+
            ...                                ...
   +----+----+----+----+              +----+----+----+----+
nf |*?? | ?? | ?? |  0 |           nf |*?? | ?? | ?? |  c |
   +----+----+----+----+              +----+----+----+----+

Again, nothing spectacular here, we’re simply adjusting a few pointers to make the most recently freed block the first item in the free list.

That’s because finding the previous free block would mean a reverse traversal of blocks until we found a free one, and it’s just easier to put it at the head of the list. No traversal is needed.

Realloc Scenarios

Finally, we can cover realloc, which has the following basic operation.

The first thing we do is assimilate up with the next free block of memory if possible. This step might help if we’re resizing to a bigger block of memory. It also helps if we’re downsizing and creating a new free block with the leftover memory.

First we check to see if the next block is free, and we assimilate it to this block if it is. If the previous block is also free, and if combining it with the current block would satisfy the request, then we assimilate with that block and move the current data down to the new location.

Assimilating with the previous free block and moving the data works like this:

   BEFORE                             AFTER

   +----+----+----+----+              +----+----+----+----+
pf |*?? | ?? | cf | ?? |           pf |*?? | ?? | nf | ?? |
   +----+----+----+----+              +----+----+----+----+
            ...                                ...
   +----+----+----+----+              +----+----+----+----+
cf |* c | ?? | nf | pf |            c |  n | ?? |   ...   | The data gets
   +----+----+----+----+              +----+----+----+----+ moved from c to
   +----+----+----+----+                                    the new data area
 c |  n | cf |   ...   |                                    in cf, then c is
   +----+----+----+----+                                    adjusted to cf
   +----+----+----+----+              +----+----+----+----+
 n | ?? |  c |   ...   |            n | ?? |  c | ?? | ?? |
   +----+----+----+----+              +----+----+----+----+
            ...                                ...
   +----+----+----+----+              +----+----+----+----+
nf |*?? | ?? | ?? | cf |           nf |*?? | ?? | ?? | pf |
   +----+----+----+----+              +----+----+----+----+

Once we’re done that, there are three scenarios to consider:

  1. The current block size is exactly the right size, so no more work is needed.

  2. The current block is bigger than the new required size, so carve off the excess and add it to the free list.

  3. The current block is still smaller than the required size, so malloc a new block of the correct size and copy the current data into the new block before freeing the current block.

The only one of these scenarios that involves an operation that has not yet been described is the second one, and it’s shown below:

BEFORE                             AFTER

   +----+----+----+----+              +----+----+----+----+
 p |  c | ?? |   ...   |            p |  c | ?? |   ...   |
   +----+----+----+----+              +----+----+----+----+
   +----+----+----+----+              +----+----+----+----+
 c |  n |  p |   ...   |            c |  s |  p |   ...   |
   +----+----+----+----+              +----+----+----+----+
                                      +----+----+----+----+ This is the
                                    s |  n |  c |   ..    | new block at
                                      +----+----+----+----+ c+blocks
   +----+----+----+----+              +----+----+----+----+
 n | ?? |  c |   ...   |            n | ?? |  s |   ...   |
   +----+----+----+----+              +----+----+----+----+

Then we call free() with the adress of the data portion of the new block (s) which adds it to the free list.

Esp8266 LIBC Component

This Component accommodates the differences in runtime libraries for the various supported toolchains.

See also ESP Quick Toolchain.

References
Used by
SoC support
  • esp8266

Esp8266 LWIP Version 2

This Component implements the current Version 2 LWIP stack. Note that at present espconn_* functions are not supported.

References
Environment Variables
SoC support
  • esp8266

Submodule: lwip2
lwIP-2 from original source

This repo offers a link layer for esp82xx-nonos-sdk-2. The original goal is to try and use a recent lwIP version for stability reasons. Currently lwIP-v2 is implemented, other IP stacks could be tried.

Notes
  • UDP/TCP codes using lwIP-1.4 need some updates - two examples: arduino and sming

  • ipv6 not tried yet

  • tcp is more stable (example1 and example2)

  • needs more testing

Tested
  • arduino NTPClient

  • arduino WiFiAccessPoint

  • arduino OTA

  • Sming Telnet sample

Build

makefiles are working with linux/osx, and maybe with windows (using ‘make’ included in msys from mingw…)

get lwIP sources

git submodule update --init --recursive

optionnally tune lwIP configuration in glue-lwip/lwipopts.h

build & install

make -f Makefile.<arch> install
MSS

Remember the MSS footprint: 4*MSS bytes in RAM per tcp connection. The lowest recommanded value is 536 which is the default here.

How it works

Espressif binary libraries rely on their lwip implementation. The idea, as described in this comment is to wrap espressif calls, and rewrite them for a new tcp implementation.

Example with lwip1.4’s ethernet_input() called by espressif binary blobs finally reaching lwip2’s:

-- LWIP2-----------------------------------
#define ethernet_input ethernet_input_LWIP2
- lwip2's ethernet_input_LWIP2_is called
                            (/ \)
                             | |
-- glue (new side)-----------^-v-----------
                             | |
glue_ethernet_input          | |
- maps structures glue->new  |
- calls ethernet_input_LWIP2(^ v)
- maps structures new->glue    |
                             | |
-- glue (old side)-----------^-v-----------
                             | |
ethernet_input():            | |
- maps structures old->glue  |
- calls glue_ethernet_input (^ v)
- maps structures glue->old    |
                             | |
- espressif blobs -----------^-v-----------
XXXXXXXXXXXXXXXXXXXXXXXXXXXX | | XXXXXXXXXX
wifi calls    ethernet_input(/ \) XXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-------------------------------------------
History

Original cleaning and port of espressif’s patch set from lwIP-v1.4 to lwIP-v2 with references to lwIP-git-sha1

Discussion on how further work could done

First version of this implementation

Second version for arduino only

Esp8266 SPI Flash Support

Provides functions for access to flash memory.

API Documentation
enum SPIFlashMode

Values:

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_FAST_READ
enumerator MODE_SLOW_READ
enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enum SPIFlashSpeed

Values:

enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enum SPIFlashSize

Values:

enumerator SIZE_1MBIT
enumerator SIZE_2MBIT
enumerator SIZE_4MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT

Not listed.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

enum SPIFlashMode

Values:

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_FAST_READ
enumerator MODE_SLOW_READ
enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enum SPIFlashSpeed

Values:

enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enum SPIFlashSize

Values:

enumerator SIZE_1MBIT
enumerator SIZE_2MBIT
enumerator SIZE_4MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT

Not listed.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

enum SPIFlashMode

Values:

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_FAST_READ
enumerator MODE_SLOW_READ
enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enum SPIFlashSpeed

Values:

enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enum SPIFlashSize

Values:

enumerator SIZE_1MBIT
enumerator SIZE_2MBIT
enumerator SIZE_4MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT

Not listed.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

static inline uint32_t flashmem_get_address(const void *memptr)

Obtain the flash memory address for a memory pointer.

Note

If memptr is not in valid flash memory it will return an offset which exceeds the internal flash memory size.

Note

The flash location is dependent on where rBoot has mapped the firmware.

Parameters:

memptr

Return values:

uint32_t – Offset from start of flash memory

uint32_t flashmem_write(const void *from, uint32_t toaddr, uint32_t size)

Write a block of data to flash.

Note

None of the parameters need to be aligned

Parameters:
  • from – Buffer to obtain data from

  • toaddr – Flash location to start writing

  • size – Number of bytes to write

Return values:

uint32_t – Number of bytes written

uint32_t flashmem_read(void *to, uint32_t fromaddr, uint32_t size)

Read a block of data from flash.

Note

none of the parameters need to be aligned

Parameters:
  • to – Buffer to store data

  • fromaddr – Flash location to start reading

  • size – Number of bytes to read

Return values:

uint32_t – Number of bytes written

bool flashmem_erase_sector(uint32_t sector_id)

Erase a single flash sector.

Parameters:

sector_id – the sector to erase

Return values:

true – on success

SPIFlashInfo flashmem_get_info()

Get flash memory information block.

Return values:

SPIFlashInfo – Information block

uint8_t flashmem_get_size_type()

Returns a number indicating the size of flash memory chip.

Return values:

uint8_t – See SpiFlashInfo.size field for possible values

uint32_t flashmem_get_size_bytes()

get the total flash memory size

Return values:

uint32_t – Size in bytes

uint16_t flashmem_get_size_sectors()

Get the total number of flash sectors.

Return values:

uint16_t – Sector count

uint32_t flashmem_find_sector(uint32_t address, uint32_t *pstart, uint32_t *pend)

Helper function: find the flash sector in which an address resides.

Note

Optional parameters may be null

Parameters:
  • address

  • pstart – OUT/OPTIONAL: Start of sector containing the given address

  • pend – OUT/OPTIONAL: Last address in sector

Return values:

uint32_t – Sector number for the given address

uint32_t flashmem_get_sector_of_address(uint32_t addr)

Get sector number containing the given address.

Parameters:

addr

Return values:

uint32_t – sector number

uint32_t spi_flash_get_id(void)
uint32_t flashmem_write_internal(const void *from, uint32_t toaddr, uint32_t size)

write to flash memory

Note

All parameters MUST be aligned to 4-byte word boundaries, including the RAM pointer

Parameters:
  • from – Buffer to read data from - MUST be word-aligned

  • toaddr – Flash address (offset) to write to - MUST be word-aligned

  • size – Number of bytes to write - MUST be word-aligned

Return values:

uint32_t – Number of bytes actually written

uint32_t flashmem_read_internal(void *to, uint32_t fromaddr, uint32_t size)

Read from flash memory.

Note

All parameters MUST be aligned to 4-byte word boundaries, including the RAM pointer

Parameters:
  • to – Buffer to store data - MUST be word-aligned

  • fromaddr – Flash address (offset) to read from - MUST be word-aligned

  • size – Number of bytes to read - MUST be word-aligned

Return values:

uint32_t – Number of bytes actually read

uint32_t flashmem_get_first_free_block_address()
void flashmem_sfdp_read(uint32_t addr, void *buffer, size_t count)
INTERNAL_FLASH_WRITE_UNIT_SIZE

Flash memory access must be aligned and in multiples of 4-byte words.

INTERNAL_FLASH_READ_UNIT_SIZE
FLASH_TOTAL_SEC_COUNT
SYS_PARAM_SEC_COUNT

Number of flash sectors reserved for system parameters at start.

FLASH_WORK_SEC_COUNT
INTERNAL_FLASH_SECTOR_SIZE
INTERNAL_FLASH_SIZE
INTERNAL_FLASH_WRITE_UNIT_SIZE

Flash memory access must be aligned and in multiples of 4-byte words.

INTERNAL_FLASH_READ_UNIT_SIZE
FLASH_TOTAL_SEC_COUNT
SYS_PARAM_SEC_COUNT

Number of flash sectors reserved for system parameters at start.

FLASH_WORK_SEC_COUNT
INTERNAL_FLASH_SECTOR_SIZE
INTERNAL_FLASH_SIZE
INTERNAL_FLASH_START_ADDRESS
FLASH_TOTAL_SEC_COUNT
SYS_PARAM_SEC_COUNT

Number of flash sectors reserved for system parameters at start.

FLASH_WORK_SEC_COUNT
INTERNAL_FLASH_SECTOR_SIZE
INTERNAL_FLASH_SIZE
INTERNAL_FLASH_START_ADDRESS
struct SPIFlashInfo
#include <esp_spi_flash.h>

SPI Flash memory information block. Copied from bootloader header. See esp_image_header_t.

SPI Flash memory information block. Stored at the beginning of flash memory.

Public Members

uint8_t mode

SPIFlashMode.

uint8_t speed

SPIFlashSpeed.

uint8_t size

SPIFlashSize.

struct STORE_TYPEDEF_ATTR
#include <esp_spi_flash.h>

SPI Flash memory information block. Stored at the beginning of flash memory.

Public Members

uint8_t mode

SPIFlashMode.

uint8_t speed

SPIFlashSpeed.

uint8_t size

SPIFlashSize.

IRAM pre-caching
Introduction

This is taken from the esp8266 arduino core. For details see:

See Flash memory for some background.

Code is always executed from IRAM, however it must be read in from flash memory on first use which can cause issues within timing-critical code.

This can be avoided by placing the code in IRAM. However, IRAM is a very limited resource and should generally be reserved for interrupt service routines.

An alternative solution is to arrange for critical code to be pre-loaded into IRAM before it starts execution.

Note

This cannot be used for interrupt service routines.

By their nature, interrupts are essentially random events and therefore code must be available in IRAM at any time.

Usage

The steps required are:

  1. Mark the function containing code using IRAM_PRECACHE_ATTR()

  2. Place a call to IRAM_PRECACHE_START() before the first line of critical code

  3. Place a call to IRAM_PRECACHE_END() after the last line of critical code

You must always declare a tag to avoid the risk of section name conflicts.

You can find an example of how precaching is used here:

https://github.com/esp8266/Arduino/blob/master/cores/esp8266/core_esp8266_spi_utils.cpp

API Documentation
void iram_precache(void *addr, uint32_t bytes)

Pre-load flash data into the flash instruction cache.

Note

All pages containing the requested region will be read to pull them into cache RAM.

Parameters:
  • addr – First location to cache, specify NULL to use current location.

  • bytes – Number of bytes to cache

IRAM_PRECACHE_ATTR
IRAM_PRECACHE_START(tag)
IRAM_PRECACHE_END(tag)
IRAM_PRECACHE_ATTR

Tools for pre-loading code into the flash cache.

  • It can be useful for code that accesses/uses SPI0 which is connected to the flash chip.

  • Non interrupt handler code that is infrequently called but might otherwise require being in valuable IRAM - such as bit-banging I/O code or some code run at bootup can avoid being permanently in IRAM.

Mark functions containing critical code using this attribute

IRAM_PRECACHE_START(tag)

Place this macro before the first line of the critical code.

Note

Do not omit the tag, and be careful with naming to avoid conflicts

Parameters:
  • tag – Used to create the precached section name, must be globally unique

IRAM_PRECACHE_END(tag)

Place this macro after the last line of critical code.

Parameters:
  • tag – Must be the same tag used in IRAM_PRECACHE_START()

IRAM_PRECACHE_ATTR
IRAM_PRECACHE_START(tag)
IRAM_PRECACHE_END(tag)
References
Used by
SoC support
  • esp8266

Sming Host Emulator

Summary

Allows Sming applications to be built as an executable to run on the development host (Windows or Linux).

This is a source-level emulator for developing and testing new framework code prior to flashing a real device.

This is not a machine emulator; if you need something operating at a lower level take a look at QEMU.

Requirements

CMake is required to build LWIP

Ensure you are using relatively recent compilers, with 32-bit libraries available.

For Linux, you may require the gcc-multilib and g++-multilib packages to build 32-bit executables on a 64-bit OS.

For Windows, make sure your MinGW distro is up to date. See Windows Installation for further details.

Building

Build the framework and application as usual, specifying SMING_ARCH =Host. For example:

cd $SMING_HOME/../samples/Basic_Serial
make SMING_ARCH=Host

This builds the application as an executable in, for example, out/Host/firmware/app.exe. Various command-line options are supported, use --help for details.

The easiest way to run the emulator is via make run. Variables are used to pass the appropriate options to the command line.

To find out what options are in force, use make list-config.

Configuration
CLI_TARGET_OPTIONS

Use this to add any custom options to the emulator command line. e.g.:

make run CLI_TARGET_OPTIONS=–help make run CLI_TARGET_OPTIONS=”–debug=0 –cpulimit=2”

Note: These settings are not ‘sticky’

Components
Sming (Host)

This Component builds a library containing architecture-specific code, and defines dependencies for Sming to build for the Host Emulator.

References
Used by
SoC support
  • host

Serialib

Serialib is a simple C++ library for serial communication. The library has been designed to work under Linux and Windows.

More details on [Lulu’s blog](https://lucidar.me/en/serialib/cross-plateform-rs232-serial-library/)

References
Used by
SoC support
  • host

Submodule: seriallib
Serialib

Serialib is a simple C++ library for serial communication. The library has been designed to work under Linux and Windows.

More details on Lulu’s blog

Host Drivers

Provides low-level peripheral support classes.

Host ADC

Stub functions are provided which insert an appropriate ‘sampling’ delay and return 0.

Host GPIO

The emulator does not provide any low-level GPIO support, this is handled at a higher level using DigitalHooks.

API Documentation
class DigitalHooks

Class to customise behaviour for digital functions.

Note

By default, actions get output to console but this can get very busy. The easiest way to change the behaviour is by sub-classing DigitalHooks and passing the new class instance to setDigitalHooks().

Public Functions

virtual void badPin(const char *function, uint16_t pin)

Report invalid pin number.

Parameters:
  • function – Name of function which was called

  • pin – The pin number

virtual bool pinMode(uint16_t pin, uint8_t mode)

Set pin mode.

Parameters:
  • pin – Has already been range checked

  • mode

Return values:

true – if mode can be set for this pin, will be stored

virtual void digitalWrite(uint16_t pin, uint8_t val)

Change pin output.

Parameters:
  • pin – Has already been range checked

  • val – New pin value

virtual uint8_t digitalRead(uint16_t pin, uint8_t mode)

Read pin state.

Parameters:
  • pin – Has already been range checked

  • mode – The currently set mode for this pin

  • val – State for pin

virtual void pullup(uint16_t pin, bool enable)

Set or clear pullup state for a pin.

Parameters:
  • pin – Has already been range checked

  • enable – true for pullup, false for no pullup

virtual unsigned long pulseIn(uint16_t pin, uint8_t state, unsigned long timeout)

Measure duration of pulse on GPIO.

Parameters:
  • pin – GPIO to measure

  • state – State of pulse to measure [HIGH | LOW]

  • timeout – Maximum duration of pulse

Return values:

unsigned – long Pulse duration in microseconds

virtual uint16_t analogRead(uint16_t pin)

Sample analogue input.

Parameters:

pin – GPIO to read

Return values:

uint16_t – Sample value

Host hw_timer driver

Hardware timers are emulated using a background thread. The O/S scheduler timeslice granularity is quite coarse and for time-critical threads is typically 1ms. Therefore, when a timer becomes close to expiry the thread sits (‘spins’) in a loop to get better resolution. For the rest of the time the thread is suspended or in a wait state.

Host PWM

A header is provided to allow code to compile but the emulator does not provide any further PWM support.

Host UART driver
Introduction

Implements a UART driver to connect via TCP socket, allowing terminal emulation using telnet, or directly to local host serial device (e.g. /dev/ttyUSB0, COM4, etc.)

If not otherwise reassigned, UART0 output is sent to the console and keyboard input is written to the UART0 receive queue.

Build variables
ENABLE_HOST_UARTID

To enable emulation for a UART, set this value to the numbers required. You would normally add this to a project’s component.mk file.

For example:

ENABLE_HOST_UARTID = 0 1

If setting it on the command line, remember to use quotes:

make ENABLE_HOST_UARTID="0 1"

See Basic Serial which uses both serial ports like this.

HOST_UART_PORTBASE

The base port number to use for telnet. Default is 10000, which corresponds to UART0.

This is passed to the command line --uartport option.

HOST_UART_OPTIONS

By default, this value combines the above options.

Allows full customisation of UART command-line options for make run.

TCP port emulation

Set required ports for emulation using the ENABLE_HOST_UARTID, then execute make run.

Note

As an alternative to make run, you can run the compiled application manually like this:

out/Host/debug/firmware/app --pause --uart=0 --uart=1

Now start a telnet session for each serial port, in separate command windows:

telnet localhost 10000
telnet localhost 10001

In the application window, press Enter. This behaviour is enabled by the pause option, which stops the emulator after initialisation so telnet can connect to it. Without pause you’ll lose any serial output at startup.)

Note

For Windows users, putty is a good alternative to telnet. It also has options for things like carriage-return/linefeed translation (“\n” -> “\r\n`”). Run using:

putty telnet://localhost:10000

Port numbers are allocated sequentially from 10000. If you want to use different port numbers, set HOST_UART_PORTBASE.

Physical device connection

Override HOST_UART_OPTIONS adding the –device option. For example:

make run HOST_UART_OPTIONS="--uart=0 --device=/dev/ttyUSB0"

The --device option must follow the --uart option. Another example:

make run HOST_UART_OPTIONS="--uart=0 --device=/dev/ttyUSB0 --uart=1 --device=/dev/ttyUSB1"

The port is opened when uart_init() gets called.

The default baud rate is whatever the application has requested. This can be overridden as follows:

make run HOST_UART_OPTIONS="--uart=0 --device=/dev/ttyUSB0 --baud=921600"

For Windows, substitute the appropriate device name, e.g. COM4 instead of /dev/ttyUSB0.

Note

If necessary, add ENABLE_HOST_UARTID= to prevent telnet windows from being created.

Console I/O may be assigned to a different port like this:

make run HOST_UART_OPTIONS="--uart=1 --device=console"
References
Used by
Environment Variables
SoC support
  • host

Host ESP HAL

Provides implementations for various low-level functions similar to the Esp8266.

  • Task queues

  • Timer queues

  • System functions

References
Used by
SoC support
  • host

Host WiFi

Provides WiFi / network functions. The actual implementations are provided in LWIP

References
Used by
Environment Variables
  • HOST_NETWORK_OPTIONS

SoC support
  • host

GDB Stub for Host

This defines the command line to use when make gdb is run. No additional code is required to debug for the Host.

If you want to debug your application while having a separate UART then make sure to send the following commands to your debugger:

handle SIGUSR1 nostop noprint

This component provides also gdbinit file containing the optimal settings needed for debugging.

References
Used by
Environment Variables
SoC support
  • host

Heap

This Component implements heap-related housekeeping functions. Heap usage is tracked using malloc_count. This also provides some validation (using sentinels to detect if memory blocks are overwritten).

ENABLE_MALLOC_COUNT

We require malloc_count to keep track of heap usage for system_get_free_heap_size(). It does this by hooking the memory allocation routines (malloc, free, etc.). If you wish to disable this behaviour, set ENABLE_MALLOC_COUNT=0. If using tools such as Valgrind, this will provide a cleaner trace.

References
Used by
Environment Variables
SoC support
  • host

Host Library

This Components provides the core functionality for the Host Emulator:

Sockets

Classes to provide simple Berkeley socket support for both Linux and Windows

Options

Command line argument parsing. Applications may pass parameters and access them using the CommandLine.

Startup

Initialises SPI Flash, Uart server (in Host Drivers) and LWIP networking, then enters the main task loop. This loop services LWIP plus the task and timer queues (implemented in Host ESP HAL). The Ctrl+C keypress is trapped to provide an orderly exit. If the system has become stuck in a loop or is otherwise unresponsive, subsequent Ctrl+C presses will force a process termination.

Threads and Interrupts

The emulator uses Posix threads (pthread library) to perform background processing which would probably be done in hardware or which is outside of the framework.

Ideally we’d use SCHED_FIFO to disable time-slicing and more closely resemble how interrupts work on a single-core CPU. However, this mode isn’t supported in Windows, and Linux requires privileged access and can potentially crash the system. Best avoided, I think.

All ESP code runs at a specific interrupt level, where 0 represents regular code. Interrupts are triggered from a separate thread (a CThread instance) which calls :cpp:func:` When an interrupt occurs, the level is raised according to the priority of that interrupt. Until that code has finished, only interrupts of a higher priority will preempt it.

Thread ‘interrupt’ code is sandwiched between calls to interrupt_begin() and interrupt_end(), which blocks interrupts from other threads at the same or lower level. The threads aren’t suspended but will block if they call interrupt_begin(). However, the main thread (level 0) is halted to reflect normal interrupt behaviour.

HOST_PARAMETERS

Set this value to pass additional parameters to a Host application. For example:

make run HOST_PARAMETERS='param1=12 param2="parameter with spaces"'
make run HOST_PARAMETERS="param1=12 param2=\"parameter with spaces\""

See Basic Utility for a worked example.

API
class CommandLine

Provides access to the command line.

Anything which doesn’t start with - is interpreted as an application parameter. For example:

    app param1=value
Parameters which start with - are handled by the Host Emulator. Anything after -- is passed directly to the application:
    app -- --debug --verbose

Public Functions

inline const Parameters &getParameters()

Fetch a reference to the list of command-line parameters.

struct Parameter

Manages a single parameter, may be optionally separated into name=value.

Public Functions

String getName() const

Get parameter name, if there is one.

String getValue() const

Get parameter value.

Public Members

const char *text = {nullptr}

The text exactly as presented on the command line.

class Parameters : public Vector<Parameter>

List of command-line parameters, in order.

Public Functions

Parameter find(const String &name) const

Fetch parameter by name.

Parameters:

name – Search is case-sensitive

Parameter findIgnoreCase(const String &name) const

Fetch parameter by name.

Parameters:

name – Search is NOT case-sensitive

References
Used by
Environment Variables
SoC support
  • host

libc

Contains implementations of any non-standard C library functions.

References
Used by
SoC support
  • host

SPI Flash

This Component emulates an embedded flash memory device using a backing file. It includes additional checks on addresses, sizes and alignments to detect common issues which can be more difficult to find when running on target hardware.

The default backing file is called flash.bin, located in the same directory as the host executable.

See Virtual Flasher for configuration details.

References
Used by
SoC support
  • host

Virtual Flasher

Flash memory access is emulated using SPI Flash.

This Component implements make targets to operate on the flash backing file in a similar way to flashing a real device.

Build Variables

The following options are interpreted and used to provide command-line parameters to the emulator executable:

FLASH_BIN

Full path to the flash backing file

HOST_FLASH_OPTIONS

This defaults to a combination of the above variables, but you can override if necessary.

The size of the flash memory is set via Hardware configuration.

See Esptool for details and other applicable variables.

Build targets
  • make flashinit to clear and reset the file.

  • make flashfs to copy the generated SPIFFS image into the backing file.

  • make flash writes out all required images to the backing file. For now, this is the same as make flashfs but that will change when support is added for custom user images.

References
Used by
Environment Variables
SoC support
  • host

Sming RP2040 Architecture

Support building Sming for the Raspberry Pi RP2040 SOC.

Testing so far has been limited to the Rasperry Pi Pico, but there are lots of other boards available. Configure this using the PICO_BOARD setting. The default is pico. You can find the full list here.

Special mention to the arduino-pico project https://github.com/earlephilhower/arduino-pico. Lots of helpful stuff in there!

Note

This architecture should be considered experimental at present.

Tested and working:

  • CPU frequency adjustment system_get_cpu_freq(), system_update_cpu_freq()

  • Timers working: hardware, software and CPU cycle counter

  • Hardware serial ports (UART driver)

  • Task queue (also supports queuing tasks from code running on core #1)

  • Flash memory routines

  • os_random() and os_get_random() implemented using ring oscillator. This is the best the hardware is capable of, but not crypto grade.

  • Heap is standard newlib implementation, system_get_free_heap_size() provided.

  • Software watchdog implemented, timeout is 8 seconds

  • A disassembly and symbol file are generated for this architecture.

  • SDK declares flash memory size in the board header files. This is checked at compile time against the value declared in the partition table.

  • Reset information system_get_rst_info() indicates watchdog or manual resets. Exception information not yet implemented.

  • System functions system_get_chip_id(), system_get_sdk_version().

  • Partitions and file systems (except SD cards and FAT)

  • SPIClass tested with Radio_nRF24L01 sample only

  • WiFi networking support for the Pico-W

  • Standard analogue I/O via analogRead. More advanced hardware capabilities require use of the SDK directly.

  • Dual-core support. See below for details.

Yet to be implemented:

USB

Best to write a separate Sming-USB library (based on TinyUSB) to support the RP2040, ESP32-S2 & ESP32-S3 variants. Needs some thought about good integration into the framework. Arduino-Pico overrides HardwareSerial to support serial devices, we can do something similar.

HardwareSPI

To support DMA, etc.

PWM

Hardware can drive up to 16 outputs and measure input frequency/duty cycle.

I2C

Has hardware support

RTC

Can wake from deep sleep but requires an external clock (e.g. 32kHz crystal) and appropriate API. (Setting and reading the time is implemented.)

Low-power modes

Deep sleep / suspend / power-saving

PIO (Programmable I/O)

A killer feature for the RP2040. Uses range from simple glue logic to I2S, etc.

Crash/exception handling & serial debugging

RP2040 supports JTAG debugging but requires extra hardware. Serial debugging is often enough and easier to set up. Requires GDB stub plus implementing crash handler callbacks, etc.

Multi-boot / OTA updates.

If you run make map you’ll see there is no bootloader! It’s part of the firmware image at present. Adding RP2040 support to rBoot may work, however the Pico typically has only 2MByte flash which is quite restrictive. It is also necessary to compile images at different addresses as there is no windowed XIP (eXecute In Place) capability. See Flash In-Place library for a basic method of OTA.

Requirements

These requirements are in addition to the standard Sming setup.

The easiest way to get started is with the Sming installer - see Getting Started.

Note: Windows is not currently included in the chocolatey repository. The following instructions should help.

Compiler/linker

The RP2040 contains two ARM Cortex-M0+ cores. Tools for all platforms can be downloaded from the ARM developer website.

Unzip the archive to a suitable location (e.g. /opt/rp2040 or c:\tools\rp2040) and set PICO_TOOLCHAIN_PATH accordingly.

Note

At time of writing the Ubuntu repositories contain an older version of this toolchain. It also does not contain GDB, but can be installed separately:

sudo apt install gcc-arm-none-eabi gdb-multiarch

To use gdb-multiarch you’ll need to do this:

make gdb GDB=gdb-multiarch

Ninja

This is used to build the RP2040 SDK code:

sudo apt install ninja-build

It is available for other platforms at https://ninja-build.org/ and consists of a single executable file. The application should either be in the system path, or set NINJA to the full path of the executable file.

If you have Sming working with the ESP32 then you’ll already have it installed.

Setup and programming

Serial support requires a 3.3v USB adapter connected to the appropriate pins:

  • UART0: TX = 0, RX = 1

  • UART1: TX = 4, RX = 5

To program your device, unplug the USB cable (i.e. remove power from the device) then hold down the BOOTSEL button whilst plugging it back in again.

You can then run:

make flash

as usual and the device will be programmed.

Once Sming is running on the device, reprogramming is simpler and only requires pressing the BOOTSEL button (no power cycle).

If the firmware has crashed or stalled the watchdog timer should reboot the system after 8 seconds, at which point BOOTSEL should be detected. So just hold the button down until this happens.

If all else fails, go through the initial power-cycle process.

This behaviour can be disabled using the ENABLE_BOOTSEL setting.

Boot process

Unlike the Espressif parts, the RP2040 is not programmed via the serial port, but written to the device when configured as a Mass Storage device (removable flash drive).

Data to be flashed must be in UF2 format and sent as a single file. See UF2 Support.

Once the file has finished sending the RP2040 reboots itself into normal operating mode (assuming BOOTSEL has been released).

The RP2040 can also be programmed via JTAG debugging but this requires additional hardware and setup.

Note

The RP2040 bootloader does not include support for reading flash memory via mass storage, so commands such as make verifyflash won’t work at present.

Dual-core support

Sming is a strictly non-threaded framework, and all code runs on core #0. The SDK multicore API may still be used to run code on core #1, but this requires some care to ensure smooth operation.

The task queue (System::queueTask(), etc.) may be used to send messages to Sming from Core #1 code.

Passing messages the other way, from Sming code to core #1, could be done using a separate SDK task queue.

Flash access

Core 1 code may run directly from flash memory (via XIP) without any special considerations. However, during flash erase/write operations (e.g. file writes) XIP is disabled. If core 1 code attempts to access flash during these periods the system will hard fault.

Note

Floating-point support requires use of routines in flash memory. Integer operations should all be safe to use.

If unexplained crashes are occurring then check the build output files (in out/Rp2040/debug/build) or use a debugger to identify any errant code running from flash.

A typical use for core #1 might be to perform processing of some kind, such as processing data sampled via analogue inputs. If all code is run from RAM then it can continue uninterrupted even during filing system operations.

Alternatively some kind of synchronisation mechanism may be used to ensure that core 1 is suspended or running from RAM during any flash erase/write operations.

Networking

The Pico-W variant includes an Infineon CYW43439 bluetooth/WiFi SoC.

Raspberry Pi use the … driver. The SDK also includes an LWIP implementation.

The physical interface is SPI using a custom (PIO) implementation. This requires the use of GPIOxx which can no longer be accessed directly, but instead via xxxxx.

The CYW43 chip is initialised (via cyw43_ensure_up) when application code makes the first call into the networking API, for example by enabling station or AP access. Part of the hardware configuration here is to download firmware to the CYW43 chip (about 240KB) plus the CLM BLOB (< 1KB).

Sming contains patches which compresses this data (based on https://github.com/raspberrypi/pico-sdk/issues/909) to about 145KB. By default, it is linked into the application image, but can also be read from a separate partition.

Source code

The RP2040 is a very capable SOC, albeit without WiFi. A massive advantage is that the platform is fully open-source. Even the bootrom is published!

Here’s a summary of the various Github repositories the Raspberry Pi Foundation have made available:

https://github.com/raspberrypi/pico-sdk

The core SDK for the RP2040 SOC. Sming includes this as a submodule.

https://github.com/raspberrypi/picotool

This is a tool for inspecting RP2040 binaries, and interacting with RP2040 devices when they are in BOOTSEL mode. It does this by talking to a custom USB device implemented in the RP2040 bootrom.

Getting this to build is a bit fiddly. So far I’ve managed without it, but there is a picotool component in the framework which can be used to try building it.

https://github.com/raspberrypi/pico-bootrom

Contents of the RP2040 boot rom. Very handy to be able to see this.

https://github.com/raspberrypi/pico-examples

Examples using the pico SDK directly.

https://github.com/raspberrypi/picoprobe

An RP2040 board can be used as a low-cost JTAG adapter using this firmware. Takes some setting up to use though. See [Getting Started PDF](https://datasheets.raspberrypi.org/pico/getting-started-with-pico.pdf) for details.

https://github.com/raspberrypi/pico-extras

Some additional libraries which may or may not end up in the SDK.

https://github.com/raspberrypi/pico-playground

Further examples using the pico-extras libraries.

Build variables
PICO_TOOLCHAIN_PATH

This contains the base directory for the toolchain used to build the framework. Pre-compiled toolchains can be downloaded from the ARM Developer website.

Components
Sming (Rp2040)

This Component builds a library containing architecture-specific code, and defines dependencies for Sming to build for Raspberry Pi 2040 microcontroller-based boards.

References
Used by
Environment Variables
  • TARGET_BIN

SoC support
  • rp2040

RP2040 Drivers

Provides low-level peripheral drivers.

GPIO: General-Purpose I/O

SDK definitions for GPIO.

enum GPIO_INT_TYPE

Values:

enumerator GPIO_PIN_INTR_DISABLE

Interrupt disabled for this pin

enumerator GPIO_PIN_INTR_POSEDGE

Interrupt occurs on positive edge

enumerator GPIO_PIN_INTR_NEGEDGE

Interrupt occurs on negative edge

enumerator GPIO_PIN_INTR_ANYEDGE

Interrupt occurs on both positive and negative edge

enumerator GPIO_PIN_INTR_LOLEVEL

Interrupt occurs when GPIO low

enumerator GPIO_PIN_INTR_HILEVEL

Interrupt occurs when GPIO high

enumerator GPIO_PIN_INTR_DISABLE
enumerator GPIO_PIN_INTR_POSEDGE
enumerator GPIO_PIN_INTR_NEGEDGE
enumerator GPIO_PIN_INTR_ANYEDGE
enumerator GPIO_PIN_INTR_LOLEVEL
enumerator GPIO_PIN_INTR_HILEVEL
hw_timer: Hardware Timers

Driver for hardware timers.

API Documentation
enum hw_timer_clkdiv_t

Values:

enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enum hw_timer_intr_type_t

Values:

enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enum hw_timer_source_type_t

Values:

enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enum hw_timer_clkdiv_t

Values:

enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enum hw_timer_intr_type_t

Values:

enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enum hw_timer_source_type_t

Values:

enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enum hw_timer_clkdiv_t

Values:

enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enumerator TIMER_CLKDIV_1
enumerator TIMER_CLKDIV_16
enumerator TIMER_CLKDIV_256
enum hw_timer_intr_type_t

Values:

enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enumerator TIMER_EDGE_INT
enumerator TIMER_LEVEL_INT
enum hw_timer_source_type_t

Values:

enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
enumerator TIMER_FRC1_SOURCE
enumerator TIMER_NMI_SOURCE
typedef void (*hw_timer_callback_t)(void *arg)
typedef void (*hw_timer_callback_t)(void *arg)
typedef void (*hw_timer_callback_t)(void *arg)
struct hw_timer_private_t hw_timer_private
void hw_timer1_attach_interrupt(hw_timer_source_type_t source_type, hw_timer_callback_t callback, void *arg)

Attach an interrupt for the timer.

Parameters:
  • source_type – Ignored, uses APB clock source

  • callback – Callback function invoked via timer interrupt

  • arg – Passed to callback function

  • source_type

  • callback – Callback function invoked via timer interrupt

  • arg – Passed to callback function

inline void hw_timer1_enable(hw_timer_clkdiv_t div, hw_timer_intr_type_t intr_type, bool auto_load)

Enable the timer.

Note: With one-shot timer application callback must stop the timer when it is no longer required. This is to reduce callback latency. If this is not done, timer will trigger again when timer counter wraps around to 0. For /16 divisor this is only 1.7s.

Parameters:
  • div

  • intr_type – Ignored, always level-triggered

  • auto_load

  • div

  • intr_type

  • auto_load – true for repeating timer, false for one-shot

  • div

  • intr_type

  • auto_load

void hw_timer1_write(uint32_t ticks)

Set the timer interval.

Set the timer interval and arm.

Parameters:

ticks

void hw_timer1_disable(void)

Disable the timer.

void hw_timer1_detach_interrupt(void)

Detach interrupt from the timer.

uint32_t hw_timer1_read(void)

Get timer1 count.

Return values:

uint32_t – Current count value, counts from initial value down to 0

static uint32_t hw_timer2_read(void)

Read current timer2 value.

Return values:

uint32_t

void hw_timer_init(void)

Initialise hardware timers.

Note

Called by startup code

void hw_timer2_set_alarm(uint32_t ticks)

Set timer2 alarm count value.

Note

For internal use ONLY; used by software timers

Parameters:

ticks

uint32_t hw_timer_ticks()

Fetch 32-bit microsecond count.

All timers reference from a single 64-bit counter. We use only the lower 32 bits here as it provides lowest latency and compatibility with existing API.

HW_TIMER1_GROUP
HW_TIMER1_INDEX
MAX_HW_TIMER1_INTERVAL

Maximum timer interval in ticks.

Note

The corresponding time interval depends on the prescaler in use:

    /1 - 26.84s
    /16 - 429.50s
    /256 - 6871.95s
ESP32 supports a wide range of prescalers and uses 54-bit counter value. Limiting the range 31 bits avoids issues with overflows and moving to 64-bit calculations.

MIN_HW_TIMER1_INTERVAL_US

Minimum hardware interval in microseconds.

Note

Attempting to use repetitive interrupts below this level can lead to system instabliity and lockups, due to the software overhead in servicing the interrupts.

NOW()
MAX_HW_TIMER1_INTERVAL

Maximum timer interval in ticks.

Note

The corresponding time interval depends on the prescaler in use:

    /1 - 0.1048s
    /16 - 1.677s
    /256 - 26.84s

MIN_HW_TIMER1_INTERVAL_US

Minimum hardware interval in microseconds.

Note

Attempting to use repetitive interrupts below this level can lead to system instabliity and lockups, due to the software overhead in servicing the interrupts.

HW_TIMER2_CLKDIV
HW_TIMER2_CLK
MAX_HW_TIMER1_INTERVAL

Maximum timer interval in ticks.

MIN_HW_TIMER1_INTERVAL_US

Minimum hardware interval in microseconds.

Note

Attempting to use repetitive interrupts below this level can lead to system instabliity and lockups, due to the software overhead in servicing the interrupts.

HW_TIMER2_CLK
struct hw_timer_private_t
#include <hw_timer.h>
OS Timer

Driver for software timer queues

using smg_timer_func_t = void (*)(void *arg)
void smg_timer_arm_ticks(os_timer_t *ptimer, uint32_t ticks, bool repeat_flag)

Set a software timer using the Timer2 tick value.

This function has been added to Sming for more efficient and flexible use of software timers. It can be used alongside the SDK os_timer_arm_new() function.

Parameters:
  • ptimerTimer structure

  • ticks – Tick count duration for the timer

  • repeat_flag – true if timer will automatically repeat

void smg_timer_setfn(os_timer_t *ptimer, os_timer_func_t pfunction, void *parg)
void smg_timer_arm_us(os_timer_t *ptimer, uint32_t time_us, bool repeat_flag)
void smg_timer_arm(os_timer_t *ptimer, uint32_t time_ms, bool repeat_flag)
void smg_timer_disarm(os_timer_t *ptimer)
void smg_timer_done(os_timer_t *ptimer)
static inline uint64_t smg_timer_expire(const os_timer_t *ptimer)
void os_timer_arm_ticks(os_timer_t *ptimer, uint32_t ticks, bool repeat_flag)

Set a software timer using the Timer2 tick value.

This function has been added to Sming for more efficient and flexible use of software timers. It can be used alongside the SDK os_timer_arm_new() function.

Parameters:
  • ptimerTimer structure

  • ticks – Tick count duration for the timer

  • repeat_flag – true if timer will automatically repeat

static inline bool os_timer_is_armed(const os_timer_t *ptimer)
static inline uint64_t os_timer_expire(const os_timer_t *ptimer)
static inline void os_timer_done(os_timer_t *ptimer)
os_timer_func_t
os_timer_t
os_timer_arm
os_timer_arm_us
os_timer_disarm
os_timer_setfn
os_timer_arm_ticks
os_timer_expire
os_timer_done
struct smg_timer_t
#include <os_timer.h>
PWM: Pulse-Width Modulation
API Documentation
void pwm_init(uint32_t period, uint32_t *duty, uint32_t pwm_channel_num, uint32_t (*pin_info_list)[3])

Initialize PWM function, including GPIO selection, period and duty cycle.

Example:

    uint32 ioInfo[][3] = {
        {PWM_0_OUT_IO_MUX, PWM_0_OUT_IO_FUNC, PWM_0_OUT_IO_NUM},
        {PWM_1_OUT_IO_MUX, PWM_1_OUT_IO_FUNC, PWM_1_OUT_IO_NUM},
        {PWM_2_OUT_IO_MUX, PWM_2_OUT_IO_FUNC, PWM_2_OUT_IO_NUM}
};

pwm_init(light_param.pwm_period, light_param.pwm_duty, 3, ioInfo);

Example:

    uint32 io_info[][3] = {
        {PWM_0_OUT_IO_MUX, PWM_0_OUT_IO_FUNC, PWM_0_OUT_IO_NUM},
        {PWM_1_OUT_IO_MUX, PWM_1_OUT_IO_FUNC, PWM_1_OUT_IO_NUM},
        {PWM_2_OUT_IO_MUX, PWM_2_OUT_IO_FUNC, PWM_2_OUT_IO_NUM}
};

pwm_init(light_param.pwm_period, light_param.pwm_duty, 3, io_info);

Note

This API can be called only once.

Note

This API can be called only once.

Parameters:
  • period – PWM period

  • duty – duty cycle of each output

  • pwm_channel_num – PWM channel number

  • pin_info_list – Array containing an entry for each channel giving

  • period – PWM period

  • duty – duty cycle of each output

  • pwm_channel_num – PWM channel number

  • pin_info_list – Array containing an entry for each channel giving

void pwm_start(void)

Starts PWM.

This function needs to be called after PWM configuration is changed.

void pwm_set_duty(uint32_t duty, uint8_t channel)

Sets duty cycle of a PWM output.

Set the time that high-level signal will last. The range of duty depends on PWM period. Its maximum value of which can be Period * 1000 / 45.

For example, for 1-KHz PWM, the duty range is 0 ~ 22222.

Parameters:
  • duty – The time that high-level single will last, duty cycle will be (duty*45)/(period*1000)

  • channel – PWM channel, which depends on how many PWM channels are used

uint32_t pwm_get_duty(uint8_t channel)

Get duty cycle of PWM output.

Duty cycle will be (duty*45) / (period*1000).

Parameters:

channel – PWM channel, which depends on how many PWM channels are used

Return values:

uint32 – Duty cycle of PWM output

void pwm_set_period(uint32_t period)

Set PWM period.

Parameters:

period – PWM period in us. For example, 1-KHz PWM period = 1000us.

uint32_t pwm_get_period(void)

Get PWM period.

Return values:

uint32 – Return PWM period in us.

uint32_t get_pwm_version(void)

Get version information of PWM.

Return values:

uint32 – PWM version

UART: Universal Asynchronous Receive/Transmit

Custom asynchronous driver.

Warning

doxygengroup: Cannot find group “uart_driver” in doxygen xml output for project “api” from directory: ../api/xml/

References
Used by
Environment Variables
SoC support
  • rp2040

GDB Stub for RP2040

This defines the command line to use when make gdb is run.

TODO: Implement stub code to allow serial debugging.

References
Used by
Environment Variables
SoC support
  • rp2040

RP2040 LIBC Component

This Component accommodates the differences in runtime libraries for the various supported toolchains.

References
Used by
SoC support
  • rp2040

Picotool

Picotool is a tool for inspecting RP2040 binaries, and interacting with RP2040 devices when they are in BOOTSEL mode.

Note for full documentation see https://rptl.io/pico-get-started Appendix B.

References
Environment Variables
  • PICOTOOL

SoC support
  • rp2040

Submodule: picotool
Building

You need to set PICO_SDK_PATH in the environment, or pass it to cmake You need to install libusb-1.0.

Linux/Mac: use your favorite package tool

Windows: download from here https://libusb.info/

If you are on Windows, set LIBUSB_ROOT environment variable to the install directory

mkdir build
cd build
cmake ..
make

for Windows non MinGW/WSL:

mkdir build
cd build
cmake -G "NMake Makefiles" ..
nmake
Overview

Picotool is a tool for inspecting RP2040 binaries, and interacting with RP2040 devices when they are in BOOTSEL mode.

Note for full documentation see https://rptl.io/pico-get-started

$ picotool help
PICOTOOL:
    Tool for interacting with a RP2040 device in BOOTSEL mode, or with a RP2040 binary

SYNOPSYS:
    picotool info [-b] [-p] [-d] [-l] [-a] [--bus <bus>] [--address <addr>] [-f] [-F]
    picotool info [-b] [-p] [-d] [-l] [-a] <filename> [-t <type>]
    picotool load [-v] [-x] <filename> [-t <type>] [-o <offset>] [--bus <bus>] [--address <addr>] [-f] [-F]
    picotool save [-p] [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>]
    picotool save -a [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>]
    picotool save -r <from> <to> [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>]
    picotool verify [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t <type>] [-r <from> <to>] [-o <offset>]
    picotool reboot [-a] [-u] [--bus <bus>] [--address <addr>] [-f] [-F]
    picotool version [-s]
    picotool help [<cmd>]

COMMANDS:
    info      Display information from the target device(s) or file.
              Without any arguments, this will display basic information for all connected RP2040 devices in BOOTSEL mode
    load      Load the program / memory range stored in a file onto the device.
    save      Save the program / memory stored in flash on the device to a file.
    verify    Check that the device contents match those in the file.
    reboot    Reboot the device
    version   Display picotool version
    help      Show general help or help for a specific command

Use "picotool help <cmd>" for more info

Note commands that aren’t acting on files require an RP2040 device in BOOTSEL mode to be connected.

info

So there is now Binary Information support in the SDK which allows for easily storing compact information that picotool can find (See Binary Info section below). The info command is for reading this information.

The information can be either read from one or more connected RP2040 devices in BOOTSEL mode, or from a file. This file can be an ELF, a UF2 or a BIN file.

$ picotool help info
INFO:
    Display information from the target device(s) or file.
    Without any arguments, this will display basic information for all connected RP2040 devices in BOOTSEL mode

SYNOPSYS:
    picotool info [-b] [-p] [-d] [-l] [-a] [--bus <bus>] [--address <addr>] [-f] [-F]
    picotool info [-b] [-p] [-d] [-l] [-a] <filename> [-t <type>]

OPTIONS:
    Information to display
        -b, --basic
            Include basic information. This is the default
        -p, --pins
            Include pin information
        -d, --device
            Include device information
        -l, --build
            Include build attributes
        -a, --all
            Include all information

TARGET SELECTION:
    To target one or more connected RP2040 device(s) in BOOTSEL mode (the default)
        --bus <bus>
            Filter devices by USB bus number
        --address <addr>
            Filter devices by USB device address
    To target a file
        <filename>
            The file name
        -t <type>
            Specify file type (uf2 | elf | bin) explicitly, ignoring file extension

e.g.

$ picotool info
Program Information
 name:      hello_world
 features:  stdout to UART
$ picotool info -a
Program Information
 name:          hello_world
 features:      stdout to UART
 binary start:  0x10000000
 binary end:    0x1000606c

Fixed Pin Information
 20:  UART1 TX
 21:  UART1 RX

Build Information
 build date:        Dec 31 2020
 build attributes:  Debug build

Device Information
 flash size:   2048K
 ROM version:  2
$ picotool info -bp
Program Information
 name:      hello_world
 features:  stdout to UART

Fixed Pin Information
 20:  UART1 TX
 21:  UART1 RX
$ picotool info -a lcd_1602_i2c.uf2
File lcd_1602_i2c.uf2:

Program Information
 name:          lcd_1602_i2c
 web site:      https://github.com/raspberrypi/pico-examples/tree/HEAD/i2c/lcd_1602_i2c
 binary start:  0x10000000
 binary end:    0x10003c1c

Fixed Pin Information
 4:  I2C0 SDA
 5:  I2C0 SCL

Build Information
 build date:  Dec 31 2020
save

Save allows you to save a range of memory or a program or the whole of flash from the device to a BIN file or a UF2 file

$ picotool help save
SAVE:
    Save the program / memory stored in flash on the device to a file.

SYNOPSYS:
    picotool save [-p] [--bus <bus>] [--address <addr>] <filename> [-t <type>]
    picotool save -a [--bus <bus>] [--address <addr>] <filename> [-t <type>]
    picotool save -r <from> <to> [--bus <bus>] [--address <addr>] <filename> [-t <type>]

OPTIONS:
    Selection of data to save
        -p, --program
            Save the installed program only. This is the default
        -a, --all
            Save all of flash memory
        -r, --range
            Save a range of memory; note that the range is expanded to 256 byte boundaries
        <from>
            The lower address bound in hex
        <to>
            The upper address bound in hex
    Source device selection
        --bus <bus>
            Filter devices by USB bus number
        --address <addr>
            Filter devices by USB device address
    File to save to
        <filename>
            The file name
        -t <type>
            Specify file type (uf2 | elf | bin) explicitly, ignoring file extension

e.g.

$ picotool info
Program Information
name:      lcd_1602_i2c
web site:  https://github.com/raspberrypi/pico-examples/tree/HEAD/i2c/lcd_1602_i2c
$ picotool save spoon.uf2
Saving file: [==============================]  100%
Wrote 51200 bytes to spoon.uf2
$ picotool info spoon.uf2
File spoon.uf2:
Program Information
name:      lcd_1602_i2c
web site:  https://github.com/raspberrypi/pico-examples/tree/HEAD/i2c/lcd_1602_i2c
Binary Information

Binary information is machine locatable and generally machine consumable. I say generally because anyone can include any information, and we can tell it from ours, but it is up to them whether they make their data self describing.

Note that we will certainly add more binary info over time, but I’d like to get a minimum core set included in most binaries from launch!!

Basic Information

This information is really handy when you pick up a Pico and don’t know what is on it!

Basic information includes

  • program name

  • program description

  • program version string

  • program build date

  • program url

  • program end address

  • program features - this is a list built from individual strings in the binary, that can be displayed (e.g. we will have one for UART stdio and one for USB stdio) in the SDK

  • build attributes - this is a similar list of strings, for things pertaining to the binary itself (e.g. Debug Build)

The binary information is self-describing/extensible, so programs can include information picotool is not aware of (e.g. MicroPython includes a list of in-built libraries)

Pins

This is certainly handy when you have an executable called ‘hello_world.elf’ but you forgot what board it is built for…

Static (fixed) pin assignments can be recorded in the binary in very compact form:

$ picotool info --pins sprite_demo.elf
File sprite_demo.elf:

Fixed Pin Information
0-4:    Red 0-4
6-10:   Green 0-4
11-15:  Blue 0-4
16:     HSync
17:     VSync
18:     Display Enable
19:     Pixel Clock
20:     UART1 TX
21:     UART1 RX
Including Binary information

Binary information is declared in the program by macros (vile warped macros); for the previous example:

$ picotool info --pins sprite_demo.elf
File sprite_demo.elf:

Fixed Pin Information
0-4:    Red 0-4
6-10:   Green 0-4
11-15:  Blue 0-4
16:     HSync
17:     VSync
18:     Display Enable
19:     Pixel Clock
20:     UART1 TX
21:     UART1 RX

… there is one line in the setup_default_uart function:

bi_decl_if_func_used(bi_2pins_with_func(PICO_DEFAULT_UART_RX_PIN, PICO_DEFAULT_UART_TX_PIN, GPIO_FUNC_UART));

The two pin numbers, and the function UART are stored, then decoded to their actual function names (UART1 TX etc) by picotool. The bi_decl_if_func_used makes sure the binary information is only included if the containing function is called.

Equally, the video code contains a few lines like this:

bi_decl_if_func_used(bi_pin_mask_with_name(0x1f << (PICO_SCANVIDEO_COLOR_PIN_BASE + PICO_SCANVIDEO_DPI_PIXEL_RSHIFT), "Red 0-4"));
Details

Things are designed to waste as little space as possible, but you can turn everything off with preprocessor var PICO_NO_BINARY_INFO=1. Additionally any SDK code that inserts binary info can be separately excluded by its own preprocesor var.

You need

#include "pico/binary_info.h"

Basically you either use bi_decl(bi_blah(...)) for unconditional inclusion of the binary info blah, or bi_decl_if_func_used(bi_blah(...)) for binary information that may be stripped if the enclosing function is not included in the binary by the linker (think --gc-sections)

There are a bunch of bi_ macros in the headers

#define bi_binary_end(end) ...
#define bi_program_name(name) ...
#define bi_program_description(description) ...
#define bi_program_version_string(version_string) ...
#define bi_program_build_date_string(date_string) ...
#define bi_program_url(url) ...
#define bi_program_feature(feature) ...
#define bi_program_build_attribute(attr) ...
#define bi_1pin_with_func(p0, func) ...
#define bi_2pins_with_func(p0, p1, func) ...
#define bi_3pins_with_func(p0, p1, p2, func) ...
#define bi_4pins_with_func(p0, p1, p2, p3, func) ...
#define bi_5pins_with_func(p0, p1, p2, p3, p4, func) ...
#define bi_pin_range_with_func(plo, phi, func) ...
#define bi_pin_mask_with_name(pmask, label) ...
#define bi_pin_mask_with_names(pmask, label) ...
#define bi_1pin_with_name(p0, name) ...
#define bi_2pins_with_names(p0, name0, p1, name1) ...
#define bi_3pins_with_names(p0, name0, p1, name1, p2, name2) ...
#define bi_4pins_with_names(p0, name0, p1, name1, p2, name2, p3, name3) ...

which make use of underlying macros, e.g.

#define bi_program_url(url) bi_string(BINARY_INFO_TAG_RASPBERRY_PI, BINARY_INFO_ID_RP_PROGRAM_URL, url)

NOTE: It is easy to forget to enclose these in bi_decl etc., so an effort has been made (at the expense of a lot of kittens) to make the build fail with a somewhat helpful error message if you do so.

For example, trying to compile

bi_1pin_with_name(0, "Toaster activator");

gives

/home/graham/dev/mu/pico_sdk/src/common/pico_binary_info/include/pico/binary_info/code.h:17:55: error: '_error_bi_is_missing_enclosing_decl_261' undeclared here (not in a function)
17 | #define __bi_enclosure_check_lineno_var_name __CONCAT(_error_bi_is_missing_enclosing_decl_,__LINE__)
|                                                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
... more macro call stack of doom
Setting common fields from CMake

You can use

pico_set_program_name(foo "not foo") # as "foo" would be the default
pico_set_program_description(foo "this is a foo")
pico_set_program_version_string(foo "0.00001a")
pico_set_program_url(foo "www.plinth.com/foo")

Note all of these are passed as command line arguments to the compilation, so if you plan to use quotes, newlines etc you may have better luck defining via bi_decl in the code.

Additional binary information/picotool features
Block devices

MicroPython and CircuitPython, eventually the SDK and others may support one or more storage devices in flash. We already have macros to define these although picotool doesn’t do anything with them yet… but backup/restore/file copy and even fuse mount in the future might be interesting.

I suggest we tag these now…

This is what I have right now off the top of my head (at the time)

#define bi_block_device(_tag, _name, _offset, _size, _extra, _flags)

with the data going into

typedef struct __packed _binary_info_block_device {
        struct _binary_info_core core;
        bi_ptr_of(const char) name; // optional static name (independent of what is formatted)
        uint32_t offset;
        uint32_t size;
        bi_ptr_of(binary_info_t) extra; // additional info
        uint16_t flags;
} binary_info_block_device_t;

and

enum {
    BINARY_INFO_BLOCK_DEV_FLAG_READ = 1 << 0, // if not readable, then it is basically hidden, but tools may choose to avoid overwriting it
    BINARY_INFO_BLOCK_DEV_FLAG_WRITE = 1 << 1,
    BINARY_INFO_BLOCK_DEV_FLAG_REFORMAT = 1 << 2, // may be reformatted..

    BINARY_INFO_BLOCK_DEV_FLAG_PT_UNKNOWN = 0 << 4, // unknown free to look
    BINARY_INFO_BLOCK_DEV_FLAG_PT_MBR = 1 << 4, // expect MBR
    BINARY_INFO_BLOCK_DEV_FLAG_PT_GPT = 2 << 4, // expect GPT
    BINARY_INFO_BLOCK_DEV_FLAG_PT_NONE = 3 << 4, // no partition table
};
USB device descriptors

Seems like tagging these might be nice (we just need to store the pointer to it assuming - as is often the case - the descriptor is just a linear chunk of memory) … I assume there is a tool out there to prettify such a thing if picotool dumps the descriptor in binary.

Issues

If you ctrl+c out of the middle of a long operation, then libusb seems to get a bit confused, which means we aren’t able to unlock our lockout of USB MSD writes (we have turned them off so the user doesn’t step on their own toes). Simply running picotool info again will unlock it properly the next time (or you can reboot the device).

RP2040 Core Component

Contains startup code, crash handling and additional RP2040-specific support code.

Configuration variables
PICO_BOARD

default: pico

Select development board in use. List available boards with make list-boards.

The SDK defines various useful bits of information in a board header file, such as the default LED pin, how much flash memory it has, etc. Use make board-info to list these values.

If using custom hardware, select none and provide definitions as required.

ENABLE_BOOTSEL

default: 1 for debug, 0 for release builds

This setting is provided to make it easy to re-program RP2040 boards during development. When enabled, Sming monitors the BOOTSEL button and restartS in boot mode if pressed.

default: 1

The Pico-W board requires a ~140K (compressed) firmware BLOB which by default is linked into the application image.

This can be wasteful when using OTA as the firmware must be contained in all application images.

Setting this value to ‘0’ will omit firmware from the image, and instead load it from a partition called ‘cyw43_fw’. This partition can be added to the standard map using the ‘cyw43_fw’ HWCONFIG_OPT setting:

make LINK_CYW43_FIRMWARE=0 HWCONFIG_OPT=cyw43_fw

This is not the default setting since the additional partition must be managed by the end application.

PICO_DEBUG

default: 0

Set to 1 to enable additional debug output from compiled Pico SDK library. Shows things like WiFi firmware version.

References
Used by
Environment Variables
  • CYW43_FIRMWARE

  • CYW43_PIN_WL_HOST_WAKE

  • CYW43_PIN_WL_REG_ON

  • CYW43_USES_VSYS_PIN

  • CYW43_WL_GPIO_COUNT

  • CYW43_WL_GPIO_LED_PIN

  • CYW43_WL_GPIO_VBUS_PIN

  • DISABLE_NETWORK

  • DISABLE_WIFI

  • ENABLE_BOOTSEL

  • LINK_CYW43_FIRMWARE

  • PICO_BOARD

  • PICO_BOOT_STAGE2_CHOOSE_W25Q080

  • PICO_DEBUG

  • PICO_DEFAULT_I2C

  • PICO_DEFAULT_I2C_SCL_PIN

  • PICO_DEFAULT_I2C_SDA_PIN

  • PICO_DEFAULT_SPI

  • PICO_DEFAULT_SPI_CSN_PIN

  • PICO_DEFAULT_SPI_RX_PIN

  • PICO_DEFAULT_SPI_SCK_PIN

  • PICO_DEFAULT_SPI_TX_PIN

  • PICO_DEFAULT_UART

  • PICO_DEFAULT_UART_RX_PIN

  • PICO_DEFAULT_UART_TX_PIN

  • PICO_FLASH_SIZE_BYTES

  • PICO_FLASH_SPI_CLKDIV

  • PICO_RP2040_B0_SUPPORTED

  • PICO_RP2040_B1_SUPPORTED

  • PICO_VSYS_PIN

  • RASPBERRYPI_PICO_W

SoC support
  • rp2040

Submodule: pico-sdk
Raspberry Pi Pico SDK

The Raspberry Pi Pico SDK (henceforth the SDK) provides the headers, libraries and build system necessary to write programs for the RP2040-based devices such as the Raspberry Pi Pico in C, C++ or assembly language.

The SDK is designed to provide an API and programming environment that is familiar both to non-embedded C developers and embedded C developers alike. A single program runs on the device at a time and starts with a conventional main() method. Standard C/C++ libraries are supported along with C level libraries/APIs for accessing all of the RP2040’s hardware include PIO (Programmable IO).

Additionally the SDK provides higher level libraries for dealing with timers, synchronization, USB (TinyUSB) and multi-core programming along with various utilities.

The SDK can be used to build anything from simple applications, to fully fledged runtime environments such as MicroPython, to low level software such as RP2040’s on-chip bootrom itself.

Additional libraries/APIs that are not yet ready for inclusion in the SDK can be found in pico-extras.

Documentation

See Getting Started with the Raspberry Pi Pico for information on how to setup your hardware, IDE/environment and for how to build and debug software for the Raspberry Pi Pico and other RP2040-based devices.

See Connecting to the Internet with Raspberry Pi Pico W to learn more about writing applications for your Raspberry Pi Pico W that connect to the internet.

See Raspberry Pi Pico C/C++ SDK to learn more about programming using the SDK, to explore more advanced features, and for complete PDF-based API documentation.

See Online Raspberry Pi Pico SDK API docs for HTML-based API documentation.

Example code

See pico-examples for example code you can build.

Getting the latest SDK code

The master branch of pico-sdk on GitHub contains the latest stable release of the SDK. If you need or want to test upcoming features, you can try the develop branch instead.

Quick-start your own project

These instructions are extremely terse, and Linux-based only. For detailed steps, instructions for other platforms, and just in general, we recommend you see Raspberry Pi Pico C/C++ SDK

  1. Install CMake (at least version 3.13), and GCC cross compiler

    sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib
    
  2. Set up your project to point to use the Raspberry Pi Pico SDK

    • Either by cloning the SDK locally (most common) :

      1. git clone this Raspberry Pi Pico SDK repository

      2. Copy pico_sdk_import.cmake from the SDK into your project directory

      #. Set PICO_SDK_PATH to the SDK location in your environment, or pass it (-DPICO_SDK_PATH=) to cmake later. #.

      Setup a CMakeLists.txt like:

      cmake_minimum_required(VERSION 3.13)
      
      # initialize the SDK based on PICO_SDK_PATH
      # note: this must happen before project()
      include(pico_sdk_import.cmake)
      
      project(my_project)
      
      # initialize the Raspberry Pi Pico SDK
      pico_sdk_init()
      
      # rest of your project
      
    • Or with the Raspberry Pi Pico SDK as a submodule :

      #. Clone the SDK as a submodule called pico-sdk #.

      Setup a CMakeLists.txt like:

      cmake_minimum_required(VERSION 3.13)
      
      # initialize pico-sdk from submodule
      # note: this must happen before project()
      include(pico-sdk/pico_sdk_init.cmake)
      
      project(my_project)
      
      # initialize the Raspberry Pi Pico SDK
      pico_sdk_init()
      
      # rest of your project
      
    • Or with automatic download from GitHub :

      1. Copy pico_sdk_import.cmake from the SDK into your project directory

      2. Setup a CMakeLists.txt like:

        cmake_minimum_required(VERSION 3.13)
        
        # initialize pico-sdk from GIT
        # (note this can come from environment, CMake cache etc)
        set(PICO_SDK_FETCH_FROM_GIT on)
        
        # pico_sdk_import.cmake is a single file copied from this SDK
        # note: this must happen before project()
        include(pico_sdk_import.cmake)
        
        project(my_project)
        
        # initialize the Raspberry Pi Pico SDK
        pico_sdk_init()
        
        # rest of your project
        
    • Or by cloning the SDK locally, but without copying pico_sdk_import.cmake:

      #. git clone this Raspberry Pi Pico SDK repository #.

      Setup a CMakeLists.txt like:

      cmake_minimum_required(VERSION 3.13)
      
      # initialize the SDK directly
      include(/path/to/pico-sdk/pico_sdk_init.cmake)
      
      project(my_project)
      
      # initialize the Raspberry Pi Pico SDK
      pico_sdk_init()
      
      # rest of your project
      
  3. Write your code (see pico-examples or the Raspberry Pi Pico C/C++ SDK documentation for more information)

    About the simplest you can do is a single source file (e.g. hello_world.c)

    #include <stdio.h>
    #include "pico/stdlib.h"
    
    int main() {
        setup_default_uart();
        printf("Hello, world!\n");
        return 0;
    }
    

    And add the following to your CMakeLists.txt:

    add_executable(hello_world
        hello_world.c
    )
    
    # Add pico_stdlib library which aggregates commonly used features
    target_link_libraries(hello_world pico_stdlib)
    
    # create map/bin/hex/uf2 file in addition to ELF.
    pico_add_extra_outputs(hello_world)
    

    Note this example uses the default UART for stdout; if you want to use the default USB see the hello-usb example.

  4. Setup a CMake build directory.

    For example, if not using an IDE:

    $ mkdir build
    $ cd build
    $ cmake ..
    

    When building for a board other than the Raspberry Pi Pico, you should pass -DPICO_BOARD=board_name to the cmake command above, e.g. cmake -DPICO_BOARD=pico_w .. to configure the SDK and build options accordingly for that particular board.

    Doing so sets up various compiler defines (e.g. default pin numbers for UART and other hardware) and in certain cases also enables the use of additional libraries (e.g. wireless support when building for PICO_BOARD=pico_w) which cannot be built without a board which provides the requisite functionality.

    For a list of boards defined in the SDK itself, look in this directory which has a header for each named board.

  5. Make your target from the build directory you created.

    $ make hello_world
    
  6. You now have hello_world.elf to load via a debugger, or hello_world.uf2 that can be installed and run on your Raspberry Pi Pico via drag and drop.

Rp2040 SPI Flash Support

Provides functions for access to flash memory.

API Documentation
enum SPIFlashMode

Values:

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_FAST_READ
enumerator MODE_SLOW_READ
enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enum SPIFlashSpeed

Values:

enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enum SPIFlashSize

Values:

enumerator SIZE_1MBIT
enumerator SIZE_2MBIT
enumerator SIZE_4MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT

Not listed.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

enum SPIFlashMode

Values:

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_FAST_READ
enumerator MODE_SLOW_READ
enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enum SPIFlashSpeed

Values:

enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enum SPIFlashSize

Values:

enumerator SIZE_1MBIT
enumerator SIZE_2MBIT
enumerator SIZE_4MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT

Not listed.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

enum SPIFlashMode

Values:

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_FAST_READ
enumerator MODE_SLOW_READ
enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enumerator MODE_QIO
enumerator MODE_QOUT
enumerator MODE_DIO
enumerator MODE_DOUT
enumerator MODE_SLOW_READ

Not supported.

enumerator MODE_FAST_READ

Not supported.

enum SPIFlashSpeed

Values:

enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enumerator SPEED_40MHZ
enumerator SPEED_26MHZ
enumerator SPEED_20MHZ
enumerator SPEED_80MHZ
enum SPIFlashSize

Values:

enumerator SIZE_1MBIT
enumerator SIZE_2MBIT
enumerator SIZE_4MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT

Not listed.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

enumerator SIZE_4MBIT
enumerator SIZE_2MBIT
enumerator SIZE_8MBIT
enumerator SIZE_16MBIT
enumerator SIZE_32MBIT
enumerator SIZE_1MBIT

Not supported.

static inline uint32_t flashmem_get_address(const void *memptr)

Obtain the flash memory address for a memory pointer.

Note

If memptr is not in valid flash memory it will return an offset which exceeds the internal flash memory size.

Note

The flash location is dependent on where rBoot has mapped the firmware.

Parameters:

memptr

Return values:

uint32_t – Offset from start of flash memory

uint32_t flashmem_write(const void *from, uint32_t toaddr, uint32_t size)

Write a block of data to flash.

Note

None of the parameters need to be aligned

Parameters:
  • from – Buffer to obtain data from

  • toaddr – Flash location to start writing

  • size – Number of bytes to write

Return values:

uint32_t – Number of bytes written

uint32_t flashmem_read(void *to, uint32_t fromaddr, uint32_t size)

Read a block of data from flash.

Note

none of the parameters need to be aligned

Parameters:
  • to – Buffer to store data

  • fromaddr – Flash location to start reading

  • size – Number of bytes to read

Return values:

uint32_t – Number of bytes written

bool flashmem_erase_sector(uint32_t sector_id)

Erase a single flash sector.

Parameters:

sector_id – the sector to erase

Return values:

true – on success

SPIFlashInfo flashmem_get_info()

Get flash memory information block.

Return values:

SPIFlashInfo – Information block

uint8_t flashmem_get_size_type()

Returns a number indicating the size of flash memory chip.

Return values:

uint8_t – See SpiFlashInfo.size field for possible values

uint32_t flashmem_get_size_bytes()

get the total flash memory size

Return values:

uint32_t – Size in bytes

uint16_t flashmem_get_size_sectors()

Get the total number of flash sectors.

Return values:

uint16_t – Sector count

uint32_t flashmem_find_sector(uint32_t address, uint32_t *pstart, uint32_t *pend)

Helper function: find the flash sector in which an address resides.

Note

Optional parameters may be null

Parameters:
  • address

  • pstart – OUT/OPTIONAL: Start of sector containing the given address

  • pend – OUT/OPTIONAL: Last address in sector

Return values:

uint32_t – Sector number for the given address

uint32_t flashmem_get_sector_of_address(uint32_t addr)

Get sector number containing the given address.

Parameters:

addr

Return values:

uint32_t – sector number

uint32_t spi_flash_get_id(void)
uint32_t flashmem_write_internal(const void *from, uint32_t toaddr, uint32_t size)

write to flash memory

Note

All parameters MUST be aligned to 4-byte word boundaries, including the RAM pointer

Parameters:
  • from – Buffer to read data from - MUST be word-aligned

  • toaddr – Flash address (offset) to write to - MUST be word-aligned

  • size – Number of bytes to write - MUST be word-aligned

Return values:

uint32_t – Number of bytes actually written

uint32_t flashmem_read_internal(void *to, uint32_t fromaddr, uint32_t size)

Read from flash memory.

Note

All parameters MUST be aligned to 4-byte word boundaries, including the RAM pointer

Parameters:
  • to – Buffer to store data - MUST be word-aligned

  • fromaddr – Flash address (offset) to read from - MUST be word-aligned

  • size – Number of bytes to read - MUST be word-aligned

Return values:

uint32_t – Number of bytes actually read

uint32_t flashmem_get_first_free_block_address()
void flashmem_sfdp_read(uint32_t addr, void *buffer, size_t count)
INTERNAL_FLASH_WRITE_UNIT_SIZE

Flash memory access must be aligned and in multiples of 4-byte words.

INTERNAL_FLASH_READ_UNIT_SIZE
FLASH_TOTAL_SEC_COUNT
SYS_PARAM_SEC_COUNT

Number of flash sectors reserved for system parameters at start.

FLASH_WORK_SEC_COUNT
INTERNAL_FLASH_SECTOR_SIZE
INTERNAL_FLASH_SIZE
INTERNAL_FLASH_WRITE_UNIT_SIZE

Flash memory access must be aligned and in multiples of 4-byte words.

INTERNAL_FLASH_READ_UNIT_SIZE
FLASH_TOTAL_SEC_COUNT
SYS_PARAM_SEC_COUNT

Number of flash sectors reserved for system parameters at start.

FLASH_WORK_SEC_COUNT
INTERNAL_FLASH_SECTOR_SIZE
INTERNAL_FLASH_SIZE
INTERNAL_FLASH_START_ADDRESS
FLASH_TOTAL_SEC_COUNT
SYS_PARAM_SEC_COUNT

Number of flash sectors reserved for system parameters at start.

FLASH_WORK_SEC_COUNT
INTERNAL_FLASH_SECTOR_SIZE
INTERNAL_FLASH_SIZE
INTERNAL_FLASH_START_ADDRESS
struct SPIFlashInfo
#include <esp_spi_flash.h>

SPI Flash memory information block. Copied from bootloader header. See esp_image_header_t.

SPI Flash memory information block. Stored at the beginning of flash memory.

Public Members

uint8_t mode

SPIFlashMode.

uint8_t speed

SPIFlashSpeed.

uint8_t size

SPIFlashSize.

struct STORE_TYPEDEF_ATTR
#include <esp_spi_flash.h>

SPI Flash memory information block. Stored at the beginning of flash memory.

Public Members

uint8_t mode

SPIFlashMode.

uint8_t speed

SPIFlashSpeed.

uint8_t size

SPIFlashSize.

References
Used by
SoC support
  • rp2040

UF2 Support

Provides support for converting binary code into UF2 format for uploading to RP2040 devices.

USB Flashing Format (UF2)

UF2 is a file format, developed by Microsoft for PXT (also known as Microsoft MakeCode), that is particularly suitable for flashing microcontrollers over MSC (Mass Storage Class; aka removable flash drive).

For a more friendly explanation, check out this blog post. Also, take a look at the list of implementations at the bottom of this document.

Overview

The UF2 file consists of 512 byte blocks, each of which is self-contained and independent of others. Each 512 byte block consists of (see below for details):

  • magic numbers at the beginning and at the end

  • address where the data should be flashed

  • up to 476 bytes of data

The data transfers over MSC always arrive in multiples of 512 bytes. Together with the FAT file system structure, this means that blocks of the UF2 file are always aligned with the MSC writes - the microcontroller never gets a partial file.

The magic numbers let the microcontroller distinguish an UF2 file block from other data (eg., FAT table entry, or various book-keeping files stored by some operating systems). When a UF2 block is recognized, it can be flashed immediately (unless flash page size is more than 256 bytes; in that case a buffer is needed). The actual handling of file format during writing is very simple (~10 lines of C code in simplest version).

File format

A UF2 file consists of 512 byte blocks. Each block starts with a 32 byte header, followed by data, and a final magic number. All fields, except for data, are 32 bit unsigned little endian integers.

Offset

Size

Value

0

4

First magic number, 0x0A324655 ("UF2\n")

4

4

Second magic number, 0x9E5D5157

8

4

Flags

12

4

Address in flash where the data should be written

16

4

Number of bytes used in data (often 256)

20

4

Sequential block number; starts at 0

24

4

Total number of blocks in file

28

4

File size or board family ID or zero

32

476

Data, padded with zeros

508

4

Final magic number, 0x0AB16F30

The following C struct can be used:

struct UF2_Block {
    // 32 byte header
    uint32_t magicStart0;
    uint32_t magicStart1;
    uint32_t flags;
    uint32_t targetAddr;
    uint32_t payloadSize;
    uint32_t blockNo;
    uint32_t numBlocks;
    uint32_t fileSize; // or familyID;
    uint8_t data[476];
    uint32_t magicEnd;
} UF2_Block;
Flags

Currently, there are five flags defined:

  • 0x00000001 - not main flash - this block should be skipped when writing the device flash; it can be used to store “comments” in the file, typically embedded source code or debug info that does not fit on the device flash

  • 0x00001000 - file container - see below

  • 0x00002000 - familyID present - when set, the fileSize/familyID holds a value identifying the board family (usually corresponds to an MCU)

  • 0x00004000 - MD5 checksum present - see below

  • 0x00008000 - extension tags present - see below

Family ID

This field is optional, and should be set only when the corresponding flag is set. It is recommended that new bootloaders require the field to be set appropriately, and refuse to flash UF2 files without it. If you’re developing your own bootloader, and your board family isn’t listed here, pick a new family ID at random. It’s good to also send a PR here, so your family can be listed.

If the familyID doesn’t match, the bootloader should disregard the entire block, including blockNo and numBlocks fields. In particular, writing a full UF2 file with non-matching familyID should not reset the board. This also allows for several files with different familyID to be simply concatenated together, and the whole resulting file to be copied to the device with only one actually being written to flash.

Picking numbers at random

The reason to pick numbers at random is to minimize risk of collisions in the wild. Do not pick random numbers by banging on keyboard, or by using 0xdeadf00d, 0x42424242 etc. A good way is to use the following shell command: printf "0x%04x%04x\n" $RANDOM $RANDOM Another good way is the link at the bottom of https://microsoft.github.io/uf2/patcher/ This procedure was unfortunately not used for the SAMD51 and NRF52840 below.

Family list

The current master list of family IDs is maintained in a JSON file.

Rationale

The magic number at the end is meant to mitigate partial block writes.

Second and final magic numbers were randomly selected, except for the last byte of final magic number, which was forced to be '\n' (0xA). Together with the first magic number being "UF2\n" this makes it easy to identify UF2 blocks in a text editor.

The header is padded to 32 bytes, as hex editors commonly use 16 or 32 bytes as line length. This way, the data payload is aligned to line start.

32 bit integers are used for all fields so that large flash sizes can be supported in future, as well as for simplicity. Little endian is used, as most microcontrollers are little endian. 8 bit microcontrollers can choose to just use the first 16 bits of various header fields.

The total number of blocks in the file and the sequential block number make it easy for the bootloader to detect that all blocks have been transferred. It requires one bit of memory per block (eg., on SAMD21G18A it’s 128 bytes). Alternatively, the bootloader might ignore that and just implement a reset after say 1 second break in incoming UF2 blocks.

Payload sizes

The number of data bytes is configurable and depends on the size of the flash page (that is the smallest size that can be erased) on the microcontroller.

  • if the page size is more than 476 bytes, the bootloader should support any payload size, as it needs to buffer the entire page in memory anyway

  • if the page size is less than 476 bytes, the payload should be a multiple of page size, so it can be written without buffering; the target address should also be a multiple of page size

In any event, payload size and target address should always be 4-byte aligned.

Note that payload size of 256 is always correct, and makes it easy to convert between flash addresses and UF2 file offsets.

For example, on Atmel’s SAMD21 chips the page size is 256 bytes, and this also is the payload size. If the page size was 128 bytes, one could use payload of 128*3. Nordic nRF51 has page size of 1024 bytes, and thus any payload size should be allowed.

Embedding sources

Some IDEs will embed program sources in the UF2 file. This allows a UF2 files to be loaded by the IDE and serve as a natural backup and transfer format. This can be done in two ways:

  • using the “not main flash” flag

  • using normal blocks that are flashed to the device

If the bootloader can expose CURRENT.UF2 file (see below) and there is enough flash available, than the second option is more desirable, as it allows sharing programs directly from the board.

See https://makecode.com/source-embedding for more info.

Robustness

The file format is designed specifically to deal with the following problems:

  • operating system (OS) writing blocks in different order than occurs in a file

  • OS writing blocks multiple times

  • OS writing data that is not UF2 blocks

  • OS writing first/final part of a block, possibly for metadata detection or search indexing

The only file system assumption we make is that blocks of file are aligned with blocks on the hard drive. It’s likely true of many file systems besides FAT.

We also assume that USB MSC device reports its block size to be a multiple of 512 bytes. In the wild these devices always almost report exactly 512, and some operating systems do not support other values.

Files exposed by bootloaders

Bootloaders may expose virtual files in their MSC devices. These are standardized here, so that flashing tools can automatically detect the bootloaders.

  • INFO_UF2.TXT - contains information about the bootloader build and the board on which it is running

  • INDEX.HTM - redirects to a page that contains an IDE or other information

  • CURRENT.UF2 - the contents of the entire flash of the device, starting at 0x00000000, with 256 payload size; thus, the size of this file will report as twice the size of flash

Flashing tools can use the presence of INFO_UF2.TXT (in upper or lower case, as FAT is case-insensitive) file as an indication that a given directory is actually a connected UF2 board. The other files should not be used for detection.

Typical INFO_UF2.TXT file looks like this:

UF2 Bootloader v1.1.3 SFA
Model: Arduino Zero
Board-ID: SAMD21G18A-Zero-v0

The Board-ID field is machine-readable and consists of a number of dash-separated tokens. The first token is the CPU type, second is the board type, and third is the board revision. More tokens can be also added.

The bootloader should contain its info file as a static string somewhere in its code. If possible, the last word of the bootloader code should point to this string. This way, the info file can be found in the initial section of the CURRENT.UF2 file as well. Thus, a board type can be determined from the contents of CURRENT.UF2. This is particularly useful with the source embedding (see above).

File containers

It is also possible to use the UF2 format as a container for one or more regular files (akin to a TAR file, or ZIP archive without compression). This is useful when the embedded device being flashed sports a file system.

The program to run may reside in one of the files, or in the main flash memory.

In such a usage the file container flag is set on blocks, the field fileSize holds the file size of the current file, and the field targetAddr holds the offset in current file.

The not main flash flag on blocks should be ignored when the file container is set.

The file name is stored at &data[payloadSize] (ie., right after the actual payload) and terminated with a 0x00 byte. The format of filename is dependent on the bootloader (usually implemented as some sort of file system daemon).

The bootloader will usually allow any size of the payload.

The current files on device might be exposed as multiple UF2 files, instead of a single CURRENT.UF2. They may reside in directories, however, due to UF2 general design, it doesn’t matter which directory the UF2 file is written to.

Typical writing procedure is as follows:

  • validate UF2 magic numbers

  • make sure that targetAddr < fileSize and that fileSize isn’t out of reasonable range

  • write 0x00 at data[475] to ensure NUL termination of file name

  • read file name from &data[payloadSize]; perform any mapping on the file name

  • create a directory where the file is to be written if it doesn’t exist

  • open the file for writing

  • truncate the file to fileSize

  • seek targetAddr

  • write the payload (ie., data[0 ... payloadSize - 1])

  • close the file

The fields blockNo and numBlocks refer to the entire UF2 file, not the current file.

MD5 checksum

When the 0x4000 flag is set, the last 24 bytes of data[] hold the following structure:

Offset

Size

Value

0

4

Start address of region

4

4

Length of region in bytes

8

16

MD5 checksum in binary format

The flashing program should compute the MD5 sum of the specified region. If the region checksum matches, flashing of the current block can be skipped. Typically, many blocks in sequence will have the same region specified, and can all be skipped, if the matching succeeded. The position of the current block will typically be inside of the region. The position and size of the region should be multiple of page erase size (4k or 64k on typical SPI flash).

This is currently only used on ESP32, which is also why MD5 checksum is used.

Extension tags

When the 0x8000 flag is set, additional information can be appended right after payload data (i.e., it starts at 32 + payloadSize). Every tag starts at 4 byte boundary. The first byte of tag contains its total size in bytes (including the size byte and type designation). The next three bytes designate the type of tag (if you want to define custom tags, pick them at random). The last tag has size of 0 and type of 0.

Standard tag designations follow:

  • 0x9fc7bc - version of firmware file - UTF8 semver string

  • 0x650d9d - description of device for which the firmware file is destined (UTF8)

  • 0x0be9f7 - page size of target device (32 bit unsigned number)

  • 0xb46db0 - SHA-2 checksum of firmware (can be of various size)

  • 0xc8a729 - device type identifier - a refinement of familyID meant to identify a kind of device (eg., a toaster with specific pinout and heating unit), not only MCU; 32 or 64 bit number; can be hash of 0x650d9d

For example, the following bytes encode firmware version 0.1.2 for device named ACME Toaster mk3 (line breaks added for clarity):

09 bc c7 9f 30 2e 31 2e 32 00 00 00
14 9d 0d 65 41 43 4d 45 20 54 6f 61 73 74 65 72 20 6d 6b 33
00 00 00 00

Extension tags can, but don’t have to, be repeated in all blocks.

Implementations
Bootloaders

There’s an ongoing effort to implement UF2 in Codal.

Editors
Libraries
License

MIT

Code of Conduct

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.

uf2conv – Packing and unpacking UF2 files
SYNOPSIS

This tool is based on the code at https://github.com/Microsoft/uf2 but has been modified for use with Sming and the RP2040:

- Handles multiple binary inputs using addr=content format
- Locations specified as offsets, relative to base address
- Defaults are set for RP2040, but can be changed (family ID and base address)
- Content is padded to flash sector size (4096 bytes) - required by RP2040 bootrom
- Content is padded with 0xff (not 0) as this represents erased (unprogrammed) flash location
- Renamed 'deploy' as 'upload' and added progress indicator
- Support for hex files and 'C' arrays removed

Run uf2conv.py -h for full list of options.

EXAMPLES
Pack binary file(s) to .uf2

Specify source using offset=content values:

uf2conv.py --convert 0x2000=cpx/firmware.bin 0x4000=cpx/partitions.bin --output cpx/upload.uf2

Offsets are relative to the start of flash memory.

Unpack a .uf2 file

uf2conv.py current.uf2 --output current.bin

Produces a list of current-{addr}.bin files for each chunk found in the .uf2 source file. Without the --output option just displays a chunk summary.

Use the --verbose option to display details for every block in the file.

Configuration variables
UF2CONV_PY

Path to the uf2conv utility should you wish to run it manually.

References
Used by
Environment Variables
SoC support
  • rp2040

Framework

Timers and Clocks

Hardware timers

The ESP8266 has one of these, HardwareTimer, which can be programmed to trigger when a pre-programmed interval has elapsed. The callback handler is executed in an interrupt context so is restricted on what it can do.

The timer is appropriate where accurate and short timing is required.

The API for hardware (and Software timer queues timers) is identical, implemented using a CallbackTimer class template for best performance.

Note

Applies to Esp8266 architecture only.

As with all Callback timers, the HardwareTimer can be one-shot or repeating.

With the Esp8266 a one-shot timer will repeat after it has been triggered, but only after the timer counter wraps around. The Esp8266 only has 23 bits of resolution so with a clock divider of 16 this will happen after about 1.7s.

Because the Esp8266 lacks any proper PWM hardware the timer latency is critical. Adding any additional code to the callback is therefore left to the programmer.

group hardware_timer

Hardware callback timer.

Typedefs

template<hw_timer_clkdiv_t clkdiv = TIMER_CLKDIV_16, HardwareTimerMode mode = eHWT_NonMaskable>
using HardwareTimer1 = CallbackTimer<Timer1Api<clkdiv, mode>>

Hardware Timer class template with selectable divider and interrupt mode.

using HardwareTimer = HardwareTimer1<>

Default hardware Timer class.

Enums

enum HardwareTimerMode

Hardware Timer interrupt mode.

Values:

enumerator eHWT_Maskable
enumerator eHWT_NonMaskable

Variables

static uint8_t state
static TickType interval
template<hw_timer_clkdiv_t clkdiv, HardwareTimerMode mode>
class Timer1Api : public CallbackTimerApi<Timer1Api<clkdiv, mode>>
#include <HardwareTimer.h>

Class template for Timer1 API.

Note

Provides low-level interface for timer access

Software timer queues

Sming has one timer queue which is managed via the Timer and SimpleTimer classes.

Like hardware timers, these are callback timers, but the callback handlers execute in the task context so there are no special restrictions on what you can do there.

Because of this, however, these timers are less precise hardware timers, and the accuracy generally depends on how busy the CPU is.

Timers can be ‘one-shot’, for timing single events, or ‘auto-reset’ repetitive timers. A repetitive timer will ensure that time intervals between successive callbacks are consistent.

Timer

The Timer class is the most flexible way to use software timers, supporting extended time intervals and delegate callback functions so you can use it with class methods, capturing lambdas, etc.

group timer

Extended timer queue class.

Functions

void setInterval(TickType interval)
void longTick()
template<class TimerClass>
class OsTimer64Api : public CallbackTimerApi<OsTimer64Api<TimerClass>>
#include <Timer.h>

Class template implementing an extended OS Timer with 64-bit microsecond times and delegate callback support.

template<typename TimerApi>
class DelegateCallbackTimer : public CallbackTimer<TimerApi>
#include <Timer.h>

Class template adding delegate callback method support to the basic CallbackTimer template.

Public Functions

template<TimeType microseconds>
inline DelegateCallbackTimer &initializeUs(TimerDelegate delegateFunction)

Initialise timer in microseconds, with static check.

Template Parameters:

microsecondsTimer interval in microseconds

Parameters:

delegateFunction – Function to call when timer triggers

Return values:

ExtendedCallbackTimer& – Reference to timer

template<uint32_t milliseconds>
inline DelegateCallbackTimer &initializeMs(TimerDelegate delegateFunction)

Initialise hardware timer in milliseconds, with static check.

Template Parameters:

millisecondsTimer interval in milliseconds

Parameters:

delegateFunction – Function to call when timer triggers

Return values:

ExtendedCallbackTimer& – Reference to timer

inline DelegateCallbackTimer &initializeMs(uint32_t milliseconds, TimerDelegate delegateFunction)

Initialise millisecond timer.

Note

Delegate callback method

Parameters:
  • milliseconds – Duration of timer in milliseconds

  • delegateFunction – Function to call when timer triggers

inline DelegateCallbackTimer &initializeUs(uint32_t microseconds, TimerDelegate delegateFunction)

Initialise microsecond timer.

Note

Delegate callback method

Parameters:
  • microseconds – Duration of timer in milliseconds

  • delegateFunction – Function to call when timer triggers

inline void setCallback(TimerDelegate delegateFunction)

Set timer trigger function using Delegate callback method.

Note

Don’t use this for interrupt timers

Parameters:

delegateFunction – Function to be called on timer trigger

class Timer : public DelegateCallbackTimer<OsTimer64Api<Timer>>
#include <Timer.h>

Callback timer class.

Subclassed by Profiling::MinMaxTimes< Timer >

class AutoDeleteTimer : public DelegateCallbackTimer<OsTimer64Api<AutoDeleteTimer>>
#include <Timer.h>

Auto-delete callback timer class.

Simple Timer

The SimpleTimer class only supports regular function callbacks, but if you don’t need the additional functionality that a regular Timer offers then it will save some RAM.

group simple_timer

Basic timer queue class.

Typedefs

using SimpleTimer = CallbackTimer<OsTimerApi>

Basic callback timer.

Note

For delegate callback support and other features see Timer class.

class OsTimerApi : public CallbackTimerApi<OsTimerApi>
#include <SimpleTimer.h>

Implements common system callback timer API.

Subclassed by CallbackTimer< OsTimerApi >

Callback Timer API
template<typename TimerApi>
class CallbackTimer : protected TimerApi

Callback timer class template.

Note

Methods return object reference for Method Chaining http://en.wikipedia.org/wiki/Method_chaining This class template provides basic C-style callbacks for best performance

Template Parameters:

TimerApi – The physical timer implementation

Subclassed by DelegateCallbackTimer< OsTimer64Api< AutoDeleteTimer > >, DelegateCallbackTimer< OsTimer64Api< Timer > >, DelegateCallbackTimer< TimerApi >

Public Functions

template<NanoTime::Unit unit, TimeType time>
inline CallbackTimer &initialize(TimerCallback callback, void *arg = nullptr)

Initialise timer with an interval (static check) and callback.

Note

If interval out of range compilation will fail with error

Template Parameters:
  • unit – Time unit for interval

  • timeTimer interval

Parameters:
  • callback – Callback function to call when timer triggers

  • arg – Optional argument passed to callback

Return values:

CallbackTimer& – Reference to timer

template<NanoTime::Unit unit>
inline CallbackTimer &initialize(TimeType time, TimerCallback callback, void *arg = nullptr)

Initialise timer with an interval and callback.

Template Parameters:

unit – Time unit for interval

Parameters:
  • timeTimer interval

  • callback – Callback function to call when timer triggers

  • arg – Optional argument passed to callback

Return values:

CallbackTimer& – Reference to timer

template<TimeType microseconds>
inline CallbackTimer &initializeUs(TimerCallback callback, void *arg = nullptr)

Initialise timer in microseconds (static check) with Timer Callback and optional argument.

template<TimeType microseconds>
inline CallbackTimer &initializeUs(InterruptCallback callback = nullptr)

Initialise timer in microseconds (static check) with optional Interrupt Callback (no argument)

inline CallbackTimer &initializeUs(TimeType microseconds, TimerCallback callback, void *arg = nullptr)

Initialise timer in microseconds with Timer Callback and optional argument.

inline CallbackTimer &initializeUs(TimeType microseconds, InterruptCallback callback = nullptr)

Initialise timer in microseconds with optional Interrupt Callback (no arg)

template<uint32_t milliseconds>
inline CallbackTimer &initializeMs(TimerCallback callback, void *arg = nullptr)

Initialise hardware timer in milliseconds (static check) with Timer Callback and optional argument.

template<uint32_t milliseconds>
inline CallbackTimer &initializeMs(InterruptCallback callback = nullptr)

Initialise hardware timer in milliseconds (static check) and optional Interrupt Callback (no arg)

inline CallbackTimer &initializeMs(uint32_t milliseconds, TimerCallback callback, void *arg = nullptr)

Initialise hardware timer in milliseconds with Timer Callback and optional argument.

inline CallbackTimer &initializeMs(uint32_t milliseconds, InterruptCallback callback = nullptr)

Initialise hardware timer in milliseconds with optional Interrupt Callback (no arg)

inline bool start(bool repeating = true)

Start timer running.

Parameters:

repeating – True to restart timer when it triggers, false for one-shot (Default: true)

Return values:

bool – True if timer started

inline bool startOnce()

Start one-shot timer.

Note

Timer starts and will run for configured period then stop

Return values:

bool – True if timer started

inline void stop()

Stops timer.

inline bool restart()

Restart timer.

Note

Timer is stopped then started with current configuration

Return values:

bool – True if timer started

inline bool isStarted() const

Check if timer is started.

Return values:

bool – True if started

inline NanoTime::Time<TimeType> getIntervalUs() const

Get timer interval in microseconds.

inline NanoTime::Time<uint32_t> getIntervalMs() const

Get timer interval in milliseconds.

inline TickType getInterval() const

Get timer interval in clock ticks.

inline bool checkInterval(TickType ticks) const

Check timer interval is valid.

Parameters:

ticks – Interval to check

Return values:

bool – true if interval is within acceptable range for this timer

inline bool setInterval(TickType ticks)

Set timer interval in timer ticks.

Parameters:

ticks – Interval in timer ticks

template<TimeType ticks>
inline void setInterval()

Set timer interval in timer ticks (static check)

Note

On error, compilation fails with error message

Template Parameters:

ticks – Interval in ticks

template<NanoTime::Unit unit, TimeType time>
inline void setInterval()

Set timer interval in specific time unit (static check)

Note

On error, compilation fails with error message

Template Parameters:
  • unit

  • time – Interval to set

template<NanoTime::Unit unit>
inline bool setInterval(TimeType time)

Set timer interval in timer ticks.

Template Parameters:

unit

Parameters:

time – Interval in given units

inline bool setIntervalUs(TimeType microseconds)

Set timer interval in microseconds.

template<TimeType microseconds>
inline void setIntervalUs()

Set timer interval in microseconds (static check)

inline bool setIntervalMs(uint32_t milliseconds)

Set timer interval in milliseconds.

template<uint32_t milliseconds>
inline void setIntervalMs()

Set timer interval in milliseconds (static check)

inline void setCallback(TimerCallback callback, void *arg = nullptr)

Set timer trigger callback.

Parameters:
  • callback – Function to call when timer triggers

  • arg – Optional argument passed to callback

inline void setCallback(InterruptCallback callback)

Set timer trigger callback.

Note

Provided for convenience where callback argument not required

Parameters:

callback – Function to call when timer triggers

Public Static Functions

static inline constexpr Millis millis()

Get a millisecond time source.

static inline constexpr Micros micros()

Get a microsecond time source.

template<uint64_t us>
static inline constexpr uint64_t usToTicks()

Convert microsecond count into timer ticks.

static inline TickType usToTicks(TimeType time)

Convert microsecond count into timer ticks.

template<uint64_t ticks>
static inline constexpr uint64_t ticksToUs()

Convert timer ticks into microseconds.

static inline TimeType ticksToUs(TickType ticks)

Convert timer ticks into microseconds.

template<uint64_t ticks>
static inline constexpr void checkInterval()

Check timer interval in ticks is valid (static check)

Note

On error, compilation fails with error message

Template Parameters:

ticksTimer interval to check

template<NanoTime::Unit unit, uint64_t time>
static inline constexpr void checkInterval()

Check timer interval in specific time unit is valid (static check)

Note

On error, compilation fails with error message

Template Parameters:
  • unit – Time unit for interval

  • time – Interval to check

template<uint64_t milliseconds>
static inline constexpr void checkIntervalMs()

Check timer interval in milliseconds is valid (static check)

template<uint64_t microseconds>
static inline constexpr void checkIntervalUs()

Check timer interval in microseconds is valid (static check)

Polled timers

Polled timers can be used to measure elapsed time intervals or to check for timeouts within code loops. Traditionally one might do this as follows:

const unsigned TIMEOUT_MS = 100;

unsigned start = millis();
while(millis() - start < TIMEOUT_MS) {
   // do some stuff
}
unsigned elapsed = millis() - start;
Serial.print("Elapsed time: ");
Serial.print(elapsed);
Serial.println("ms");

Note

A common source of bugs when hand-coding such loops can be illustrated by this alternative:

unsigned timeout = millis() + TIMEOUT_MS;
while(millis() < timeout) {
   // do some stuff
}

At first glance this looks better than the first approach as we don’t have a subtraction within the loop. However, when millis() exceeds MAXINT - TIMEOUT_MS the timeout calculation will wrap and the loop will never get executed. This takes a little under 25 days, but with microseconds it’ll happen in less than an hour. This may not be the desired behaviour.

It’s generally safer and easier to use a PolledTimer:

OneShotFastMs timer(TIMEOUT_MS);
while(!timer.expired()) {
   // do some stuff
}
auto elapsed = timer.elapsedTime(); // Returns a `NanoTime::Time` object, value with time units
Serial.print("Elapsed time: ");
Serial.println(elapsed.toString()); // Includes units in the elapsed time interval
// Show time rounded to nearest whole seconds
Serial.println(elapsed.as<NanoTime::Seconds>().toString());

If you prefer to use microseconds, use OneShotFastUs instead or specify the units directly:

OneShotElapseTimer<NanoTime::Microseconds> timer;

Another advantage of polled timers is speed. Every call to millis (or micros) requires a calculation from clock ticks into milliseconds (or microseconds). It’s also a function call.

Polled timers measure time using hardware clock ticks, and query the hardware timer register directly without any function calls or calculations. This makes them a much better choice for tight timing loops.

Here’s the output from the BenchmarkPolledTimer module:

How many loop iterations can we achieve in 100 ms ?
Using millis(), managed 55984 iterations, average loop time = 1786ns (143 CPU cycles)
Using micros(), managed 145441 iterations, average loop time = 688ns (55 CPU cycles)
Using PolledTimer, managed 266653 iterations, average loop time = 375ns (30 CPU cycles)
API Documentation
group polled_timer

Polled interval timers.

Unnamed Group

template<NanoTime::Unit unit>
using OneShotElapseTimer = PolledTimer::OneShot<PolledTimerClock, unit>
template<NanoTime::Unit unit>
using PeriodicElapseTimer = PolledTimer::Periodic<PolledTimerClock, unit>
using OneShotFastMs = OneShotElapseTimer<NanoTime::Milliseconds>
using PeriodicFastMs = PeriodicElapseTimer<NanoTime::Milliseconds>
using OneShotFastUs = OneShotElapseTimer<NanoTime::Microseconds>
using PeriodicFastUs = PeriodicElapseTimer<NanoTime::Microseconds>
using ElapseTimer = OneShotFastUs
template<NanoTime::Unit units>
using OneShotCpuCycleTimer = PolledTimer::OneShot<CpuCycleClockNormal, units>
template<NanoTime::Unit units>
using PeriodicCpuCycleTimer = PolledTimer::Periodic<CpuCycleClockNormal, units>
template<NanoTime::Unit units>
using OneShotCpuCycleTimerFast = PolledTimer::OneShot<CpuCycleClockFast, units>
template<NanoTime::Unit units>
using PeriodicCpuCycleTimerFast = PolledTimer::Periodic<CpuCycleClockFast, units>
using CpuCycleTimer = OneShotCpuCycleTimer<NanoTime::Nanoseconds>
using CpuCycleTimerFast = OneShotCpuCycleTimerFast<NanoTime::Nanoseconds>
typedef OneShotFastMs oneShotFastMs
typedef PeriodicFastMs periodicFastMs
typedef OneShotFastUs oneShotFastUs
typedef PeriodicFastUs periodicFastUs

Defines

POLLED_TIMER_MARGIN_US

Timer intervals are limited to the maximum clock time, minus this safety margin.

Note

Specified in microseconds, this is the minimum timer poll interval to ensure no missed polls across the full timer range. Larger margin means smaller time range.

Timer range

The maximum interval for a timer varies depending on the clock source and the selected prescaler. The count may be further restricted by hardware. For example, Timer1 only provides a 23-bit count which means with a /16 prescaler (the default for HardwareTimer) it overflows after only 1.67 seconds.

It’s therefore important to check that timers are being used within their valid range. There are generally two ways to do this:

Runtime checks

This means checking function/method return values and acting accordingly. This often gets omitted because it can lead to cluttered code, which then leads to undiagnosed bugs creeping in which can be very difficult to track down later on.

With a polled timer, you’d use one of these methods to set the time interval:

bool reset(const TimeType& timeInterval);
bool resetTicks(const TimeType& interval);

They both return true on success.

Static checks

These checks are performed during code compilation, so if a check fails the code won’t compile. In regular ‘C’ code you’d do this using #if statements, but C++ offers a much better way using static_assert.

To reset a polled timer and incorporate a static check, use this method:

template <uint64_t timeInterval> void reset();

Note that timeInterval cannot be a variable (even if it’s const) as the compiler must be able to determine its value. It must therefore be constexpr compatible.

You can use static checking to pre-validate the range for a timer before using it:

timer.checkTime<10>();
timer.checkTime<10000>();

This will throw a compile-time error if the timer is not capable of using intervals in the range 10 - 10000 microseconds (or whichever time unit you’ve selected). It doesn’t add any code to the application. If the code compiles, then you can be confident that the timer will function as expected and you don’t need to check return values.

You can see these checks in action in the Live Debug sample, which uses the HardwareTimer with a /16 prescaler. If you change BLINK_INTERVAL_MS to 2000 then the code will not compile.

Clocks

Timers and their capabilities can vary considerably. For example, Timer1 can be configured with a prescaler of 1, 16 or 256 which affects both the resolution and range of the timer. One might also consider the CPU cycle counter to have a selectable prescaler of 1 or 2, depending on whether it’s running at 80MHz or 160MHz.

A Clock definition is a class template which allows us to query timer properties and perform time conversions for a specific timer configuration. These definitions can be found in Sming/Platform/Clocks.h.

Note

A Clock is a purely virtual construct and does not provide any means to configure the hardware, although it does provide the ticks() method to obtain the current timer value.

Clocks are made more useful by TimeSource, a generic class template defined in Sming/Core/NanoTime.h. This provides methods to convert between time values and tick values for a specific time unit.

Let’s say we want a microsecond source using Timer2:

TimeSource<Timer2Clock, NanoTime::Microseconds, uint32_t> t2source;

We can now call methods of t2source like this:

// What's the maximum Timer2 value in microseconds?
Serial.println(t2source.maxClockTime());

// How many clock ticks per microsecond ?
Serial.println(t2source.ticksPerUnit()); // 5/1

// How many clock ticks for 100us ?
Serial.println(t2source.template timeConst<100>().ticks());

Note that all of these values are computed at compile time. Some runtime conversions:

Serial.println(t2source.timeToTicks(100));
Serial.println(t2source.ticksToTime(10000));

The results of conversions are rounded rather than truncated, which provides more accurate results and reduces timing jitter.

For debugging purposes you can print a description:

Serial.println(t2source.toString()); // "Timer2Clock/5MHz/32-bit/microseconds"

See Sming/Core/NanoTime.h for further details.

System Clock API
template<hw_timer_clkdiv_t clkdiv>
struct Timer1Clock : public NanoTime::Clock<Timer1Clock<clkdiv>, HW_TIMER_BASE_CLK / (1 << clkdiv), uint32_t, MAX_HW_TIMER1_INTERVAL>
#include <Clocks.h>

Clock implementation for Hardware Timer 1.

Template Parameters:

clkdiv – Prescaler in use

NanoTime

Utilities for handling time periods at nanosecond resolution.

namespace NanoTime

Typedefs

template<Unit unit>
using UnitTickRatio = std::ratio<unitTicks[unit].num, unitTicks[unit].den>

Class template to define tick std::ratio type.

Template Parameters:

unit

Retval std::ratio:

Ticks per second

Enums

enum Unit

Identify units for a scalar quantity of time.

Note

Ordered in increasing unit size, e.g. days > seconds

Values:

enumerator Nanoseconds
enumerator Microseconds
enumerator Milliseconds
enumerator Seconds
enumerator Minutes
enumerator Hours
enumerator Days
enumerator UnitMax

Functions

const char *unitToString(Unit unit)

Get a string identifying the given time units, e.g. “ns”.

const char *unitToLongString(Unit unit)

Get a long string identifying the given time units, e.g. “seconds”.

template<uint64_t time, Unit unitsFrom, Unit unitsTo, typename R = std::ratio_divide<UnitTickRatio<unitsTo>, UnitTickRatio<unitsFrom>>>
constexpr uint64_t convert()

Function template to convert a constant time quantity from one unit to another.

Note

example:

uint32_t micros = convert<50, Milliseconds, Microseconds>();

Template Parameters:
  • time – The time to convert

  • unitsFrom – Units for time parameter

  • unitsTo – Units for return value

Return values:

TimeType – Converted time

template<typename TimeType>
TimeType convert(const TimeType &time, Unit unitsFrom, Unit unitsTo)

Function template to convert a time quantity from one unit to another.

Template Parameters:

TimeType – Variable type to use for calculation

Parameters:
  • time – The time to convert

  • unitsFrom – Units for time parameter

  • unitsTo – Units for return value

Return values:

TimeType – Converted time, returns TimeType(-1) if calculation overflowed

template<typename T>
Time<T> time(Unit unit, T value)

Helper function to create a Time and deduce the type.

template<Unit unitsFrom, Unit unitsTo, typename TimeType>
TimeType convert(const TimeType &time)

Function template to convert a time quantity from one unit to another.

Template Parameters:
  • unitsFrom – Units for time parameter

  • unitsTo – Units for return value

  • TimeType – Variable type to use for calculation

Parameters:

time – The time to convert

Return values:

TimeType – Converted time, returns TimeType(-1) if calculation overflowed

Variables

constexpr BasicRatio32 unitTicks[UnitMax + 1] = {{1000000000, 1}, {1000000, 1}, {1000, 1}, {1, 1}, {1, 60}, {1, 60 * 60}, {1, 24 * 60 * 60},}

List of clock ticks for each supported unit of time.

struct Frequency
#include <NanoTime.h>

Class to represent a frequency.

template<class Clock_, Unit unit_, uint64_t time_>
struct TimeConst
#include <NanoTime.h>

Class template to represent a fixed time value for a specific Clock.

Note

Includes compile-time range checking. Time is taken as reference for conversions.

Template Parameters:
  • Clock_

  • unit_

  • time_

template<class Clock_, uint64_t ticks_>
struct TicksConst
#include <NanoTime.h>

Class template representing a fixed clock tick count.

Note

Includes compile-time range checking

Template Parameters:
  • Source_

  • ticks_

template<class Clock_, Unit unit_, typename TimeType_>
struct TimeSource : public Clock_
#include <NanoTime.h>

Class template for accessing a Clock in specific time units.

Note

Includes compile-time range checking. Time is taken as reference for conversions.

Template Parameters:
  • Clock_

  • units_

  • TimeType_ – Limits range of calculations

Subclassed by PolledTimer::Timer< NanoTime::Microseconds >, PolledTimer::Timer< NanoTime::Milliseconds >, PolledTimer::Timer< NanoTime::Seconds >, PolledTimer::Timer< NanoTime::Nanoseconds >

template<typename T>
struct Time
#include <NanoTime.h>

Class to handle a simple time value with associated unit.

template<typename Clock_, typename T>
struct Ticks
#include <NanoTime.h>

Class to handle a tick value associated with a clock.

template<typename ClockDef, uint32_t frequency_, typename TickType_, TickType_ maxTicks_>
struct Clock
#include <NanoTime.h>

Class template representing a physical Clock with fixed timing characteristics.

See also

Use TimeSource to work with a Clock in specific time units

Note

Physical clocks are implemented using this as a base. The provided frequency accounts for any prescaler setting in force. Fixing this at compile time helps to avoid expensive runtime calculations and permits static range checks.

Template Parameters:
  • ClockDef – The actual Clock being constructed (so we can query its properties)

  • frequency_Clock frequency in Hz

  • TickType_ – Variable type representing the clock’s tick value

  • maxTicks_ – Maximum count value for the clock

Subclassed by NanoTime::TimeSource< Clock, unit_, TimeType >

struct TimeValue
#include <NanoTime.h>

A time time broken into its constituent elements.

Note

Useful for analysing and printing time values

Core Framework

Program Space

Support for storing and accessing data from Program Space (flash memory).

A string literal (e.g. “string”) used in code gets emitted to the .rodata segment by the compiler. That means it gets read into RAM at startup and remains there.

To avoid this, and reclaim the RAM, the data must be stored in a different segment. This is done using the PROGMEM macro.

Such strings are usually defined using the PSTR() macro, which also ensures that any duplicate strings are merged and thus avoids storing them more than once. This is particularly beneficial for debugging strings.

Templated code

Attention

PROGMEM may not work when used in templated code.

GCC silently ignores ‘section attributes’ in templated code, which means const variables will remain in the default .rodata section.

Strings defined using PSTR() (and related macros) or FlashString definitions are handled correctly because internally they use special names (__pstr__ and __fstr__) which the linker picks up on.

memcpy_aligned

Once in flash memory, string data must be read into RAM before it can be used. Accessing the flash memory directly is awkward. If locations are not strictly accessed as 4-byte words the system will probably crash; I say ‘probably’ because sometimes it just behaves weirdly if the RAM address isn’t aligned.

So, the location being accessed, the RAM buffer it’s being copied to and the length all have to be word-aligned, i.e. integer multiples of 4 bytes. If these conditions are satisfied, then it’s safe to use a regular memcpy() call. However, you are strongly discouraged from doing this. Instead, use memcpy_aligned(), which will check the parameters and raise an assertion in debug mode if they are incorrect.

FakePgmSpace

Standard string functions such as memcpy_P(), strcpy_P(), etc. are provided to enable working with P-strings. With the new arduino-provided toolchains these are now part of the standard library, however Sming has some additions and differences.

F()

Loads a String object with the given text, which is allocated to flash:

String s = F("test");

Note

The F() macro differs from the Arduino/Esp8266 implementation in that it instantiates a String object.

Since the length of the string is known at compile-time, it can be passed to the String constructor which avoids an additional call to strlen_P().

_F()

Like F() except buffer is allocated on stack. Most useful where nul-terminated data is required:

m_printf(_F("C-style string\n"));

This macro is faster than F(), but you need to be careful as the temporary stack buffer becomes invalid as soon as the containing block goes out of scope. Used as a function parameter, that means the end of the function call.

Examples:

println(_F("Debug started"));

commandOutput->print(_F("Welcome to the Tcp Command executor\r\n"));

Bad:

char* s = _F("string")

An assignment such as this will not work because the temporary will be out of scope after the statement, hence s will point to garbage. In this instance PSTR_ARRAY(s, "string") can be used.

DEFINE_PSTR()

Declares a PSTR stored in flash. The variable (name) points to flash memory so must be accessed using the appropriate xxx_P function.

LOAD_PSTR()

Loads pre-defined PSTR into buffer on stack:

// The ``_LOCAL`` macro variants include static allocation
DEFINE_PSTR_LOCAL(testFlash, "This is a test string\n");
   LOAD_PSTR(test, testFlash)
   m_printf(test);
PSTR_ARRAY()

Create and load a string into the named stack buffer. Unlike _F(), this ensures a loaded string stays in scope:

String testfunc() {
   //char * test = "This is a string"; <<- BAD
   PSTR_ARRAY(test, "This is a string");
   m_printf(test);
   ...
   return test; // Implicit conversion to String
}

Both DEFINE_PSTR() and PSTR_ARRAY() load a PSTR into a stack buffer, but using sizeof on that buffer will return a larger value than the string itself because it’s aligned. Calling sizeof on the original flash data will get the right value. If it’s a regular nul-terminated string then strlen_P() will get the length, although it’s time-consuming.

FlashString

For efficient, fast and flexible use of PROGMEM data see FlashString.

API Documentation
FLASH_MEMORY_START_ADDR
isFlashPtr(ptr)

Simple check to determine if a pointer refers to flash memory.

PROGMEM

Place entity into flash memory.

Attach to const variable declaration to have it stored in flash memory

Such variables should not be accessed like regular pointers as aligned instructions are required. Use the provided library functions, such as memcpy_P, instead.

PROGMEM_PSTR

Place NUL-terminated string data into flash memory.

Duplicate string data will be merged according to the rules laid out in https://sourceware.org/binutils/docs/as/Section.html

PSTR(str)

Define and use a NUL-terminated ‘C’ flash string inline.

Note

Uses string section merging so must not contain embedded NULs

Parameters:
  • str

Return values:

char[] – In flash memory, access using flash functions

PGM_P

Identifies a char pointer as living in flash memory Use to clarify code.

PRIPSTR

Remove ?

void *memcpy_aligned(void *dst, const void *src, unsigned len)

copy memory aligned to word boundaries

dst and src must be aligned to word (4-byte) boundaries len will be rounded up to the nearest word boundary, so the dst buffer MUST be large enough for this.

Parameters:
  • dst

  • src

  • len – Size of the source data

int memcmp_aligned(const void *ptr1, const void *ptr2, unsigned len)

compare memory aligned to word boundaries

ptr1 and ptr2 must all be aligned to word (4-byte) boundaries. len is rounded up to the nearest word boundary

Parameters:
  • ptr1

  • ptr2

  • len

Return values:

int – 0 if all bytes match

IS_ALIGNED(_x)

determines if the given value is aligned to a word (4-byte) boundary

ALIGNUP4(n)

Align a size up to the nearest word boundary.

ALIGNDOWN4(n)

Align a size down to the nearest word boundary.

printf_P_heap(f_P, ...)
printf_P_stack(f_P, ...)
printf_P(fmt, ...)
PSTR_COUNTED(str)

Define and use a counted flash string inline.

Note

Strings are treated as binary data so may contain embedded NULs, but duplicate strings are not merged.

Parameters:
  • str

Return values:

char[] – In flash memory, access using flash functions

_F(str)
DEFINE_PSTR(name, str)

define a PSTR

Parameters:
  • name – name of string

  • str – the string data

DEFINE_PSTR_LOCAL(name, str)

define a PSTR for local (static) use

Parameters:
  • name – name of string

  • str – the string data

DECLARE_PSTR(name)

Declare a global reference to a PSTR instance.

Parameters:
  • name

LOAD_PSTR(name, flash_str)

Create a local (stack) buffer called name and load it with flash data.

If defining a string within a function or other local context, must declare static.

Example:

    void testfunc() {
        static DEFINE_PSTR(test, "This is a test string\n");
        m_printf(LOAD_PSTR(test));
    }

Parameters:
  • name

  • flash_str – Content stored in flash. The compiler knows its size (length + nul), which is rounded up to multiple of 4 bytes for fast copy.

_FLOAD(pstr)
PSTR_ARRAY(name, str)

Define a flash string and load it into a named array buffer on the stack.

For example, this:

    PSTR_ARRAY(myText, "some text");
is roughly equivalent to this:
    char myText[ALIGNED_SIZE] = "some text";
where ALIGNED_SIZE is the length of the text (including NUL terminator) rounded up to the next word boundary. To get the length of the text, excluding NUL terminator, use:
    sizeof(PSTR_myText) - 1

Note

Must not contain embedded NUL characters

Core Data Classes
BitSet
Introduction

The C++ STL provides std::bitset which emulates an array of bool elements.

The BitSet class template provides similar support for sets of strongly-typed elements. For example:

enum class Fruit {
   apple,
   banana,
   kiwi,
   orange,
   passion,
   pear,
   tomato,
};

using FruitBasket = BitSet<uint8_t, Fruit, unsigned(Fruit::tomato) + 1>;

static constexpr FruitBasket fixedBasket = Fruit::orange | Fruit::banana | Fruit::tomato;

A FruitBasket uses one byte of storage, with each bit representing an item of Fruit. If the basket contains a piece of fruit, the corresponding bit is set. If it does not, the bit is clear.

Without BitSet you implement this as follows:

using FruitBasket = uint8_t;

static constexpr FruitBasket fixedBasket = _BV(Fruit::orange) | _BV(Fruit::banana) | _BV(Fruit::tomato);

To test whether the set contains a value you’d do this:

if(fixedBasket & _BV(Fruit::orange)) {
   Serial.println("I have an orange");
}

With a BitSet, you do this:

if(fixedBasket[Fruit::orange]) {
   Serial.println("I have an orange");
}

And you can add an element like this:

basket[Fruit::kiwi] = true;

Bit manipulation operators are provided so you can do logical stuff like this:

FruitBasket basket1; // Create an empty basket

// Add a kiwi fruit
basket1 = fixedBasket + Fruit::kiwi;

// Create a second basket containing all fruit not in our first basket
FruitBasket basket2 = ~basket1;

// Remove some fruit
basket2 -= Fruit::orange | Fruit::tomato;

And so on.

To display the contents of a BitSet, do this:

Serial.print(_F("My basket contains: "));
Serial.println(basket1);

You will also need to provide an implementation of toString(Fruit) or whatever type you are using for the set elements.

API
template<typename S, typename E, size_t size_ = sizeof(S) * 8>
class BitSet

Manage a set of bit values using enumeration.

API is similar to a simplified std::bitset, but with added +/- operators.

Note

It is important to specify size correctly when using enumerated values. In the FruitBasket example, we use a uint8_t storage type so can have up to 8 possible values. However, the Fruit enum contains only 7 values. The set operations will therefore be restricted to ensure that the unused bit is never set.

Template Parameters:
  • SStorage type (e.g. uint32_t). This determines how much space to use, and must be an unsigned integer. It is safe to use this class in structures, where it will occupy exactly the required space.

  • E – Element type e.g. enum class. You can use any enum or unsigned integer for the elements. These must have ordinal sequence starting at 0.

  • size_ – Number of possible values in the set. Defaults to maximum for given storage type. Number of possible values in the set. This must be at least 1, and cannot be more than the given Storage type may contain. For example, a :cpp:type:uint8_t may contain up to 8 values.

Public Functions

constexpr BitSet() = default

Construct empty set.

template<typename S2>
inline constexpr BitSet(const BitSet<S2, E> &bitset)

Copy constructor.

Parameters:

bitset – The set to copy

inline constexpr BitSet(S value)

Construct from a raw set of bits.

Parameters:

value – Integral type whose bits will be interpreted as set{E}

inline constexpr BitSet(E e)

Construct set containing a single value.

Parameters:

e – Value to place in our new BitSet object

inline bool operator==(const BitSet &other) const

Compare this set with another for equality.

inline bool operator!=(const BitSet &other) const

Compare this set with another for inequality.

inline constexpr BitSet operator~() const

Obtain a set containing all elements not in this one.

inline BitSet &flip()

Flip all bits in the set.

inline BitSet &flip(E e)

Flip state of the given bit.

inline size_t count() const

Get the number of elements in the set, i.e. bits set to 1.

inline BitSet &operator+=(const BitSet &rhs)

Union: Add elements to set.

inline BitSet &operator-=(const BitSet &rhs)

Remove elements from set.

inline BitSet &operator&=(const BitSet &rhs)

Intersection: Leave only elements common to both sets.

inline BitSet &operator|=(const BitSet &rhs)

Union: Add elements to set.

inline BitSet &operator^=(const BitSet &rhs)

XOR - toggle state of bits using another set.

inline bool test(E e) const

Test to see if given element is in the set.

inline bool operator[](E e) const

Read-only [] operator.

Parameters:

e – Element to test for

Return values:

bool – true if given element is in the set

inline BitRef operator[](E e)

Read/write [] operator.

This returns a temporary BitRef object to support assignment operations such as set[x] = value

Parameters:

e – Element to read or write

Return values:

BitRef – Temporary object used to do the read or write

inline bool any() const

Determine if set contains any values.

inline bool any(const BitSet &other) const

Determine if set contains any values from another set i.e. intersection != [].

inline bool all() const

Test if set contains all possible values.

inline bool none() const

Test if set is empty.

inline BitSet &set()

Add all possible values to the bit set.

inline BitSet &set(E e, bool state = true)

Set the state of the given bit (i.e. add to or remove from the set)

Parameters:
  • e – Element to change

  • state – true to add the element, false to remove it

inline BitSet &reset()

Remove all values from the set.

inline BitSet &reset(E e)

Clear the state of the given bit (i.e. remove it from the set)

inline bool operator==(E e) const

Determine if set consists of only the one given element.

inline explicit constexpr operator S() const

Allow casts from the native storage type to get a numeric result for this set.

inline constexpr S value() const

Get stored bits for this bitset.

inline size_t printTo(Print &p, const String &separator = ", ") const

Class template to print the contents of a BitSet to a String.

Note

Requires an implementation of toString(E)

Public Static Functions

static inline constexpr size_t size()

Get the number of possible elements in the set.

static inline constexpr BitSet domain()

Get the set of all possible values.

static inline constexpr S bitVal(E e)

Get the bitmask corresponding to a given value.

class BitRef
CString
Introduction

Whilst use of char* pointers is very common in Sming code, it is generally advisable to avoid pointers in C++ where possible.

The STL provides class templates such as unique_ptr which deals with memory allocation and de-allocation to avoid issues with memory leaks.

The CString class implements this on a char[] and adds some additional methods which are similar to the String class.

String vs. CString

String objects each require a minimum of 24 bytes of RAM, and always contain a length field. A CString is much simpler and contains only a char* pointer, so a NULL string is only 4 bytes.

When storing arrays or lists of strings (or objects containing those strings) which change infrequently, such as fixed configuration data, use a CString for memory efficiency.

API Documentation
class CString : public std::unique_ptr<char[]>

Class to manage a NUL-terminated C-style string When storing persistent strings in RAM the regular String class can become inefficient, so using a regular char* can be preferable. This class provides that with additional methods to simplify lifetime management and provide some interoperability with Wiring String objects.

CStringArray
Introduction

This is a class to manage a double NUL-terminated list of strings, such as "one\0two\0three\0".

It’s similar in operation to Vector<String>, but more memory-efficient as all the data is stored in a single String object. (CStringArray is a subclass of String.)

You can see some examples in Sming/Core/DateTime.cpp and Sming/Core/Data/WebConstants.cpp.

Background

Each value in the sequence is terminated by a NUL character \0. For clarity, placing one string per line is suggested:

// ["one", "two", "three"]
CStringArray csa = F(
   "one\0"
   "two\0"
   "three\0"
);

Note use of the F() macro. Assignments require a length because of the NUL characters, so this won’t work as expected:

// ["one"]
CStringArray csa =
   "one\0"
   "two\0"
   "three\0";

When assigning sequences, the final NUL separator may be omitted (it will be added automatically):

// ["one", "two", "three"]
CStringArray csa = F(
   "one\0"
   "two\0"
   "three"
);

Sequences may contain empty values, so this example contains four values:

// ["one", "two", "three", ""]
CStringArray csa = F(
   "one\0"
   "two\0"
   "three\0"
   "\0"
);
Adding strings

Elements can be added using standard concatenation operators:

CStringArray arr;
arr += "string1";
arr += 12;
arr += 5.4;
arr += F("data");

Be mindful that each call may require a heap re-allocation, so consider estimating or calculating the required space and using String::reserve():

CStringArray arr;
arr.reserve(250);
// now add content
Use with FlashString

You can use a single FlashString containing these values and load them all at the same time into a CStringArray:

DEFINE_FSTR_LOCAL(fstr_list,
   "a\0"
   "b\0"
   "c\0"
   "d\0"
   "e\0"
);

CStringArray list(fstr_list);
for(unsigned i = 0; i < list.count(); ++i) {
   debug_i("list[%u] = '%s'", i, list[i]);
}

Note

The entire FlashString is loaded into RAM so better suited for occasional lookups or if instantiated outside of a loop.

You may find FSTR::Array, FSTR::Vector or FSTR::Map more appropriate. See FlashString for details.

Iterator support

Looping by index is slow because the array must be scanned from the start for each access. Iterators are simpler to use and much more efficient:

for(auto s: list) {
   debug_i("'%s'", s);
}

For more complex operations:

CStringArray::Iterator pos;
for(auto it = list.begin(); it != list.end(); ++it) {
   debug_i("list[%u] = '%s' @ %u", it.index(), *it, it.offset());
   // Can use direct comparison with const char* or String
   if(it == "c") {
      pos = it; // Note position
   }
}

if(pos) {
   debug_i("Item '%s' found at index %u, offset %u", pos.str(), pos.index(), pos.offset());
} else {
   debug_i("Item not found");
}
Pushing and popping

CStringArray can be used as a simple FIFO or stack using push/pop methods. Behaviour is similar to STL deque, except pop methods also return a value.

STACK:

CStringArray csa;
csa.pushBack("first value");
csa.pushBack("second value");
String popStack = csa.popBack(); // "second value"

FIFO:

CStringArray csa;
csa.pushBack("first value");
csa.pushBack("second value");
String deque = csa.popFront(); // "first value"

Note that popping values does not perform any memory de-allocation.

Comparison with Vector<String>
Advantages

More memory efficient Uses only a single heap allocation (assuming content is passed to constructor) Useful for simple lookups, e.g. mapping enumerated values to strings

Disadvantages

Slower. Items must be iterated using multiple strlen() calls Ordering and insertions / deletions not supported

API Documentation
class CStringArray : private String

Class to manage a double null-terminated list of strings, such as “one\0two\0three\0”.

Concatenation operators

template<typename T>
inline CStringArray &operator+=(T value)

Append numbers, etc. to the array.

Parameters:

value – char, int, float, etc. as supported by String

Public Functions

bool add(const char *str, int length = -1)

Append a new string (or array of strings) to the end of the array.

Note

If str contains any NUL characters it will be handled as an array

Parameters:
  • str

  • length – Length of new string in array (default is length of str)

Return values:

bool – false on memory allocation error

inline bool add(const String &str)

Append a new string (or array of strings) to the end of the array.

Note

If str contains any NUL characters it will be handled as an array

Parameters:

str

Return values:

bool – false on memory allocation error

int indexOf(const char *str, bool ignoreCase = true) const

Find the given string and return its index.

Note

Comparison is not case-sensitive

Parameters:
  • strString to find

  • ignoreCase – Whether search is case-sensitive or not

Return values:

int – index of given string, -1 if not found

inline int indexOf(const String &str, bool ignoreCase = true) const

Find the given string and return its index.

Note

Comparison is not case-sensitive

Parameters:
  • strString to find

  • ignoreCase – Whether search is case-sensitive or not

Return values:

int – index of given string, -1 if not found

inline bool contains(const char *str, bool ignoreCase = true) const

Check if array contains a string.

Note

Search is not case-sensitive

Parameters:
  • strString to search for

  • ignoreCase – Whether search is case-sensitive or not

Return values:

bool – True if string exists in array

inline bool contains(const String &str, bool ignoreCase = true) const

Check if array contains a string.

Note

Search is not case-sensitive

Parameters:
  • strString to search for

  • ignoreCase – Whether search is case-sensitive or not

Return values:

bool – True if string exists in array

const char *getValue(unsigned index) const

Get string at the given position.

Parameters:

index – 0-based index of string to obtain

Return values:

const – char* nullptr if index is not valid

inline const char *operator[](unsigned index) const

Get string at the given position.

Parameters:

index – 0-based index of string to obtain

Return values:

const – char* nullptr if index is not valid

inline const char *front() const

Get first value in array, null if empty.

bool pushFront(const char *str)

Insert item at start of array.

Parameters:

str – Item to insert

Return values:

bool – false on memory error

String popFront()

Pop first item from array (at index 0)

Return values:

String – null if array is empty

const char *back() const

Get last item in array, null if empty.

inline bool pushBack(const char *str)

Add item to end of array.

Parameters:

str – Item to add

Return values:

bool – false on memory error

String popBack()

Pop last item from array.

Return values:

String – null if array is empty

inline void clear()

Empty the array.

unsigned count() const

Get quantity of strings in array.

Return values:

unsigned – Quantity of strings

String join(const String &separator = ",") const

Get contents of array as delimited string.

Parameters:

separator – What to join elements with

Return values:

String – e.g. CStringArray(F(“a\0b\0c”)).join() returns “a,b,c”

bool reserve(size_t size)

Pre-allocate String memory.

On failure, the String is left unchanged. reserve(0), if successful, will validate an invalid string (i.e., “if (s)” will be true afterwards)

Parameters:

size

Return values:

bool – true on success, false on failure

inline const char *c_str() const

Get a constant (un-modifiable) pointer to String content.

Return values:

const – char* Always valid, even for a null string

bool endsWith(char suffix) const

Compare the end of a String.

Parameters:

suffix

Return values:

bool – true on match

bool endsWith(const String &suffix) const

Compare the end of a String.

Parameters:

suffix

Return values:

bool – true on match

size_t getBytes(unsigned char *buf, size_t bufsize, size_t index = 0) const

Read contents of a String into a buffer.

Note

Returned data always nul terminated so buffer size needs to take this into account

Parameters:
  • buf – buffer to write data

  • bufsize – size of buffer in bytes

  • index – offset to start

Return values:

unsigned – number of bytes copied, excluding nul terminator

inline size_t length(void) const

Obtain the String length in characters, excluding NUL terminator.

inline bool startsWith(const String &prefix) const

Compare the start of a String Comparison is case-sensitive, must match exactly.

Parameters:

prefix

Return values:

bool – true on match

bool startsWith(const String &prefix, size_t offset) const

Compare a string portion.

mis-named as does not necessarily compare from start

Note

Comparison is case-sensitive, must match exactly

Parameters:
  • prefix

  • offset – Index to start comparison at

Return values:

bool – true on match

inline void toCharArray(char *buf, size_t bufsize, size_t index = 0) const

Read contents of String into a buffer.

See also

See getBytes()

void toLowerCase(void)

Convert the entire String content to lower case.

void toUpperCase(void)

Convert the entire String content to upper case.

class Iterator
CSV Reader
class CsvReader

Class to parse a CSV file.

Spec: https://www.ietf.org/rfc/rfc4180.txt

  1. Each record is located on a separate line

  2. Line ending for last record in the file is optional

  3. Field headings are provided either in the source data or in constructor (but not both)

  4. Fields separated with ‘,’ and whitespace considered part of field content

  5. Fields may or may not be quoted - if present, will be removed during parsing

  6. Fields may contain line breaks, quotes or commas

  7. Quotes may be escaped thus “” if field itself is quoted

Additional features:

  • Line breaks can be

    or \r

  • Escapes codes within fields will be converted:

    \r \t “, \

  • Field separator can be changed in constructor

Public Functions

inline CsvReader(IDataSourceStream *source, char fieldSeparator = ',', const CStringArray &headings = nullptr, size_t maxLineLength = 2048)

Construct a CSV reader.

Parameters:
  • source – Stream to read CSV text from

  • fieldSeparator

  • headings – Required if source data does not contain field headings as first row

  • maxLineLength – Limit size of buffer to guard against malformed data

void reset()

Reset reader to start of CSV file.

Cursor is set to ‘before start’. Call ‘next()’ to fetch first record.

inline bool next()

Seek to next record.

inline unsigned count() const

Get number of columns.

inline const char *getValue(unsigned index)

Get a value from the current row.

Parameters:

index – Column index, starts at 0

Return values:

const – char* nullptr if index is not valid

inline const char *getValue(const char *name)

Get a value from the current row.

Parameters:

index – Column name

Return values:

const – char* nullptr if name is not found

inline int getColumn(const char *name)

Get index of column given its name.

Parameters:

name – Column name to find

Return values:

int – -1 if name is not found

inline explicit operator bool() const

Determine if row is valid.

inline const CStringArray &getHeadings() const

Get headings.

inline const CStringArray &getRow() const

Get current row.

Data formatting

A standard mechanism is provided for encoding and decoding text in commonly-used text formats using a Format::Formatter class implementation.

namespace Format

Variables

Html html
Json json
Standard standard
Xml xml
class Formatter
#include <Formatter.h>

Virtual class to perform format-specific String adjustments.

Subclassed by Format::Standard

Public Functions

virtual void escape(String &value) const = 0

Perform any necessary text escaping so output is valid.

virtual void quote(String &value) const = 0

Convert a value into quoted string.

virtual void unQuote(String &value) const = 0

Remove any quotes from a value.

virtual MimeType mimeType() const = 0

Corresponding MIME type for this format.

Note

New types must be added to WebConstants.h

class Html : public Format::Standard
#include <Html.h>

Public Functions

virtual void escape(String &value) const override

Perform any necessary text escaping so output is valid.

inline virtual MimeType mimeType() const override

Corresponding MIME type for this format.

Note

New types must be added to WebConstants.h

class Json : public Format::Standard
#include <Json.h>

Public Functions

virtual void escape(String &value) const override

Perform any necessary text escaping so output is valid.

inline virtual MimeType mimeType() const override

Corresponding MIME type for this format.

Note

New types must be added to WebConstants.h

class Standard : public Format::Formatter
#include <Standard.h>

Subclassed by Format::Html, Format::Json, Format::Xml

Public Functions

inline virtual void escape(String &value) const override

Perform any necessary text escaping so output is valid.

virtual void quote(String &value) const override

Convert a value into quoted string.

virtual void unQuote(String &value) const override

Remove any quotes from a value.

inline virtual MimeType mimeType() const override

Corresponding MIME type for this format.

Note

New types must be added to WebConstants.h

class Xml : public Format::Standard
#include <Xml.h>

Public Functions

virtual void escape(String &value) const override

Perform any necessary text escaping so output is valid.

inline virtual MimeType mimeType() const override

Corresponding MIME type for this format.

Note

New types must be added to WebConstants.h

Linked Object Lists
class LinkedObject
#include <LinkedObject.h>

Base virtual class to allow objects to be linked together.

This can be more efficient than defining a separate list, as each object requires only an additional pointer field for ‘next’.

Subclassed by LinkedObjectTemplate< Request >, LinkedObjectTemplate< Item >, LinkedObjectTemplate< HttpResourcePlugin >, LinkedObjectTemplate< StationInfo >, LinkedObjectTemplate< Element >, LinkedObjectTemplate< PluginRef >, LinkedObjectTemplate< Object >, LinkedObjectTemplate< Asset >, LinkedObjectTemplate< Answer >, LinkedObjectTemplate< Controller >, LinkedObjectTemplate< Handler >, LinkedObjectTemplate< Question >, LinkedObjectTemplate< Service >, LinkedObjectTemplate< Device >, LinkedObjectTemplate< PriorityNode< ObjectType > >, LinkedObjectTemplate< Renderer >, LinkedObjectTemplate< ClassType >, LinkedObjectTemplate< Info >, LinkedObjectTemplate< ObjectType >

Public Functions

inline virtual ~LinkedObject()
inline virtual LinkedObject *next() const
inline bool insertAfter(LinkedObject *object)
inline bool operator==(const LinkedObject &other) const
inline bool operator!=(const LinkedObject &other) const

Private Members

LinkedObject *mNext = {nullptr}

Friends

friend class LinkedObjectList
template<typename ObjectType>
class LinkedObjectTemplate : public LinkedObject
#include <LinkedObject.h>

Base class template for linked items with type casting.

Public Types

using Iterator = IteratorTemplate<ObjectType, ObjectType*, ObjectType&>
using ConstIterator = IteratorTemplate<const ObjectType, const ObjectType*, const ObjectType&>

Public Functions

inline ObjectType *getNext() const
inline bool insertAfter(ObjectType *object)
inline Iterator begin() const
inline Iterator end() const
inline Iterator cbegin() const
inline Iterator cend() const
template<typename T, typename TPtr, typename TRef>
class IteratorTemplate
#include <LinkedObject.h>

Public Types

using iterator_category = std::forward_iterator_tag
using value_type = T
using difference_type = std::ptrdiff_t
using pointer = T*
using reference = T&

Public Functions

inline IteratorTemplate(TPtr x)
inline IteratorTemplate(TRef &x)
inline IteratorTemplate(const IteratorTemplate &other)
inline IteratorTemplate &operator++()
inline IteratorTemplate operator++(int)
inline bool operator==(const IteratorTemplate &rhs) const
inline bool operator!=(const IteratorTemplate &rhs) const
inline TRef operator*()
inline TPtr operator->()
inline operator TPtr()

Private Members

TPtr mObject
class LinkedObjectList
#include <LinkedObjectList.h>

Singly-linked list of objects.

Note

We don’t own the items, just keep references to them

Subclassed by LinkedObjectListTemplate< Controller >, LinkedObjectListTemplate< Handler >, LinkedObjectListTemplate< Service >, LinkedObjectListTemplate< Control >, LinkedObjectListTemplate< ObjectType >

Public Functions

inline LinkedObjectList()
inline LinkedObjectList(LinkedObject *object)
bool add(LinkedObject *object)
inline bool add(const LinkedObject *object)
inline bool insert(LinkedObject *object)
inline bool insert(const LinkedObject *object)
bool remove(LinkedObject *object)
inline LinkedObject *pop()
inline void clear()
inline LinkedObject *head()
inline const LinkedObject *head() const
inline bool isEmpty() const

Protected Attributes

LinkedObject *mHead = {nullptr}
template<typename ObjectType>
class LinkedObjectListTemplate : public LinkedObjectList
#include <LinkedObjectList.h>

Subclassed by OwnedLinkedObjectListTemplate< Request >, OwnedLinkedObjectListTemplate< Item >, OwnedLinkedObjectListTemplate< HttpResourcePlugin >, OwnedLinkedObjectListTemplate< Surface >, OwnedLinkedObjectListTemplate< Element >, OwnedLinkedObjectListTemplate< StationInfo >, OwnedLinkedObjectListTemplate< PluginRef >, OwnedLinkedObjectListTemplate< Asset >, OwnedLinkedObjectListTemplate< Answer >, OwnedLinkedObjectListTemplate< Question >, OwnedLinkedObjectListTemplate< Device >, OwnedLinkedObjectListTemplate< PriorityNode< ObjectType > >, OwnedLinkedObjectListTemplate< ClassType >, OwnedLinkedObjectListTemplate< Info >, OwnedLinkedObjectListTemplate< ObjectType >

Public Types

using Iterator = typename LinkedObjectTemplate<ObjectType>::template IteratorTemplate<ObjectType, ObjectType*, ObjectType&>
using ConstIterator = typename LinkedObjectTemplate<ObjectType>::template IteratorTemplate<const ObjectType, const ObjectType*, const ObjectType&>

Public Functions

LinkedObjectListTemplate() = default
inline LinkedObjectListTemplate(ObjectType *object)
inline ObjectType *head()
inline const ObjectType *head() const
inline Iterator begin()
inline Iterator end()
inline ConstIterator begin() const
inline ConstIterator end() const
inline bool add(ObjectType *object)
inline bool add(const ObjectType *object)
inline bool insert(ObjectType *object)
inline bool insert(const ObjectType *object)
inline ObjectType *pop()
inline size_t count() const
inline bool contains(const ObjectType &object) const
template<typename ObjectType>
class OwnedLinkedObjectListTemplate : public LinkedObjectListTemplate<ObjectType>
#include <LinkedObjectList.h>

Class template for singly-linked list of objects.

Note

We own the objects so are responsible for destroying them when removed

Subclassed by Storage::Disk::BasePartitionTable

Public Functions

OwnedLinkedObjectListTemplate() = default
OwnedLinkedObjectListTemplate(const OwnedLinkedObjectListTemplate &other) = delete
OwnedLinkedObjectListTemplate &operator=(const OwnedLinkedObjectListTemplate &other) = delete
inline ~OwnedLinkedObjectListTemplate()
inline bool remove(ObjectType *object)
inline void clear()
Object Map
template<typename K, typename V>
class ObjectMap
#include <ObjectMap.h>

Implementation of a HashMap for owned objects, i.e. anything created with new().

Example:

void test()
{
   ObjectMap<String, MyType> map;
   MyType* object1 = new MyType();
   if (map["key1"] == nullptr) {   // Does NOT create entry in map
       map["key1"] = object1;      // Entry now created, "key1" -> object1
   }
   MyType* object2 = new MyType();
   map["key1"] = object2;          // object1 is destroyed, "key1" -> object2

   // Demonstrate use of value reference
   auto value = map["key1"];       // Returns ObjectMap<String, MyType>::Value object
   value = new MyType();           // "key1" -> new object
   value = nullptr;                // Free object, "key1" -> nullptr (but still in map)
   value.remove();                 // Free object1 and remove from map

   // As soon as `map` goes out of scope, all contained objects are destroyed
   map["key1"] = new MyType();
   map["key2"] = new MyType();
}

Note

Once added to the map the object is destroyed when no longer required.

Public Functions

inline ObjectMap()
inline ~ObjectMap()
inline unsigned count() const

Get the number of entries in this map.

Return values:

intEntry count

inline const K &keyAt(unsigned idx) const
inline K &keyAt(unsigned idx)
inline const V *valueAt(unsigned idx) const
inline Value valueAt(unsigned idx)
inline const V *operator[](const K &key) const

Get value for given key, if it exists.

Note

The caller must not use delete on the returned value

Parameters:

key

Return values:

const – V* Will be null if not found in the map

inline Value operator[](const K &key)

Access map entry by reference.

See also

valueAt()

Note

If the given key does not exist in the map it will NOT be created

Parameters:

key

Return values:

Value – Guarded access to mapped value corresponding to given key

inline Value get(const K &key)

Get map entry value.

See also

operator[]

Parameters:

key

Return values:

Value

inline void set(const K &key, V *value)

Set a key value.

Parameters:
  • key

  • value

inline V *find(const K &key) const

Find the value for a given key, if it exists.

Note

If you need to modify the existing map entry, use operator[] or valueAt()

Parameters:

key

Return values:

V* – Points to the object if it exists, otherwise nullptr

inline int indexOf(const K &key) const

Get the index of a key.

Parameters:

key

Return values:

int – The index of the key, or -1 if key does not exist

inline bool contains(const K &key) const

Check if a key is contained within this map.

Parameters:

key – the key to check

Return values:

bool – true if key exists

inline void removeAt(unsigned index)

Remove entry at given index.

Parameters:

index – location to remove from this map

inline bool remove(const K &key)

Remove a key from this map.

Parameters:

key – The key identifying the entry to remove

Return values:

bool – true if the value was found and removed

inline V *extract(const K &key)

Get the value for a given key and remove it from the map, without destroying it.

Note

The returned object must be freed by the caller when no longer required

Parameters:

key

Return values:

V*

inline V *extractAt(unsigned index)

Get the value at a given index and remove it from the map, without destroying it.

Note

The returned object must be freed by the caller when no longer required

Parameters:

index

Return values:

V*

inline void clear()

Clear the map of all entries.

Protected Attributes

Vector<Entry> entries

Private Functions

ObjectMap(ObjectMap<K, V> &that)
struct Entry
#include <ObjectMap.h>

An entry in the ObjectMap.

Public Functions

inline bool operator==(const K &keyToFind) const
inline Entry(const K &key, V *value)

Public Members

K key
std::unique_ptr<V> value
class Value
#include <ObjectMap.h>

Class to provide safe access to mapped value.

Note

ObjectMap operator[] returns one of these, which provides behaviour consistent with V*

Public Functions

inline Value(ObjectMap<K, V> &map, const K &key)
inline const K &getKey() const
inline V *getValue() const
inline Value &operator=(V *newValue)
inline operator V*() const
inline V *operator->() const
inline bool remove()

Remove this value from the map.

Return values:

bool – true if the value was found and removed

inline V *extract()

Get the value for a given key and remove it from the map, without destroying it.

Note

The returned object must be freed by the caller when no longer required

Return values:

V*

Private Members

ObjectMap<K, V> &map
K key
Object Queue
template<typename T, int rawSize>
class ObjectQueue : public FIFO<T*, rawSize>
#include <ObjectQueue.h>

FIFO for objects.

Note

Objects are not owned so construction/destruction must be managed elsewhere

Public Functions

inline virtual ~ObjectQueue()
inline T *peek() const
inline T *dequeue()
Packet writing
struct Packet
#include <Packet.h>

Helper class for reading/writing packet content.

Subclassed by HostPacket, NetworkPacket

Public Functions

inline Packet(void *data, uint16_t pos = 0)
inline const uint8_t *ptr() const
inline uint8_t *ptr()
inline void skip(uint16_t len) const
inline uint8_t peek8() const
inline uint8_t read8() const
inline void read(void *buffer, uint16_t len) const
inline String readString(uint16_t length) const
inline void write8(uint8_t value)
inline void write(const void *s, uint16_t len)

Public Members

uint8_t *data
mutable uint16_t pos
struct NetworkPacket : public Packet
#include <Packet.h>

Helper class for reading/writing packet content in network byte-order (MSB first)

Public Functions

inline uint16_t peek16() const
inline uint16_t read16() const
inline uint32_t read32() const
inline void write16(uint16_t value)
inline void write32(uint32_t value)
inline Packet(void *data, uint16_t pos = 0)
struct HostPacket : public Packet
#include <Packet.h>

Helper class for reading/writing packet content in host byte-order (LSB first)

Public Functions

inline uint16_t peek16() const
inline uint16_t read16() const
inline uint32_t read32() const
inline void write16(uint16_t value)
inline void write32(uint32_t value)
inline Packet(void *data, uint16_t pos = 0)
Range

Functions

template<typename T>
inline String toString(TRange<T> range)
template<typename T>
struct TRange
#include <Range.h>

Manage a range of numbers between specified limits.

Values in the range meet the criteria (min <= value <= max)

Public Functions

inline constexpr TRange()
inline constexpr TRange(T min, T max)
inline constexpr TRange(T count)
inline bool contains(T value) const

Determine if range contains a value.

template<typename Q>
inline bool contains(const TRange<Q> &value) const

Determine if range contains another range (subset)

inline T clip(T value) const

Clip values to within the range.

inline T random() const

Return a random value within the range.

inline Iterator begin() const
inline Iterator end() const
inline String toString() const
inline operator String() const

Public Members

T min = {}
T max = {}
class Iterator
#include <Range.h>

Public Types

using value_type = T
using difference_type = std::ptrdiff_t
using pointer = T*
using reference = T&
using iterator_category = std::random_access_iterator_tag

Public Functions

inline Iterator(T value)
inline T operator*() const
inline bool operator==(const Iterator &other) const
inline bool operator!=(const Iterator &other) const
inline Iterator operator++(int)
inline Iterator &operator++()

Private Members

T value
Streams

Sming provides a set of Stream class which extend Stream methods.

IDataSourceStream is used where read-only access is required. It introduces the IDataSourceStream::readMemoryBlock() method which performs a regular read without updating the stream position. This allows optimistic reading and re-sending, but cannot be handled by some stream types and should be used with care.

ReadWriteStream is used where read/write operation is required.

Printing

The arduino Print class provides the basic output streaming mechanism. Sming has some enhancements:

C++ streaming operation <<

Building output is commonly done like this:

Serial.print("Temperature: ");
Serial.print(temperature);
Serial.print(" °C, humidity: ");
Serial.print(humidity);
Serial.println("%");

In Sming, this will produce exactly the same result:

Serial << "Temperature" << temperature << " °C, humidity: " << humidity << "%" << endl;

Note

Sming does NOT support the C++ STL streaming classes, such as iostream, etc.

Number Printing

Examples:

Serial.print(12, HEX);         // "c"
Serial.print(12, HEX, 4);      // "000c"
Serial.print(12, HEX, 4, '.'); // "...c"
Serial.print(12);              // "12"
Serial.print(12, DEC, 4);      // "0012"

Similar extensions are provided for String construction:

Serial << "0x" << String(12, HEX, 8);       // "0x0000000c"
Serial << String(12, DEC, 4, '.');          // "..12"
Field-width control

Supported via String methods:

Serial << String(12).padLeft(4);          // "  12"
Serial << String(12).padLeft(4, '0');     // "0012"
Serial << String(12).padRight(4);         // "12  "
Serial << String(12).pad(-4, '0');        // "0012"
Serial << String(12).pad(4);              // "12  "
Strongly-typed enumerations

Use of enum class is good practice as it produces strongly-typed and scoped values. Most of these are also provided with a standard toString(E) function overload.

This allows string equivalents to be printed very easily:

auto status = HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED;
auto type = MIME_HTML;
Serial.print(type); // "text/html"
// "Status: HTTP Version Not Supported, type: text/html"
Serial << "Status: " << status << ", type: " << type << endl;
// Status: 505, type: 0
Serial << "Status: " << int(status) << ", type: " << int(type) << endl;
API Documentation
IDataSourceStream
class IDataSourceStream : public Stream

Base class for read-only stream.

Subclassed by FSTR::Stream, Graphics::SubStream, IFS::FWFS::ArchiveStream, MultiStream, ReadWriteStream, RtttlJsonListStream, SectionStream, SharedMemoryStream< T >, StreamTransformer, TemplateStream, UPnP::DescriptionStream, UrlencodedOutputStream, XorOutputStream

Public Functions

inline virtual StreamType getStreamType() const

Get the stream type.

Return values:

StreamType – The stream type.

inline virtual bool isValid() const

Determine if the stream object contains valid data.

Note

Where inherited classes are initialised by constructor this method indicates whether that was successful or not (e.g. FileStream)

Return values:

bool – true if valid, false if invalid

virtual size_t readBytes(char *buffer, size_t length) override

Read chars from stream into buffer.

Terminates if length characters have been read or timeout (see setTimeout). Returns the number of characters placed in the buffer (0 means no valid data found).

Note

Inherited classes may provide more efficient implementations without timeout.

virtual uint16_t readMemoryBlock(char *data, int bufSize) = 0

Read a block of memory.

Todo:

Should IDataSourceStream::readMemoryBlock return same data type as its bufSize param?

Parameters:
  • data – Pointer to the data to be read

  • bufSize – Quantity of chars to read

Return values:

uint16_t – Quantity of chars read

virtual int read() override

Read one character and moves the stream pointer.

Return values:

The – character that was read or -1 if none is available

virtual int peek() override

Read a character without advancing the stream pointer.

Return values:

int – The character that was read or -1 if none is available

inline virtual int seekFrom(int offset, SeekOrigin origin)

Change position in stream.

Note

This method is implemented by streams which support random seeking, such as files and memory streams.

Parameters:
  • offset

  • origin

Return values:

New – position, < 0 on error

inline virtual bool seek(int len)

Move read cursor.

Parameters:

len – Relative cursor adjustment

Return values:

bool – True on success.

virtual bool isFinished() = 0

Check if all data has been read.

Return values:

bool – True on success.

inline virtual int available()

Return the total length of the stream.

Return values:

int – -1 is returned when the size cannot be determined

inline virtual size_t write(uint8_t charToWrite) override

From Stream class: We don’t write using this stream.

Parameters:

charToWrite

inline virtual String id() const

Returns unique id of the resource.

Return values:

String – the unique id of the stream.

inline virtual String getName() const

Returns name of the resource.

Note

Commonly used to obtain name of file

Return values:

String

inline virtual MimeType getMimeType() const

Get MIME type for stream content.

Return values:

MimeType

virtual String readString(size_t maxLen) override

Overrides Stream method for more efficient reading.

Note

Stream position is updated by this call

inline virtual bool moveString(String &s)

Memory-based streams may be able to move content into a String.

If the operation is not supported by the stream, s will be invalidated and false returned.

Because a String object must have a NUL terminator, this will be appended if there is sufficient capacity. In this case, the method returns true.

If there is no capacity to add a NUL terminator, then the final character of stream data will be replaced with a NUL. The method returns false to indicate this.

Parameters:

sString object to move data into

Return values:

bool – true on success, false if there’s a problem.

size_t readBytes(char *buffer, size_t length)

Read chars from stream into buffer.

Terminates if length characters have been read or timeout (see setTimeout). Returns the number of characters placed in the buffer (0 means no valid data found).

Note

Inherited classes may provide more efficient implementations without timeout.

enum class SeekOrigin

Stream/file seek origins.

Values:

enumerator Start

SEEK_SET: Start of file.

enumerator Current

SEEK_CUR: Current position in file.

enumerator End

SEEK_END: End of file.

ReadWriteStream
class ReadWriteStream : public IDataSourceStream

Base class for read/write stream.

Subclassed by CircularBuffer, EndlessMemoryStream, HardwareSerial, IFS::FileStream, LimitedMemoryStream, MemoryDataStream, Ota::UpgradeOutputStream, OtaUpgrade::BasicStream, Storage::PartitionStream, StreamWrapper, USB::CDC::UsbSerial

Public Functions

inline virtual size_t write(uint8_t charToWrite) override

From Stream class: We don’t write using this stream.

Parameters:

charToWrite

virtual size_t write(const uint8_t *buffer, size_t size) = 0

Write chars to stream.

Note

Although this is defined in the Print class, ReadWriteStream uses this as the core output method so descendants are required to implement it

Parameters:
  • buffer – Pointer to buffer to write to the stream

  • size – Quantity of chars to write

Return values:

size_t – Quantity of chars written to stream

virtual size_t copyFrom(IDataSourceStream *source, size_t size = SIZE_MAX)

Copy data from a source stream.

Parameters:
  • source – Stream to read data from

  • size – Quantity of chars to write, determines size of intermediate buffer to use

Return values:

size_t – Quantity of chars actually written, may be less than requested

size_t write(uint8_t c) = 0

Writes a single character to output stream.

Parameters:

c – Character to write to output stream

Return values:

size_t – Quantity of characters written to output stream

inline size_t write(const char *str)

Writes a c-string to output stream.

Parameters:

str – Pointer to c-string

Return values:

size_t – Quantity of characters written to stream

size_t write(const uint8_t *buffer, size_t size)

Writes characters from a buffer to output stream.

Parameters:
  • buffer – Pointer to character buffer

  • size – Quantity of characters to write

Return values:

size_t – Quantity of characters written to stream

inline size_t write(const char *buffer, size_t size)

Writes characters from a buffer to output stream.

Parameters:
  • buffer – Pointer to character buffer

  • size – Quantity of characters to write

Return values:

size_t – Quantity of characters written to stream

Stream Classes
group stream

Data stream classes.

Typedefs

using FlashMemoryStream = FSTR::Stream

Provides a read-only stream buffer on flash storage.

using TemplateFlashMemoryStream = FSTR::TemplateStream

Template stream using content stored in flash.

class CircularBuffer : public ReadWriteStream
#include <CircularBuffer.h>

Circular stream class.

Base class for data source stream

class IDataSourceStream : public Stream
#include <DataSourceStream.h>

Base class for read-only stream.

Subclassed by FSTR::Stream, Graphics::SubStream, IFS::FWFS::ArchiveStream, MultiStream, ReadWriteStream, RtttlJsonListStream, SectionStream, SharedMemoryStream< T >, StreamTransformer, TemplateStream, UPnP::DescriptionStream, UrlencodedOutputStream, XorOutputStream

class EndlessMemoryStream : public ReadWriteStream
#include <EndlessMemoryStream.h>

Memory stream that stores unlimited number of bytes.

Memory is allocated on write and released when all written bytes have been read out. This behaviour differs from a circular buffer as the size is not fixed.

class FileStream : public IFS::FileStream
#include <FileStream.h>

File stream class.

class GdbFileStream : public IFS::FileStream
#include <GdbFileStream.h>

GDB File stream class to provide access to host files whilst running under debugger.

class DirectoryTemplate : public SectionTemplate
#include <DirectoryTemplate.h>

Directory stream class.

Subclassed by IFS::HtmlDirectoryTemplate, IFS::JsonDirectoryTemplate

class FileStream : public IFS::FsBase, public ReadWriteStream
#include <FileStream.h>

File stream class.

Subclassed by FileStream, GdbFileStream, HostFileStream

class HtmlDirectoryTemplate : public IFS::DirectoryTemplate
#include <HtmlDirectoryTemplate.h>

Read-only stream access to directory listing with HTML output.

class LimitedMemoryStream : public ReadWriteStream
#include <LimitedMemoryStream.h>

Memory stream operating on fixed-size buffer Once the limit is reached the stream will discard incoming bytes on write.

class LimitedWriteStream : public StreamWrapper
#include <LimitedWriteStream.h>

A stream wrapper class that limits the number of bytes that can be written. Helpful when writing on a file system or memory should be limited to the available size of the media.

class MemoryDataStream : public ReadWriteStream
#include <MemoryDataStream.h>

Read/write stream using expandable memory buffer.

This is intended to allow data to be streamed into it, then streamed back out at a later date.

It is not intended to have data continuously written in and read out; memory is not reclaimed as it is read.

Subclassed by JsonObjectStream, UPnP::ActionResponse::Stream

class MultiStream : public IDataSourceStream
#include <MultiStream.h>

Base class for read-only stream which generates output from multiple source streams.

Subclassed by MultipartStream, StreamChain

class ReadWriteStream : public IDataSourceStream
#include <ReadWriteStream.h>

Base class for read/write stream.

Subclassed by CircularBuffer, EndlessMemoryStream, HardwareSerial, IFS::FileStream, LimitedMemoryStream, MemoryDataStream, Ota::UpgradeOutputStream, OtaUpgrade::BasicStream, Storage::PartitionStream, StreamWrapper, USB::CDC::UsbSerial

template<typename T>
class SharedMemoryStream : public IDataSourceStream
#include <SharedMemoryStream.h>

Memory stream operating on fixed shared buffer.

One reason for templating this class is for distinction between char or const char types, to avoid dangerous casts. Elements may be structures or other types.

class StreamWrapper : public ReadWriteStream
#include <StreamWrapper.h>

An abstract class that provides a wrapper around a stream.

Subclassed by LimitedWriteStream, PartCheckerStream

class TemplateFileStream : public TemplateStream
#include <TemplateFileStream.h>

Template stream using content from the filesystem.

class TemplateStream : public IDataSourceStream
#include <TemplateStream.h>

Stream which performs variable-value substitution on-the-fly.

Template uses {varname} style markers which are replaced as the stream is read.

Note: There must be no whitespace after the opening brace. For example, { varname } will be emitted as-is without modification.

This allows inclusion of CSS fragments such as td { padding: 0 10px; } in HTML.

If necessary, use double-braces {{varname}} in templates and enable by calling setDoubleBraces(true).

Invalid tags, such as {"abc"} will be ignored, so JSON templates do not require special treatment.

Subclassed by FSTR::TemplateStream, SectionTemplate, TemplateFileStream

class XorOutputStream : public IDataSourceStream
#include <XorOutputStream.h>

Xors original stream content with the specified mask.

class HostFileStream : public IFS::FileStream
#include <HostFileStream.h>

Host File stream class.

class TemplateStream : public TemplateStream
#include <TemplateStream.hpp>

Template Flash memory stream class.

class Base64OutputStream : public StreamTransformer
#include <Base64OutputStream.h>

Read-only stream to emit base64-encoded content from source stream.

class ChunkedStream : public StreamTransformer
#include <ChunkedStream.h>

Read-only stream to obtain data using HTTP chunked encoding.

Used where total length of stream is not known in advance

class MultipartStream : public MultiStream
#include <MultipartStream.h>

Read-only stream for creating HTTP multi-part content.

class QuotedPrintableOutputStream : public StreamTransformer
#include <QuotedPrintableOutputStream.h>

Read-only stream that transforms bytes of data into quoted printable data stream.

class UrlencodedOutputStream : public IDataSourceStream
#include <UrlencodedOutputStream.h>

Represents key-value pairs as urlencoded string content.

class PartitionStream : public ReadWriteStream
#include <PartitionStream.h>

Stream operating directory on a Storage partition.

To support write operations, the target region must be erased first.

class JsonObjectStream : public MemoryDataStream
#include <JsonObjectStream.h>

JsonObject stream class.

class StreamTransformer : public IDataSourceStream

Class that can be used to transform streams of data on the fly.

Subclassed by Base64OutputStream, ChunkedStream, QuotedPrintableOutputStream

Public Functions

inline virtual void saveState()

A method that backs up the current state.

inline virtual void restoreState()

A method that restores the last backed up state.

Protected Functions

virtual size_t transform(const uint8_t *in, size_t inLength, uint8_t *out, size_t outLength) = 0

Inherited class implements this method to transform a block of data.

Note

Called with in = nullptr and inLength = 0 at end of input stream

Parameters:
  • in – source data

  • inLength – source data length

  • out – output buffer

  • outLength – size of output buffer

Return values:

size_t – number of output bytes written

Template Streams

Sming provides several classes to assist with serving dynamic content:

Basic Templating

The TemplateStream class is a stream which performs variable-value substitution using {varname} style markers, which are replaced as the stream is read.

You can find a simple demonstration of how this class is used in the Bootstrap Http Server sample application.

Note

There must be no whitespace after the opening brace. For example, { varname } will be emitted as-is without modification.

This allows inclusion of CSS fragments such as td { padding: 0 10px; } in HTML without resorting to double-braces.

If necessary, use double-braces {{varname}} in your template and call TemplateStream::setDoubleBraces() (true).

Invalid tags, such as {"abc"} will be ignored, so JSON templates do not require special treatment.

Variable values can be set using TemplateStream::setVar() or TemplateStream::setVars(). These are stored in a HashMap which can be accessing directly via TemplateStream::variables().

To support calculated values and external lookups, an optional callback may be provided via TemplateStream::onGetValue(). This is invoked only if a variable is not found in the map.

Another option is to use TemplateStream as a base class and override the TemplateStream::getValue() method.

Important

If required, text must be escaped appropriately for the output format. For example, encoding reserved HTML characters can be handled using Format::Html::escape().

Advanced Templating
Introduction

The SectionTemplate class extends TemplateStream to provide more advanced dataset processing capabilities. It is intended to be used as the base class for a data provider.

One such implementation is the IFS::DirectoryTemplate class. The Basic IFS sample demonstrates how it can be used to provide a formatted directory listing in multiple formats, using a different template for each format.

The Basic Templates sample illustrates a similar approach using data from CSV data files.

If the output format requires escaping, create an instance of the appropriate Format::Formatter and call SectionTemplate::setFormatter(). If providing custom values via callback, obtain the current formatter via SectionTemplate::formatter() class and call the escape method. Note that for performance reasons this is not done automatically as often variable values do not require escaping. User-provided values or filenames must always be properly escaped.

Sections

Templates typically contain multiple sections. The IFS::DirectoryTemplate, for example, uses 3 sections for header, content and footer. The header and footer are emitted exactly once, but the content section is repeated for each available data record.

The SectionStream class is used internally so that all sections can be provided within a single file.

Sections are (by default) marked {SECTION}{/SECTION}. Everything outside of these markers is ignored, so can contain comments.

Using SectionTemplate

Implementations should provide the following methods:

nextRecord

This method is called before a new content record is about to be output. Here’s the annotated IFS::DirectoryTemplate implementation:

// Return true if we have a new valid record, false if not
bool nextRecord() override
{
    // Content section we fetch the next directory record, if there is one
    if(sectionIndex() == 1) {
        return directory->next();
    }

    // This code emits the header and footer sections exactly once
    // Returning false suppresses their output completely
    return recordIndex() < 0;
}

This sets up the ‘current’ directory information record.

getValue

Lookup values for a given field:

String getValue(const char* name) override
{
    // return ...
}

Important

If required, text must be escaped appropriately for the output format. Use SectionTemplate::formatter() to obtain the current For example, encoding reserved HTML characters can be handled using Format::Html::escape().

Control language

A basic control language is implemented using ! escaped tags. Commands may have zero or more arguments, separated by :.

  • Numbers must be decimal and start with a digit, e.g. 11 or 5.6

  • Strings must be quoted “…”

  • Sub-expressions must be contained in braces {…}

Anything else is treated as a variable name. Variable names beginning with $ are reserved for internal use. The following values are currently defined:

$section The current section index $record The current record index

Conditional if/else/endif statements may be nested.

This is the current command list:

  • {!int:A} Output A as integer

  • {!float:A} Output A as float

  • {!string:A} Output A as quoted string

  • {!mime_type:A} Get MIME type string for a filename

  • {!replace:A:B:C} Copy of A with all occurrences of B replaced with C

  • {!length:A} Number of characters in A

  • {!pad:A:B:C} Copy of A padded to at least B characters with C (default is space). Use -ve B to left-pad. C

  • {!repeat:A:B} Repeat A, number of iterations is B

  • {!kb:A} Convert A to KB

  • {!ifdef:A} emit block if A is not zero-length

  • {!ifdef:A} emit block if A is zero-length

  • {!ifeq:A:B} emit block if A == B

  • {!ifneq:A:B} emit block if A != B

  • {!ifgt:A:B} emit block if A > B

  • {!iflt:A:B} emit block if A < B

  • {!ifge:A:B} emit block if A >= B

  • {!ifle:A:B} emit block if A <= B

  • {!ifbtw:A:B:C} emit block if B <= A <= C

  • {!ifin:A:B} emit block if A contains B

  • {!ifin:A:B} emit block if A does not contain B

  • {!else}

  • {!endif}

  • {!add:A:B} A + B

  • {!sub:A:B} A - B

  • {!goto:A} move to section A

  • {!count:A} emit number of records in section A

  • {!index:A} emit current record index for section A

Note

See Sming/Core/Data/Streams/SectionTemplate.h for an up-to-date list of commands and internal variables.

Here’s an excerpt from the Basic_IFS sample, displaying information for a single file:

{!iflt:$record:100} <!-- If $record < 100 -->
    <tr>
        <td>{$record}</td>
        <td>{file_id}</td>
        <td><a href="{path}{name}"><span style='font-size:20px'>{icon}</span> {name}</a></td>
        <td>{!mime_type:name}</td>
        <td>{modified}</td>
        {!ifin:attr:"D"} <!-- Value of 'attr' variable contains "D" ->
            <td></td><td></td>
        {!else}
            <td>{size}<br>{!kb:size}&nbsp;KB</td>
            <td>{original_size}<br>{!kb:original_size}&nbsp;KB</td>
        {!endif}
        <td>{!replace:attr_long:", ":"<br>"}</td>
        <td>{compression}</td>
        <td>{access_long}</td>
    </tr>
{!else} <!-- $record >= 100 -->
    Too many records {$record}
{!endif}
API Reference
class TemplateStream : public IDataSourceStream

Stream which performs variable-value substitution on-the-fly.

Template uses {varname} style markers which are replaced as the stream is read.

Note: There must be no whitespace after the opening brace. For example, { varname } will be emitted as-is without modification.

This allows inclusion of CSS fragments such as td { padding: 0 10px; } in HTML.

If necessary, use double-braces {{varname}} in templates and enable by calling setDoubleBraces(true).

Invalid tags, such as {"abc"} will be ignored, so JSON templates do not require special treatment.

Subclassed by FSTR::TemplateStream, SectionTemplate, TemplateFileStream

Public Types

using Variables = HashMap<String, String>

Maps variable names to values.

using GetValueDelegate = Delegate<String(const char *name)>

Callback type to return calculated or externally stored values.

Public Functions

inline TemplateStream(IDataSourceStream *stream, bool owned = true)

Create a template stream.

Parameters:
  • stream – source of template data

  • owned – If true (default) then stream will be destroyed when complete

inline virtual StreamType getStreamType() const override

Get the stream type.

Return values:

StreamType – The stream type.

virtual uint16_t readMemoryBlock(char *data, int bufSize) override

Read a block of memory.

Todo:

Should IDataSourceStream::readMemoryBlock return same data type as its bufSize param?

Parameters:
  • data – Pointer to the data to be read

  • bufSize – Quantity of chars to read

Return values:

uint16_t – Quantity of chars read

virtual int seekFrom(int offset, SeekOrigin origin) override

Change position in stream.

Note

This method is implemented by streams which support random seeking, such as files and memory streams.

Parameters:
  • offset

  • origin

Return values:

New – position, < 0 on error

inline virtual bool isFinished() override

Check if all data has been read.

Return values:

bool – True on success.

inline void setVar(const String &name, const String &value)

Set value of a variable in the template file.

Note

Sets and existing variable or adds a new variable if variable does not already exist

Parameters:
  • name – Name of variable

  • value – Value to assign to the variable

inline void setVars(const Variables &vars)

Set multiple variables in the template file.

Parameters:

vars – Template Variables

inline Variables &variables()

Get the template variables.

Return values:

TemplateVariables – Reference to the template variables

inline virtual String getName() const override

Returns name of the resource.

Note

Commonly used to obtain name of file

Return values:

String

inline void onGetValue(GetValueDelegate callback)

Set a callback to obtain variable values.

Parameters:

callback – Invoked only if variable name not found in map

inline void enableOutput(bool enable)

During processing applications may suppress output of certain sections by calling this method from within the getValue callback.

inline bool isOutputEnabled() const

Determine if stream output is active.

Used by SectionTemplate class when processing conditional tags.

inline void setDoubleBraces(bool enable)

Use two braces {{X}} to mark tags.

Parameters:

enable – true: use two braces, false (default): single brace only

virtual String evaluate(char *&expr)

Evaluate a template expression.

This method is overridden by SectionTemplate to support more complex expressions.

Parameters:

expr – IN: First character after the opening brace(s) OUT: First character after the closing brace(s)

Return values:

String – Called internally and an opening brace (“{” or “{{”) has been found. Default behaviour is to locate the closing brace(s) and interpret the bounded text as a variable name, which is passed to getValue.

inline String eval(String expr)

Evaluate an expression in-situ.

Parameters:

expr – Expression to evaluate

Return values:

String

virtual String getValue(const char *name)

Fetch a templated value.

Parameters:

name – The variable name

Return values:

String – value, invalid to emit tag unprocessed

class SectionTemplate : public TemplateStream

Provides enhanced template tag processing for use with a SectionStream.

Subclassed by IFS::DirectoryTemplate

Public Types

using GetValue = Delegate<String(const char *name)>

Application callback to process additional fields.

Note

Applications should call escape() if required before returning content.

Param templateStream:

Param name:

Field name, never null

Retval String:

The field value

Public Functions

inline void onGetValue(GetValue callback)

Set a callback to be invoked.

Alternative to subclassing.

inline void setFormatter(Formatter &formatter)

Associate a text format with this template stream.

Parameters:

formatter – Provide formatter so we can call escape(), etc. as required

inline Formatter &formatter() const

Get the stream format.

Return values:

Formatter& – The formatter in effect. Default is :cpp:class:Format::Standard.

inline virtual MimeType getMimeType() const override

Get the MIME type associated with this template stream.

Return values:

MimeType – As defined by the formatter. Default is MIME_TEXT.

inline const SectionStream &stream() const

Access the underlying section stream.

Provided for debugging and other purposes. Applications should not use this method.

Return values:

SectionStream& – Wraps source stream provided in constructor

inline int sectionIndex() const

Get the index for the current section.

Return values:

int – Indices are 0-based, returns -1 if ‘Before Start’

inline uint8_t sectionCount() const

Get number of sections in source stream.

Return values:

uint8_t – Source is scanned in constructor so this is always valid

inline int recordIndex() const

Get current record index.

Return values:

int – Indices are 0-based, returns -1 if ‘Before Start’

bool gotoSection(uint8_t index)

Discard current output and change current section.

Parameters:

uint8_t – Index of section to move to

Return values:

bool – true on success, false if section index invalid

inline void onNextRecord(NextRecord callback)

Set a callback to be invoked when a new record is required.

Can be used as alternative to subclassing.

virtual String evaluate(char *&expr) override

Evaluate a template expression.

This method is overridden by SectionTemplate to support more complex expressions.

Parameters:

expr – IN: First character after the opening brace(s) OUT: First character after the closing brace(s)

Return values:

String – Called internally and an opening brace (“{” or “{{”) has been found. Default behaviour is to locate the closing brace(s) and interpret the bounded text as a variable name, which is passed to getValue.

virtual String getValue(const char *name) override

Fetch a templated value.

Parameters:

name – The variable name

Return values:

String – value, invalid to emit tag unprocessed

class SectionStream : public IDataSourceStream

Presents each section within a source stream as a separate stream.

Sections are (by default) marked {!SECTION} … {/SECTION} This is typically used with templating but can be used with any stream type provided the tags do not conflict with content.

Public Types

using NextSection = Delegate<void()>

Application notification callback when section changes.

using NextRecord = Delegate<bool()>

Application callback to move to next record.

Retval bool:

true to emit section, false to skip

Public Functions

inline SectionStream(IDataSourceStream *source, uint8_t maxSections = 5)

Construct a section stream with default options.

inline SectionStream(IDataSourceStream *source, uint8_t maxSections, const String &startTag, const String &endTag)

Construct a section stream.

Parameters:
  • source – Contains all section data, must support random seeking

  • startTag – Unique text used to mark start of a section

  • endTag – Marks end of a section

inline virtual int available() override

Return the total length of the stream.

Return values:

int – -1 is returned when the size cannot be determined

virtual uint16_t readMemoryBlock(char *data, int bufSize) override

Read a block of memory.

Todo:

Should IDataSourceStream::readMemoryBlock return same data type as its bufSize param?

Parameters:
  • data – Pointer to the data to be read

  • bufSize – Quantity of chars to read

Return values:

uint16_t – Quantity of chars read

virtual int seekFrom(int offset, SeekOrigin origin) override

Change position in stream.

Note

This method is implemented by streams which support random seeking, such as files and memory streams.

Parameters:
  • offset

  • origin

Return values:

New – position, < 0 on error

inline virtual bool isFinished() override

Check if all data has been read.

Return values:

bool – True on success.

inline size_t count() const

Get number of sections in this stream.

inline const Section *getSection() const

Get description of the current section.

Return values:

Section* – The section information, or nullptr if there is no current section

inline const Section *getSection(unsigned index) const

Get description for any section given its index.

Return values:

Section* – The section information, or nullptr if section was not found

inline void onNextSection(NextSection callback)

Register a callback to be invoked when moving to a new section.

inline void onNextRecord(NextRecord callback)

Register a callback to be invoked when moving to a new record.

bool gotoSection(uint8_t index)

Goto a new section immediately.

inline bool setNewSection(int8_t index)

Goto a new section after current tag has been processed.

struct Section
Date and Time
DateTime class
class DateTime

Date and time class.

Date and time functions mostly work with Unix time, the quantity of seconds since 00:00:00 1970-01-01. There is no support for leap seconds which are added (and in theory, removed) occasionally to compensate for earth rotation variation. This means that timespan calculation and free-running clocks may be inaccurate if they span leap seconds. To facilitate leap seconds, reference must be made to leap second table. This will not be done within the Sming framework and must be handled by application code if required.

Note

Sming uses 32-bit signed integer for its time_t data type which supports a range of +/-68 years. This means Sming is susceptible to Year 2038 problem.

Public Functions

inline DateTime()

Instantiate an uninitialised date and time object.

inline DateTime(time_t time)

Instantiate a date and time object from a Unix timestamp.

Parameters:

time – Unix time to assign to object

inline operator time_t()

Get current Unix time.

Return values:

time_t – Quantity of seconds since 00:00:00 1970-01-01

void setTime(time_t time)

Set time using Unix timestamp.

Parameters:

time – Unix time to set object time to

inline void setTime(uint8_t sec, uint8_t min, uint8_t hour, uint8_t day, uint8_t month, uint16_t year)

Set time using time and date component values.

Parameters:
  • sec – Seconds

  • min – Minutes

  • hour – Hour

  • day – Day of month

  • month – Month (0=Jan, 11=Dec)

  • year – Year

bool fromHttpDate(const String &httpDate)

Parse a HTTP full date and set time and date.

Note

Also supports obsolete RFC 850 date format, e.g. Sunday, 06-Nov-94 08:49:37 GMT where 2 digit year represents range 1970-2069

Note

GMT suffix is optional and is always assumed / ignored

Parameters:

httpDate – HTTP full date in RFC 1123 format, e.g. Sun, 06 Nov 1994 08:49:37 GMT

Return values:

bool – True on success

bool isNull()

Check if time date object is initialised.

Return values:

True – if object has no value. False if initialised.

time_t toUnixTime()

Get Unix time.

Note

Unix time does not account for leap seconds. To convert Unix time to UTC requires reference to a leap second table.

Return values:

time_t – Unix time, quantity of seconds since 00:00:00 1970-01-01

String toShortDateString()

Get human readable date.

Return values:

String – Date in requested format, e.g. DD.MM.YYYY

String toShortTimeString(bool includeSeconds = false)

Get human readable time.

Parameters:

includeSeconds – True to include seconds (Default: false)

Return values:

String – Time in format hh:mm or hh:mm:ss

String toFullDateTimeString()

Get human readable date and time.

Return values:

String – Date and time in format DD.MM.YYYY hh:mm:ss

String toISO8601()

Get human readable date and time.

Return values:

String – Date and time in format YYYY-MM-DDThh:mm:ssZ

String toHTTPDate()

Get human readable date and time.

Return values:

String – Date and time in format DDD, DD MMM YYYY hh:mm:ss GMT

void addMilliseconds(long add)

Add time to date time object.

Parameters:

add – Quantity of milliseconds to add to object

String format(const char *formatString)

Create string formatted with time and date placeholders.

Note

Uses strftime style formatting, e.g. format(“Today is %a, %d %b %Y”) returns “Today is Mon, 10 Dec 2018”

Note

Localisation may be implemented in libsming at compile time by setting LOCALE, e.g. LOCALE=LOCALE_DE_DE

Note

Default localisation is EN_GB

Note

Formatting parameters

Param

Description

Locale

%a

Abbreviated weekday name

*

%A

Full weekday name

*

%b

Abbreviated month name

*

%B

Full month name

*

%c

Locale preferred date and time format

*

%C

Century number (2 digits)

%d

Day of month as decimal number with leading zero (2 digits)

%D

US date format (mm/dd/yyyy)

%e

Day of month as decimal number with leading space (2 digits)

%F

ISO 8601 date format (YYYY-mm-dd)

%h

Equivalent to %b

*

%H

Hour as a decimal number using a 24-hour clock (range 00 to 23)

%I

Hour as a decimal number using a 12-hour clock (range 00 to 12)

%j

Day of the year as a decimal number (range 001 to 366)

%m

Month as a decimal number (range 01 to 12)

%M

Minute as a decimal number (range 00 to 59)

%n

Newline

%p

Meridiem indicator: AM or PM. Midnight is AM and noon is PM

%r

Locale 12-hour clock time notation. This is equivalent to %I:%M:%S %p

*

%R

Time in 24-hour notation (HH:MM)

%S

Second as a decimal number (range 00 to 60)

%t

Horizontal tab

%T

Time in 24-hour notation (HH:MM:SS)

%u

Day of the week as a decimal (range 1 to 7, Monday is 1)

%U

Week number as a decimal number (range 00 to 53, first Sunday as the first day of week 01)

%V

ISO 8601 week number as a decimal number (range 01 to 53, where week 1 is the first week including a Thursday)

%w

Day of the week as a decimal (range 0 to 6, Sunday is 0)

%W

Week number as a decimal number (range 00 to 53, first Monday as the first day of week 01)

%x

Locale preferred date representation

*

%X

Locale preferred time representation

*

%y

Year as a decimal number without a century (range 00 to 99)

%Y

Year as a decimal number (range 1970 to …)

%%

Percent sign

Parameters:

formatStringString including date and time formatting

Return values:

String – Formatted string

inline String format(const String &formatString)

Create string formatted with time and date placeholders.

Note

see format(const char*) for parameter details

Parameters:

formatStringString including date and time formatting

Return values:

String – Formatted string

Public Members

uint8_t Hour = 0

Hour (0-23)

uint8_t Minute = 0

Minute (0-59)

uint8_t Second = 0

Second (0-59)

uint16_t Milliseconds = 0

Milliseconds (0-999)

uint8_t Day = 0

Day of month (1-31)

uint8_t DayofWeek = 0

Day of week (0-6 Sunday is day 0)

uint16_t DayofYear = 0

Day of year (0-365)

uint8_t Month = 0

Month (0-11 Jan is month 0)

uint16_t Year = 0

Full Year number.

Public Static Functions

static void fromUnixTime(time_t timep, uint8_t *psec, uint8_t *pmin, uint8_t *phour, uint8_t *pday, uint8_t *pwday, uint8_t *pmonth, uint16_t *pyear)

Convert from Unix time to individual time components.

Note

This is a more compact version of the C library localtime function

Note

Pass the Unix timedate value and pointers to existing integers. The integers are updated with the converted values

Note

This static function may be used without instantiating a DateTime object, e.g. DateTime::convertFromUnixTime(…);

Note

32-bit Unix time has year 2036 issue.

Note

Unix time does not account for leap seconds. To convert Unix time to UTC requires reference to a leap second table.

Note

All of the return values are optional, specify nullptr if not required

Parameters:
  • timep – Unix time date value to convert

  • psec – Pointer to integer to hold resulting seconds

  • pmin – Pointer to integer to hold resulting minutes

  • phour – Pointer to integer to hold resulting hour

  • pday – Pointer to integer to hold resulting day of month

  • pwday – Pointer to integer to hold resulting day of week

  • pmonth – Pointer to integer to hold resulting month

  • pyear – Pointer to integer to hold resulting year

static time_t toUnixTime(uint8_t sec, uint8_t min, uint8_t hour, uint8_t day, uint8_t month, uint16_t year)

Convert from individual time components to Unix time.

Note

Seconds, minutes, hours and days may be any value, e.g. to calculate the value for 300 days since 1970 (epoch), set day=300

Note

This static function may be used without instantiating a DateTime object, e.g. time_t unixTime = DateTime::convertToUnixTime(…);

Note

32-bit Unix time is valid between 1901-12-13 and 03:14:07 2038-01-19

Note

Unix time does not account for leap seconds. To convert Unix time to UTC requires reference to a leap second table.

Parameters:
  • sec – Seconds

  • min – Minutes

  • hour – Hours

  • day – Days

  • month – Month (0-11, Jan=0, Feb=1, …Dec=11)

  • year – Year (1901-2036), either full 4 digit year or 2 digits for 1970-2036

System Clock

Enums

enum TimeZone

Time zones.

Values:

enumerator eTZ_UTC

Use universal time coordinate (UTC)

enumerator eTZ_Local

Use local time.

Variables

SystemClockClass SystemClock

Global instance of system clock object.

Note

Use SystemClock.function to access system clock functions

Note

Example:

SystemClock.setTimeZone(+9.5); //Darwin, Australia

class SystemClockClass
#include <SystemClock.h>

Public Functions

time_t now(TimeZone timeType = eTZ_Local) const

Get the current date and time.

Parameters:

timeType – Time zone to use (UTC / local)

Return values:

DateTime – Current date and time

bool setTime(time_t time, TimeZone timeType)

Set the system clock’s time.

Note

System time is always stored as UTC time. If setting using the value retrieved from a time server using NTP, specify eTZ_UTC. If passing a local value using eTZ_Local, ensure that the time zone has been set correctly as it will be converted to UTC before storing.

Parameters:
  • time – Unix time to set clock to (quantity of seconds since 00:00:00 1970-01-01)

  • timeType – Time zone of Unix time, i.e. is time provided as local or UTC?

String getSystemTimeString(TimeZone timeType = eTZ_Local) const

Get current time as a string.

Note

Date separator may be changed by adding #define DT_DATE_SEPARATOR "/" to source code

Parameters:

timeType – Time zone to present time as, i.e. return local or UTC time

Return values:

String – Current time in format: dd.mm.yy hh:mm:ss

bool setTimeZoneOffset(int seconds)

Sets the local time zone offset.

Parameters:

seconds – Offset from UTC of local time zone in seconds (-720 < offset < +720)

Return values:

bool – true on success, false if value out of range

inline bool setTimeZone(float localTimezoneOffset)

Set the local time zone offset in hours.

Parameters:

localTimezoneOffset – Offset from UTC of local time zone in hours (-12.0 < offset < +12.0)

Return values:

bool – true on success, false if value out of range

inline int getTimeZoneOffset() const

Get the current time zone offset.

Return values:

int – Offset in seconds from UTC

inline bool isSet() const

Determine if setTime() has been called yet.

Note

Indicates whether time returned can be relied upon

Private Members

int timeZoneOffsetSecs = 0
bool timeSet = false
File System

Defines

CHECK_FS(_method)

Typedefs

using file_t = IFS::FileHandle
using FileHandle = IFS::FileHandle
using DirHandle = IFS::DirHandle
using FileOpenFlag = IFS::OpenFlag
using FileOpenFlags = IFS::OpenFlags
using FileAttribute = IFS::FileAttribute
using FileAttributes = IFS::FileAttributes
using FileStat = IFS::Stat
using FileNameStat = IFS::NameStat
using File = IFS::File
using Directory = IFS::Directory

Functions

inline IFS::FileSystem *getFileSystem()

Get the currently active file system, if any.

Return values:

IFS::FileSystem*

void fileSetFileSystem(IFS::IFileSystem *fileSystem)

Sets the currently active file system.

Note

Any existing filing system is destroyed. Typically the filing system implementation has helper functions which create and initialise the file system to a valid state. The last step is to call this function to make it active. Call this function with nullptr to deactivate the filing system.

Parameters:

fileSystem

inline void fileFreeFileSystem()
bool fileMountFileSystem(IFS::IFileSystem *fs)

Mount a constructed filesystem with debug messages.

bool fwfs_mount()

Mount the first available FWFS volume.

Return values:

bool – true on success

bool fwfs_mount(Storage::Partition partition)

Mount SPIFFS volume from a specific partition.

Return values:

bool – true on success

inline IFS::FileSystem *fileMountArchive(const String &filename)

Mount a backup archive.

Parameters:

filename – Path to archive file

Return values:

IFS::FileSystem* – Mounted filesystem. Caller must delete when finished.

template<typename T>
inline FileHandle fileOpen(const T &path, FileOpenFlags flags = File::ReadOnly)

Open file by path.

Parameters:
  • path – Full path to file

  • flags – Mode to open file

Return values:

file – File handle or negative error code

inline int fileClose(FileHandle file)

Clode file.

Note

File Handle is returned from fileOpen function

Parameters:

file – Handle of file to close

inline int fileWrite(FileHandle file, const void *data, size_t size)

Write to file.

Parameters:
  • file – File handle

  • data – Pointer to data to write to file

  • size – Quantity of data elements to write to file

Return values:

int – Quantity of data elements actually written to file or negative error code

inline int fileTouch(FileHandle file)

Update file modification time.

Parameters:

file – File handle

Return values:

int – Error code

inline int fileRead(FileHandle file, void *data, size_t size)

Read from file.

Parameters:
  • file – File handle

  • data – Pointer to data buffer in to which to read data

  • size – Quantity of data elements to read from file

Return values:

int – Quantity of data elements actually read from file or negative error code

inline file_offset_t fileSeek(FileHandle file, file_offset_t offset, SeekOrigin origin)

Position file cursor.

Parameters:
  • file – File handle

  • offset – Quantity of bytes to move cursor

  • origin – Position from where to move cursor

Return values:

file_offset_t – Offset within file or negative error code

inline bool fileIsEOF(FileHandle file)

Check if at end of file.

Parameters:

file – File handle

Return values:

bool – true if at end of file

inline file_offset_t fileTell(FileHandle file)

Get position in file.

Parameters:

file – File handle

Return values:

file_offset_t – Read / write cursor position or error code

inline int fileFlush(FileHandle file)

Flush pending writes.

Parameters:

file – File handle

Return values:

int – Size of last file written or error code

inline String fileGetErrorString(int err)

get the text for a returned error code

Parameters:

err

Return values:

String

template<typename TFileName>
inline int fileSetContent(const TFileName &fileName, const char *content, size_t length)

Create or replace file with defined content.

Note

This function creates a new file or replaces an existing file and populates the file with the content of a c-string buffer.

Parameters:
  • fileName – Name of file to create or replace

  • content – Pointer to c-string containing content to populate file with

  • length – Number of characters to write

Return values:

int – Number of bytes transferred or error code

template<typename TFileName, typename TContent>
inline int fileSetContent(const TFileName &fileName, TContent content)
template<typename TFileName>
inline file_size_t fileGetSize(const TFileName &fileName)

Get size of file.

Parameters:

fileName – Name of file

Return values:

file_size_t – Size of file in bytes, 0 on error

inline int fileTruncate(FileHandle file, file_size_t newSize)

Truncate (reduce) the size of an open file.

Note

In POSIX ftruncate() can also make the file bigger, however SPIFFS can only reduce the file size and will return an error if newSize > fileSize

Parameters:
  • file – Open file handle, must have Write access

  • newSize

Return values:

int – Error code

inline int fileTruncate(FileHandle file)

Truncate an open file at the current cursor position.

Parameters:

file – Open file handle, must have Write access

Return values:

int – Error code

template<typename TFileName>
int fileTruncate(const TFileName &fileName, file_size_t newSize)

Truncate (reduce) the size of a file.

Note

In POSIX truncate() can also make the file bigger, however SPIFFS can only reduce the file size and will return an error if newSize > fileSize

Parameters:
  • fileName

  • newSize

Return values:

int – Error code

inline int fileRename(const char *oldName, const char *newName)

Rename file.

Parameters:
  • oldName – Original name of file to rename

  • newName – New name for file

Return values:

int – Error code

inline int fileRename(const String &oldName, const String &newName)
template<typename TFileName>
String fileGetContent(const TFileName &fileName)

Read content of a file.

Note

After calling this function the content of the file is placed in to a string. The result will be an invalid String (equates to false) if the file could not be read. If the file exists, but is empty, the result will be an empty string “”.

Parameters:

fileName – Name of file to read from

Return values:

StringString variable in to which to read the file content

template<typename TFileName>
inline size_t fileGetContent(const TFileName &fileName, char *buffer, size_t bufSize)

Read content of a file.

Note

After calling this function the content of the file is placed in to a c-string Ensure there is sufficient space in the buffer for file content plus extra trailing null, i.e. at least bufSize + 1 Always check the return value!

Note

Returns 0 if the file could not be read

Parameters:
  • fileName – Name of file to read from

  • buffer – Pointer to a character buffer in to which to read the file content

  • bufSize – Quantity of bytes to read from file

Return values:

size_t – Quantity of bytes read from file or zero on failure

template<typename TFileName>
inline size_t fileGetContent(const TFileName &fileName, char *buffer)
inline int fileStats(const char *fileName, FileStat &stat)

Get file statistics.

Parameters:
  • name – File name

  • stat – Pointer to SPIFFS statistic structure to populate

Return values:

int – Error code

inline int fileStats(const String &fileName, FileStat &stat)
inline int fileStats(FileHandle file, FileStat &stat)

brief Get file statistics

Parameters:
  • file – File handle

  • stat – Pointer to SPIFFS statistic structure to populate

Return values:

int – Error code

inline int fileDelete(const char *fileName)

Delete file.

Parameters:

name – Name of file to delete

Return values:

int – Error code

inline int fileDelete(const String &fileName)
inline int fileDelete(FileHandle file)

Delete file.

Parameters:

file – handle of file to delete

Return values:

int – Error code

inline bool fileExist(const char *fileName)

Check if a file exists on file system.

Parameters:

fileName – Full path to file to check for

Return values:

bool – true if file exists

inline bool fileExist(const String &fileName)
inline bool dirExist(const char *dirName)

Check if a directory exists on file system.

Parameters:

dirName – Full path to directory to check for

Return values:

bool – true if directory exists

inline bool dirExist(const String &dirName)
inline int fileOpenDir(const char *dirName, DirHandle &dir)

Open a named directory for reading.

Parameters:
  • name – Name of directory to open, empty or “/” for root

  • dir – Directory object

Return values:

int – Error code

inline int fileOpenDir(const String &dirName, DirHandle &dir)
inline int fileOpenRootDir(DirHandle &dir)
inline int fileCloseDir(DirHandle dir)

close a directory object

Parameters:

dir – directory to close

Return values:

int – Error code

inline int fileReadDir(DirHandle dir, FileStat &stat)

Read a directory entry.

Parameters:
  • dir – The directory object returned by fileOpenDir()

  • stat – The returned information, owned by DirHandle object

Return values:

int – Error code

inline int fileRewindDir(DirHandle dir)

Rewind to start of directory entries.

Parameters:

dir – The directory object returned by fileOpenDir()

Return values:

int – Error code

inline int fileGetSystemInfo(IFS::FileSystem::Info &info)

Get basic file system information.

Return values:

int – Error code

IFS::FileSystem::Type fileSystemType()

Get the type of file system currently mounted (if any)

Return values:

FileSystemType – the file system type

inline int fileSystemFormat()

Format the active file system.

Return values:

int – Error code

inline int fileSystemCheck()

Perform a consistency check/repair on the active file system.

Return values:

int – 0 if OK, < 0 unrecoverable errors, > 0 repairs required

inline int fileSetACL(FileHandle file, const IFS::ACL &acl)

Set access control information.

Parameters:
  • file – File handle

  • acl

Return values:

int – Error code

template<typename T>
int fileSetAttr(const T &filename, FileAttributes attr)

Set file attributes.

Parameters:
  • filename

  • attr

Return values:

int – Error code

inline int fileSetTime(FileHandle file, time_t mtime)

Set access control information for file.

Note

any writes to file will reset this to current time

Parameters:

file – handle to open file, must have write access

Return values:

int – Error code

template<typename T>
int createDirectory(const T &path)

Create a directory.

Parameters:

path – Path to directory

Return values:

int – Error code

template<typename T>
int createDirectories(const T &path)

Create a directory and all required parent directories.

Parameters:

path – Path to directory

Return values:

int – Error code

Variables

constexpr int FS_OK = IFS::FS_OK
namespace SmingInternal

Variables

IFS::FileSystem *activeFileSystem

Global file system instance.

Filing system implementations should use helper functions to setup and initialise a filing system. If successful, call fileSetFileSystem() to make it active. This ensures that any active filing system is dismounted destroyed first.

Platform Support

WiFi Access Point
group wifi_ap

Control and monitoring of WiFi access point interface.

Todo:

How is wifi access point dhcp controlled?

Note

The WiFi access point interface provides a WiFi network access point. Control of WiFi AP including WiFi SSID and password and IP address.

Variables

AccessPointClass &WifiAccessPoint

Global instance of WiFi access point object.

Note

Use WifiAccessPoint.function to access WiFi access point functions

Note

Example:

if(WifiAccessPoint.config("ESP_AP", AUTH_OPEN))
           WifiAccessPoint.enable(true);

class AccessPointClass
#include <AccessPoint.h>

Access point class.

Public Functions

virtual void enable(bool enabled, bool save = false) = 0

Enable or disable WiFi AP.

Parameters:
  • enabled – True to enable AP. False to disable.

  • save – True to save operational mode to flash, False to set current operational mode only

virtual bool isEnabled() const = 0

Get WiFi AP enable status.

Return values:

bool – True if WiFi AP enabled.

virtual bool config(const String &ssid, String password, WifiAuthMode mode, bool hidden = false, int channel = 7, int beaconInterval = 200) = 0

Configure WiFi AP.

Parameters:
  • ssid – WiFi AP SSID

  • password – WiFi AP password

  • mode – WiFi AP mode

  • hidden – True to hide WiFi AP (Default: Visible)

  • channel – WiFi AP channel (Default: 7)

  • beaconInterval – WiFi AP beacon interval in milliseconds (Default: 200ms)

Return values:

bool – True on success

virtual IpAddress getIP() const = 0

Get WiFi AP IP address.

Return values:

IpAddress – WiFi AP IP address

virtual bool setIP(IpAddress address) = 0

Set WiFi AP IP address.

Parameters:

address – New IP address for WiFi AP

Return values:

bool – True on success

virtual MacAddress getMacAddress() const = 0

Get WiFi AP MAC address.

Return values:

MacAddress

String getMAC(char sep = '\0') const

Get WiFi AP MAC address.

Parameters:

sep – separator between bytes (e.g. ‘:’)

Return values:

String – WiFi AP MAC address

virtual bool setMacAddress(const MacAddress &addr) const = 0

Set Access Point MAC address.

Must be called from init() before activating Access Point. Espressif place certain limitations on MAC addresses:

Bit 0 of the first byte of the MAC address can not be 1. For example:

OK: “1a:XX:XX:XX:XX:XX” NOT OK: “15:XX:XX:XX:XX:XX”

Parameters:

addr – The new MAC address

Return values:

bool – true on success

virtual IpAddress getNetworkMask() const = 0

Get WiFi AP network mask.

Return values:

IpAddress – WiFi AP network mask

virtual IpAddress getNetworkGateway() const = 0

Get WiFi AP default gateway.

Return values:

IpAddress – WiFi AP default gateway

virtual IpAddress getNetworkBroadcast() const = 0

Get WiFi AP broadcast address.

Return values:

IpAddress – WiFi AP broadcast address

inline bool isLocal(IpAddress address)

Determine if the given address is on the same subnet.

Note

Use to prevent external access to services

Parameters:

address

Return values:

bool – true if address is local

virtual String getSSID() const = 0

Get WiFi access point SSID.

Return values:

String – WiFi access point SSID

virtual String getPassword() const = 0

Get WiFi access point password.

Return values:

String – WiFi access point password

virtual std::unique_ptr<StationList> getStations() const = 0

Gets a list of stations connected to the access point.

Return values:

StationList

Ethernet

Currently only supported on ESP32 using embedded MAC.

class EmbeddedEthernet : public Ethernet::IdfService

Ethernet provider using ESP32 embedded MAC. Requires an external PHY.

See https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32/api-reference/network/esp_eth.html.

These are the reduced (RMII) PHY connections. Note that the high-speed signals may not be re-allocated.

    Signal    GPIO  Direction
    ------    ----  ---------
    TXD0      19    OUT
    TXD1      22    OUT
    TX_EN     21    OUT
    RXD0      25    IN
    RXD1      26    IN
    CRS_DV    27    IN
   Receive Data Valid
    MDC       23    OUT: configurable
    MDIO      18    OUT: configurable
    CLK_MII   0     IN:  No other pins supported
                 OUT: alternate pins: 16, 17
   50MHz clock provided either by the PHY or the MAC. Default is PHY.
Note: Configuring clock options must be done via SDK (make sdk-menuconfig). ESP-IDF v4.4 will add the ability to override these in software.

The following connections are optional:

    PHY_RESET -     OUT (set via PhyConfig)

Public Functions

bool begin(const Config &config)

Configure and start the ethernet service.

Applications should expect to receive Start and Connected events following this call.

Parameters:

config – Configuration options

struct Config
namespace Ethernet

Typedefs

using EventDelegate = Delegate<void(Ethernet::Event event)>

Delegate type for Ethernet events.

Param event:

Which event occurred

using GotIpDelegate = Delegate<void(IpAddress ip, IpAddress netmask, IpAddress gateway)>

Delegate type for ‘got IP address’ event.

Enums

enum class Event

Ethernet event codes.

Values:

enumerator XX
enum class Speed

Link speed.

Values:

enumerator MBPS10
enumerator MBPS100

Variables

constexpr int8_t PIN_DEFAULT = {-2}

Use default pin for platform.

constexpr int8_t PIN_UNUSED = {-1}

Do not configure this pin.

Only applies if pin is optional, otherwise it will be interpreted as ‘auto detect’.

constexpr int8_t PHY_ADDR_AUTO = {-1}

Automatically detect PHY address during initialization.

class Dp83848 : public Ethernet::PhyFactory
#include <Dp83848.h>

DP 83848 PHY interface.

Public Functions

virtual PhyInstance *create(const PhyConfig &config) override

Called by the Service to construct a PHY instance.

virtual void destroy(PhyInstance *inst) override

Called by the Service to destroy a PHY instance.

class Ip101 : public Ethernet::PhyFactory
#include <Ip101.h>

IP101 PHY interface.

Public Functions

virtual PhyInstance *create(const PhyConfig &config) override

Called by the Service to construct a PHY instance.

virtual void destroy(PhyInstance *inst) override

Called by the Service to destroy a PHY instance.

class Ksz8041 : public Ethernet::PhyFactory
#include <Ksz8041.h>

KSZ 8041 PHY interface.

Public Functions

virtual PhyInstance *create(const PhyConfig &config) override

Called by the Service to construct a PHY instance.

virtual void destroy(PhyInstance *inst) override

Called by the Service to destroy a PHY instance.

class Lan8720 : public Ethernet::PhyFactory
#include <Lan8720.h>

LAN 8720 PHY interface.

Public Functions

virtual PhyInstance *create(const PhyConfig &config) override

Called by the Service to construct a PHY instance.

virtual void destroy(PhyInstance *inst) override

Called by the Service to destroy a PHY instance.

class Rtl8201 : public Ethernet::PhyFactory
#include <Rtl8201.h>

RTL 8201 PHY interface.

Public Functions

virtual PhyInstance *create(const PhyConfig &config) override

Called by the Service to construct a PHY instance.

virtual void destroy(PhyInstance *inst) override

Called by the Service to destroy a PHY instance.

struct PhyConfig
#include <Ethernet.h>

PHY configuration.

Public Members

int8_t phyAddr = PHY_ADDR_AUTO

PHY address.

int8_t resetPin = PIN_UNUSED

Reset GPIO number */.

uint16_t resetTimeout = 100

Reset timeout value in milliseconds.

uint16_t autoNegTimeout = 4000

Auto-negotiation timeout in milliseconds.

class PhyFactory
#include <Ethernet.h>

Virtual class used to construct a specific PHY instance.

Applications provide an instance of this factory class so that the Service can create and configure it at the correct point in initialisation or teardown.

Subclassed by Ethernet::DM9051PhyFactory, Ethernet::Dp83848, Ethernet::Ip101, Ethernet::Ksz8041, Ethernet::Lan8720, Ethernet::Rtl8201, Ethernet::W5500PhyFactory

Public Functions

virtual PhyInstance *create(const PhyConfig &config) = 0

Called by the Service to construct a PHY instance.

virtual void destroy(PhyInstance *inst) = 0

Called by the Service to destroy a PHY instance.

class Service
#include <Ethernet.h>

Abstract Service class.

Provides a common implementation for TCP/IP ethernet support.

An Ethernet interface requires a MAC layer plus PHY.

The ESP32, for example, contains a MAC but requires an external PHY. Other solutions, such as the W5500, contain MAC+PHY and require the correct PhyFactory to work.

Ethernet implementations should provide appropriate setup methods which are called by the application before invoking begin().

Subclassed by Ethernet::IdfService

Public Functions

virtual void end() = 0

Tear down the ethernet connection.

virtual MacAddress getMacAddress() const = 0

Get MAC address.

virtual bool setMacAddress(const MacAddress &addr) = 0

Set MAC address.

virtual bool setSpeed(Speed speed) = 0

Set speed of MAC.

virtual bool setFullDuplex(bool enable) = 0

Set duplex mode of MAC.

virtual bool setLinkState(bool up) = 0

Set link status of MAC.

virtual bool setPromiscuous(bool enable) = 0

Set MAC promiscuous mode.

virtual void setHostname(const String &hostname) = 0

Set DHCP hostname.

virtual String getHostname() const = 0

Get DHCP hostname.

virtual IpAddress getIP() const = 0

Get current IP address.

virtual bool setIP(IpAddress address, IpAddress netmask, IpAddress gateway) = 0

Set static IP address.

virtual bool isEnabledDHCP() const = 0

Determine if DHCP is active for this interface.

virtual bool enableDHCP(bool enable) = 0

Enable/disable DHCP on this interface.

inline void onEvent(EventDelegate callback)

Set callback for ethernet events.

inline void onGotIp(GotIpDelegate callback)

Set callback for ‘station connected with IP address’ event.

class DM9051PhyFactory : public Ethernet::PhyFactory
#include <DM9051.h>

Public Functions

virtual PhyInstance *create(const PhyConfig &config) override

Called by the Service to construct a PHY instance.

virtual void destroy(PhyInstance *inst) override

Called by the Service to destroy a PHY instance.

class DM9051Service : public Ethernet::SpiService
#include <DM9051.h>

Ethernet provider using W5500 SPI.

class SpiService : public Ethernet::IdfService
#include <SpiService.h>

SPI ethernet provider.

Subclassed by Ethernet::DM9051Service, Ethernet::W5500Service

struct Config
#include <SpiService.h>
class W5500PhyFactory : public Ethernet::PhyFactory
#include <W5500.h>

Public Functions

virtual PhyInstance *create(const PhyConfig &config) override

Called by the Service to construct a PHY instance.

virtual void destroy(PhyInstance *inst) override

Called by the Service to destroy a PHY instance.

class W5500Service : public Ethernet::SpiService
#include <W5500.h>

Ethernet provider using W5500 SPI.

class IdfService : public Ethernet::Service
#include <IdfService.h>

Base Ethernet service for IDF SDK.

Subclassed by EmbeddedEthernet, Ethernet::SpiService

Public Functions

virtual void end() override

Tear down the ethernet connection.

virtual MacAddress getMacAddress() const override

Get MAC address.

virtual bool setMacAddress(const MacAddress &addr) override

Set MAC address.

virtual bool setSpeed(Ethernet::Speed speed) override

Set speed of MAC.

virtual bool setFullDuplex(bool enable) override

Set duplex mode of MAC.

virtual bool setLinkState(bool up) override

Set link status of MAC.

virtual bool setPromiscuous(bool enable) override

Set MAC promiscuous mode.

virtual void setHostname(const String &hostname) override

Set DHCP hostname.

virtual String getHostname() const override

Get DHCP hostname.

virtual IpAddress getIP() const override

Get current IP address.

virtual bool setIP(IpAddress address, IpAddress netmask, IpAddress gateway) override

Set static IP address.

virtual bool isEnabledDHCP() const override

Determine if DHCP is active for this interface.

virtual bool enableDHCP(bool enable) override

Enable/disable DHCP on this interface.

WiFi Station
group wifi_sta

Control and monitoring of WiFi station interface.

Note

The WiFi station interface provides client access to a WiFi network. Control of WiFi connection including WiFi SSID and password and IP address, DHCP, etc.

Defines

WPS_STATUS_MAP(XX)
XX(name)
XX(name)

Typedefs

using ScanCompletedDelegate = Delegate<void(bool success, BssList &list)>

Scan complete handler function.

using SmartConfigDelegate = Delegate<bool(SmartConfigEvent event, const SmartConfigEventInfo &info)>

Smart configuration handler function.

Param event:

Param info:

Retval bool:

return true to perform default configuration

using WPSConfigDelegate = Delegate<bool(WpsStatus status)>

WPS configuration callback function.

Param status:

Retval bool:

return true to perform default configuration

Enums

enum StationConnectionStatus

WiFi station connection states.

Values:

enumerator eSCS_Idle

Connection idle.

enumerator eSCS_Connecting

Connecting.

enumerator eSCS_WrongPassword

Wrong password.

enumerator eSCS_AccessPointNotFound

AP not found.

enumerator eSCS_ConnectionFailed

Connection failed.

enumerator eSCS_GotIP

Got IP address.

enum SmartConfigType

Smart configuration type.

Values:

enumerator SCT_None
enumerator SCT_EspTouch

ESP Touch.

enumerator SCT_AirKiss

Air Kiss.

enumerator SCT_EspTouch_AirKiss

ESP Touch and Air Kiss.

enumerator SCT_EspTouch_V2

ESP Touch version 2.

enum SmartConfigEvent

Smart configuration event.

Values:

enumerator SCE_Wait

Wait.

enumerator SCE_FindChannel

Find channel.

enumerator SCE_GettingSsid

Getting SSID & password.

enumerator SCE_Link

Link established.

enumerator SCE_LinkOver

Link-over.

enum class WpsStatus

WiFi WPS callback status.

Values:

enumerator XX

Functions

String toString(WpsStatus status)

Variables

StationClass &WifiStation

Global instance of WiFi station object.

Note

Use WifiStation.function to access WiFi station functions

Note

Example:

if(WifiStation.config("My_WiFi", "My_Password"))
           WifiStation.enable(true);

struct SmartConfigEventInfo
#include <Station.h>

Smart Config callback information.

Public Members

SmartConfigType type = SCT_None

Type of configuration underway.

String ssid

AP SSID.

String password

AP Password.

bool bssidSet = false

true if connection should match both SSID and BSSID

MacAddress bssid

AP BSSID.

class StationClass
#include <Station.h>

WiFi station class.

Public Functions

virtual void enable(bool enabled, bool save = false) = 0

Enable / disable WiFi station.

Note

Disabling WiFi station will also disable and clear the handler set with waitConnection.

Parameters:
  • enabled – True to enable station. False to disable.

  • save – True to save operational mode to flash, False to set current operational mode only

virtual bool isEnabled() const = 0

Get WiFi station enable status.

Return values:

bool – True if WiFi station enabled

virtual bool config(const Config &config) = 0

Configure WiFi station.

Parameters:
  • ssid – WiFi SSID

  • password – WiFi password

  • autoConnectOnStartup – True to auto connect. False for manual. (Default: True)

  • save – True to save the SSID and password in Flash. False otherwise. (Default: True)

inline bool config(const String &ssid, const String &password, bool autoConnectOnStartup = true, bool save = true)

Configure WiFi station.

Parameters:
  • ssid – WiFi SSID

  • password – WiFi password

  • autoConnectOnStartup – True to auto connect. False for manual. (Default: True)

  • save – True to save the SSID and password in Flash. False otherwise. (Default: True)

virtual bool connect() = 0

Connect WiFi station to network.

virtual bool disconnect() = 0

Disconnect WiFi station from network.

bool isConnected() const

Get WiFi station connectoin status.

Return values:

bool – True if connected.

bool isConnectionFailed() const

Get WiFi station connection failure status.

Return values:

bool – True if connection failed

virtual StationConnectionStatus getConnectionStatus() const = 0

Get WiFi station connection status.

Return values:

StationConnectionStatus – Connection status structure

String getConnectionStatusName() const

Get WiFi station connection status name.

Return values:

StringString representing connection status

virtual bool isEnabledDHCP() const = 0

Get WiFi station DHCP enabled status.

Return values:

bool – True if DHCP enabled

virtual void enableDHCP(bool enable) = 0

Enable or disable WiFi station DHCP.

Parameters:

enable – True to enable WiFi station DHCP

virtual void setHostname(const String &hostname) = 0

Set WiFi station DHCP hostname.

Parameters:

hostname – - WiFi station DHCP hostname

virtual String getHostname() const = 0

Set WiFi station DHCP hostname.

Return values:

WiFi – station DHCP hostname

virtual IpAddress getIP() const = 0

Get WiFi station IP address.

Return values:

IpAddress – IP address of WiFi station

virtual MacAddress getMacAddress() const = 0

Get WiFi station MAC address.

Return values:

MacAddress

String getMAC(char sep = '\0') const

Get WiFi station MAC address.

Parameters:

sep – Optional separator between bytes (e.g. ‘:’)

Return values:

String – WiFi station MAC address

virtual bool setMacAddress(const MacAddress &addr) const = 0

Set WiFi station MAC address.

Must be called from init() before activating station. Espressif place certain limitations on MAC addresses:

Bit 0 of the first byte of the MAC address can not be 1. For example:

OK: “1a:XX:XX:XX:XX:XX” NOT OK: “15:XX:XX:XX:XX:XX”

Parameters:

addr – The new MAC address

Return values:

bool – true on success

virtual IpAddress getNetworkMask() const = 0

Get WiFi station network mask.

Return values:

IpAddress – WiFi station network mask

virtual IpAddress getNetworkGateway() const = 0

Get WiFi station default gateway.

Return values:

IpAddress – WiFi station default gateway

virtual IpAddress getNetworkBroadcast() const = 0

GetWiFi station broadcast address.

Return values:

IpAddress – WiFi station broadcast address

inline bool isLocal(IpAddress address)

Determine if the given address is on the same subnet.

Note

Use to prevent external access to services

Parameters:

address

Return values:

bool – true if address is local

bool setIP(IpAddress address)

Set WiFi station IP address.

Parameters:

address – IP address

Return values:

bool – True on success

virtual bool setIP(IpAddress address, IpAddress netmask, IpAddress gateway) = 0

Set WiFi station IP parameters.

Parameters:
  • address – IP address

  • netmask – Network mask

  • gateway – Default gateway

Return values:

bool – True on success

virtual String getSSID() const = 0

Get WiFi station SSID (Service Set Identifier)

Return values:

String – WiFi station SSID

virtual MacAddress getBSSID() const = 0

Get BSSID (Basic Service Set Identifier) for connected AP.

Return values:

MacAddress – Identifier of connected Access Point

virtual String getPassword() const = 0

Get WiFi station password.

Return values:

String – WiFi station password

virtual int8_t getRssi() const = 0

Get WiFi signal strength.

Return values:

int8_t – Value in dBm

virtual uint8_t getChannel() const = 0

Get active WiFi channel.

Return values:

uint8_t – channel number

virtual bool startScan(ScanCompletedDelegate scanCompleted) = 0

Start WiFi station network scan.

Parameters:

scanCompleted – Function to call when scan completes

Return values:

bool – True on success

struct Config
#include <Station.h>

Station configuration passed to config method.

Public Members

String ssid

Service Set to connect to (may be advertised by multiple access points)

String password

Password (if required)

MacAddress bssid

Set this to connect to a specific access point.

bool autoConnectOnStartup = true

Auto connect to this AP on system restart.

bool save = true

Store new settings in NV memory.

System
Task Queue

Sming has a task queue which allows execution of a function to be deferred until the system is less busy. This is done by calling SystemClass::queueCallback().

Callbacks are executed as soon as possible, and allow other higher priority tasks (such as servicing the WiFi stack) to be handled in a timely manner.

A common use for the queue is to initiate processing from an interrupt service routine.

You must not spend too much time in the callback. How much time depends on the nature of your application, but tasks consuming more than 100ms will probably affect responsiveness and should be broken into smaller chunks. You might do this by wrapping such tasks in a class together with some state information. At the end of the initial callback if there is further work to be done simply make another call to queueCallback().

The task queue size is fixed, so the call to queueCallback() will fail if there is no room.

TASK_QUEUE_LENGTH

Maximum number of entries in the task queue (default 10).

ENABLE_TASK_COUNT

If problems are suspected with task queuing, it may be getting flooded. For this reason you should check the return value from queueCallback().

You can enable this option to keep track of the number of active tasks, SystemClass::getTaskCount(), and the maximum, SystemClass::getMaxTaskCount().

By default this is disabled and both methods will return 255. This is because interrupts must be disabled to ensure an accurate count, which may not be desirable.

API Documentation
group system

Access to the ESP8266 system Provides system control and monitoring of the ESP8266.

Typedefs

using TaskCallback32 = void (*)(uint32_t param)

Task callback function type, uint32_t parameter.

Note

Callback code does not need to be in IRAM

using TaskCallback = void (*)(void *param)

Task callback function type, void* parameter.

Note

Callback code does not need to be in IRAM

using TaskDelegate = Delegate<void()>

Task Delegate callback type.

using SystemReadyDelegate = TaskDelegate

Handler function for system ready.

Enums

enum CpuFrequency

Common CPU frequencies.

Values:

enumerator eCF_80MHz
enumerator eCF_125MHz
enumerator eCF_133MHz
enumerator eCF_160MHz
enumerator eCF_240MHz
enum DeepSleepOptions

Deep sleep options.

Values:

enumerator eDSO_RF_CAL_BY_INIT_DATA

RF_CAL or not after deep-sleep wake up, depends on init data byte 108.

enumerator eDSO_RF_CAL_ALWAYS

RF_CAL after deep-sleep wake up, there will be large current.

enumerator eDSO_RF_CAL_NEVER

no RF_CAL after deep-sleep wake up, there will only be small current.

enumerator eDSO_DISABLE_RF

disable RF after deep-sleep wake up, just like modem sleep, there will be the smallest current.

enum SystemState

System state.

Values:

enumerator eSS_None

System state unknown.

enumerator eSS_Intializing

System initialising.

enumerator eSS_Ready

System ready.

Variables

SystemClass System

Global instance of system object.

Note

Use system.function to access system functions

Note

Example:

system.reset();

class ISystemReadyHandler
#include <System.h>

Interface class implemented by classes to support on-ready callback.

Subclassed by WDTClass, WifiSniffer

Public Functions

virtual void onSystemReady() = 0

Handle system ready events.

class SystemClass
#include <System.h>

System class.

Public Functions

inline bool isReady()

Check if system ready.

Return values:

bool – True if system initialisation is complete and system is now ready

void restart(unsigned deferMillis = 0)

Request a restart of the system.

Note

A delay is often required to allow network callback code to complete correctly. The restart is always deferred, either using the task queue (if deferMillis == 0) or using a timer. This method always returns immediately.

Parameters:

deferMillis – defer restart request by a number of milliseconds

inline bool setCpuFrequency(CpuFrequency freq)

Set the CPU frequency.

Parameters:

freq – Frequency to set CPU

Return values:

bool – true on success

inline CpuFrequency getCpuFrequency()

Get the CPU frequency.

Return values:

CpuFrequency – The frequency of the CPU

bool deepSleep(uint32_t timeMilliseconds, DeepSleepOptions options = eDSO_RF_CAL_BY_INIT_DATA)

Enter deep sleep mode. Deep sleep turns off processor and keeps only the RTC memory active.

Note

Determine reset cause like this:

auto info = system_get_rst_info();
if(info->reason == REASON_DEEP_SLEEP_AWAKE) {
    // ...
}

Note

ESP8266: Ensure GPIO 16 (XPD_DCDC) is connected to RST (EXT_RSTB). and call pinMode(16, WAKEUP_PULLUP) to enable wakeup from deep sleep.

Parameters:
  • timeMilliseconds – Quantity of milliseconds to remain in deep sleep mode

  • options – Deep sleep options

inline void onReady(SystemReadyDelegate readyHandler)

Set handler for system ready event.

Note

if system is ready, callback is executed immediately without deferral

Parameters:

readyHandler – Function to handle event

inline void onReady(ISystemReadyHandler *readyHandler)

Set handler for system ready event.

Note

if system is ready, callback is executed immediately without deferral

Parameters:

readyHandler – Function to handle event

Public Static Functions

static bool initialize()

System initialisation.

Note

Called by user_main: applications should not call this function or the task queue will be re-initialised and any currently queued tasks won’t be called.

Return values:

bool – true on success

static bool queueCallback(TaskCallback32 callback, uint32_t param = 0)

Queue a deferred callback.

Note

It is important to check the return value to avoid memory leaks and other issues, for example if memory is allocated and relies on the callback to free it again. Note also that this method is typically called from interrupt context so must avoid things like heap allocation, etc.

Parameters:
  • callback – The function to be called

  • param – Parameter passed to the callback (optional)

Return values:

bool – false if callback could not be queued

static inline bool queueCallback(TaskCallback callback, void *param = nullptr)

Queue a deferred callback, with optional void* parameter.

static inline bool queueCallback(InterruptCallback callback)

Queue a deferred callback with no callback parameter.

static bool queueCallback(TaskDelegate callback)

Queue a deferred Delegate callback.

Note

Provides flexibility and ease of use for using capturing lambdas, etc. but requires heap allocation and not as fast as a function callback. DO NOT use from interrupt context, use a Task/Interrupt callback.

Parameters:

callback – The Delegate to be called

Return values:

bool – false if callback could not be queued

static inline unsigned getTaskCount()

Get number of tasks currently on queue.

Return values:

unsigned

static inline unsigned getMaxTaskCount()

Get maximum number of tasks seen on queue at any one time.

Note

If return value is higher than maximum task queue TASK_QUEUE_LENGTH then the queue has overflowed at some point and tasks have been left un-executed.

Return values:

unsigned

WiFi
Build variables
ENABLE_WPS

Set to 1 to enable WiFi Protected Setup (WPS).

WPS is not enabled by default to preserve resources, and because there may be security implications which you should consider carefully.

ENABLE_SMART_CONFIG

Set to 1 to enable WiFi Smart Configuration API.

SmartConfig requires extra libraries and ENABLE_ESPCONN.

See Basic Smart Config sample application.

If you want to provide a default SSID and Password for connection to your default Access Point, you can do this:

make WIFI_SSID=MyAccessPoint WIFI_PWD=secret

These are provided as #defined symbols for your application to use. See Basic WiFi for a simple example, or MeteoControl for a more flexible solution using configuration files.

WIFI_SSID

SSID identifying default Access Point to connect to. By default, this is undefined.

WIFI_PWD

Password for the WIFI_SSID Access Point, if required. If the AP is open then leave this undefined.

API Documentation
WiFi Events
group wifi_ev

Event callback interface for WiFi events.

Defines

WIFI_DISCONNECT_REASON_CODES_MAP(XX)

Common set of reason codes to IEEE 802.11-2007.

Some acronymns used here - see the full standard for more precise definitions.

  • SSID: Service Set Identifier (the visible name given to an Access Point)

  • BSSID: Basic Service Set Identifier (a MAC address physically identifying the AP)

  • IE: Information Element (standard piece of information carried within WiFi packets)

  • STA: Station (any device which supports WiFi, including APs though the term commonly refers to a client)

  • AP: Access Point (device to which other stations may be associated)

  • RSN: Robust Security Network

  • AUTH: Authentication (how a station proves its identity to another)

Note

Codes at 200+ are non-standard, defined by Espressif.

Typedefs

using StationConnectDelegate = Delegate<void(const String &ssid, MacAddress bssid, uint8_t channel)>

Delegate type for ‘station connected’ event.

Note

This event occurs when the station successfully connects to the target AP. Upon receiving this event, the DHCP client begins the process of getting an IP address.

Param ssid:

Param bssid:

Param channel:

using StationDisconnectDelegate = Delegate<void(const String &ssid, MacAddress bssid, WifiDisconnectReason reason)>

Delegate type for ‘station disconnected’ event.

Note

This event can be generated in the following scenarios:

  • When the station is already connected to the AP, and a manual disconnect or re-configuration is taking place. e.g. WifiStation.disconnect()

  • When WifiStation.connect() is called, but the Wi-Fi driver fails to set up a connection with the AP due to certain reasons, e.g. the scan fails to find the target AP, authentication times out, etc. If there are more than one AP with the same SSID, the disconnected event is raised after the station fails to connect all of the found APs.

  • When the Wi-Fi connection is disrupted because of specific reasons, e.g., the station continuously loses N beacons, the AP kicks off the station, the AP’s authentication mode is changed, etc.

Param ssid:

SSID from which we’ve disconnected

Param bssid:

Param reason:

Why the connection was dropped

using StationAuthModeChangeDelegate = Delegate<void(WifiAuthMode oldMode, WifiAuthMode newMode)>

Delegate type for ‘station authorisation mode changed’ event.

Note

This event arises when the AP to which the station is connected changes its authentication mode, e.g., from ‘no auth’ to WPA. Generally, the application event callback does not need to handle this.

Param oldMode:

Param newMode:

using StationGotIPDelegate = Delegate<void(IpAddress ip, IpAddress netmask, IpAddress gateway)>

Delegate type for ‘station got IP address’ event.

Note

This event arises when the DHCP client successfully gets the IPV4 address from the DHCP server, or when the IPV4 address is changed. The IPV4 may be changed because of the following reasons:

  • The DHCP client fails to renew/rebind the IPV4 address, and the station’s IPV4 is reset to 0.

  • The DHCP client rebinds to a different address.

  • The static-configured IPV4 address is changed.

Param ip:

Param netmask:

Param gateway:

using AccessPointConnectDelegate = Delegate<void(MacAddress mac, uint16_t aid)>

Delegate type for ‘Access Point Connect’ event.

Note

This event occurs every time a station is connected to our Access Point.

Param mac:

MAC address of the station

Param aid:

Association ID representing the connected station

using AccessPointDisconnectDelegate = Delegate<void(MacAddress mac, uint16_t aid)>

Delegate type for ‘Access Point Disconnect’ event.

Note

This event occurs every time a station is disconnected from our Access Point.

Param mac:

MAC address of the station

Param aid:

Association ID assigned to the station

using AccessPointProbeReqRecvedDelegate = Delegate<void(int rssi, MacAddress mac)>

Delegate type for ‘Access Point Probe Request Received’ event.

Note

Probe Requests are a low-level management frame which are used to determine information about our Access Point, such as which authentication modes are supported.

Param rssi:

Signal strength

Param mac:

Enums

enum WifiDisconnectReason

Reason codes for WiFi station disconnection.

Values:

Variables

WifiEventsClass &WifiEvents

Global reference to architecture-specific implementation.

class WifiEventsClass
#include <WifiEvents.h>

WiFi events class.

Public Functions

inline void onStationConnect(StationConnectDelegate delegateFunction)

Set callback for ‘station connected’ event.

inline void onStationDisconnect(StationDisconnectDelegate delegateFunction)

Set callback for ‘station disconnected’ event.

inline void onStationAuthModeChange(StationAuthModeChangeDelegate delegateFunction)

Set callback for ‘station authorisation mode change’ event.

inline void onStationGotIP(StationGotIPDelegate delegateFunction)

Set callback for ‘station connected with IP address’ event.

inline void onAccessPointConnect(AccessPointConnectDelegate delegateFunction)

Set callback for ‘access point client connected’ event.

inline void onAccessPointDisconnect(AccessPointDisconnectDelegate delegateFunction)

Set callback for ‘access point client disconnected’ event.

inline void onAccessPointProbeReqRecved(AccessPointProbeReqRecvedDelegate delegateFunction)

Set callback for ‘access point probe request received’ event.

Public Static Functions

static String getDisconnectReasonName(WifiDisconnectReason reason)

Get short name for disconnection reason.

static String getDisconnectReasonDesc(WifiDisconnectReason reason)

Get descriptive explanation for disconnect reason.

WiFi Sniffer

See WiFi Sniffer for an example of how to use this.

API Documentation
group wifi_sniffer

WiFi promiscuous mode sniffer support.

Defines

ETH_MAC_LEN

Typedefs

using BeaconInfoList = BeaconOrClientListTemplate<BeaconInfo>

For applications to use to manage list of unique beacons.

using ClientInfoList = BeaconOrClientListTemplate<ClientInfo>

For applications to use to manage list of unique clients.

using WifiSnifferCallback = Delegate<void(uint8_t *data, uint16_t length)>
using WifiBeaconCallback = Delegate<void(const BeaconInfo &beacon)>
using WifiClientCallback = Delegate<void(const ClientInfo &client)>
struct BeaconInfo
#include <WifiSniffer.h>

Decoded Wifi beacon (Access Point) information.

struct ClientInfo
#include <WifiSniffer.h>

Decoded Wifi client information.

template<class T>
class BeaconOrClientListTemplate : public Vector<T>
#include <WifiSniffer.h>
class WifiSniffer : public ISystemReadyHandler
#include <WifiSniffer.h>

Public Functions

void begin()

Initialise the sniffer.

void end()

Stop the sniffer.

inline void onBeacon(WifiBeaconCallback callback)

Register notification for beacon (AP) info.

inline void onClient(WifiClientCallback callback)

Register notification for client info.

inline void onSniff(WifiSnifferCallback callback)

Register notification for all incoming data.

Note

Callback invoked for all packet types, including beacon/client

inline void setChannel(unsigned channel)

Set the channel to listen on.

Parameters:

channel

inline unsigned getChannel()

Get the current channel being listened on.

Services

Profiling
CPU Usage

Class to provide a CPU usage indication based on task callback availability. To produce useful results it requires a couple of seconds at startup to calibrate.

Use like this:

#include <Services/Profiling/CpuUsage.h>

// instantiate a single instance of :cpp:class:`Profiling::CpuUsage`
Profiling::CpuUsage cpuUsage;

// Will be called when CpuUsage calibration has completed
void onReady()
{
   // Continue with application initialisation
}

void init()
{
   // Begin clock calibration
   cpuUsage.begin(onReady);
}

See Basic Tasks for a more detailed example.

CPU usage is calculated over an update period which begins with a call to Profiling::CpuUsage::reset(). The actual update period must be managed elsewhere, using a callback timer, web request or other mechanism. It doesn’t need to be exact as the actual elapsed time in CPU cycles is used for the calculation.

After the update period has elapsed, call Profiling::CpuUsage::getUtilisation() to obtain a CPU usage figure.

This figure is obtained using the number of task callbacks made within the update period.

loop cycles

Set up repeating task callback and measure invocations between successive calls

total cycles

The total number of CPU cycles between calls to Profiling::CpuUsage::update().

used

total - loop

utilisation

used / total

class CpuUsage

Class to provide a CPU usage indication based on task callback availability.

Public Functions

inline void begin(InterruptCallback ready)

Calibrate the baseline figure for minimum CPU usage.

Note

Typically call this in init()

Parameters:

ready – Function to call when calibration is complete

inline void reset()

Reset counters to start a new update period.

inline unsigned getLoopIterations()

Get the number of task callbacks made so far.

inline uint32_t getElapsedCycles()

Get the total number of CPU cycles since the last call to reset()

inline uint32_t getMinLoopCycles()

Get the figure used as the baseline cycle count.

inline unsigned getUtilisation()

Get the CPU utilisation figure in 1/100ths of a percent.

Min-Max
template<typename T>
class MinMax

Class to track minimum and maximum values of a set of data, with average, total and count.

Subclassed by Profiling::MinMaxTimes< Timer >

Min-Max Times
template<class Timer>
class MinMaxTimes : public Profiling::MinMax<uint32_t>, public Timer
TaskStat

Example of use:

#include <Services/Profiling/TaskStat.h>

Profiling::TaskStat taskStat(Serial);
Timer statTimer;

void init()
{
   ...

   // Set up timer to print task information to Serial terminal every 2 seconds
   statTimer.initializeMs<2000>(InterruptCallback([]() { taskStat.update(); }));
   statTimer.start();
}
class TaskStat

Helper class to support printing of real-time task information.

Code is refactored from the FreeRTOS Real Time Stats Example.

Requires these SDK configuration settings to be set:

  • FREERTOS_USE_TRACE_FACILITY

  • FREERTOS_GENERATE_RUN_TIME_STATS

  • FREERTOS_VTASKLIST_INCLUDE_COREID (optional)

Public Functions

TaskStat(Print &out)

Constructor.

Parameters:

out – Where to print reports (e.g. Serial)

bool update()

Update the report.

Nothing will be output the first time this is called. From then on, the stats will show the difference in task usage from the previous call.

Wiring

The Wiring Framework is common to all Arduino platforms.

Some adaptations and improvements have been made for Sming which are documented here.

Vector
template<typename Element>
class Vector : public Countable<Element>

Vector class template.

Public Functions

bool setSize(unsigned int newSize)

Reduce or increase number of items.

If increasing number of items, new items will be set to current nil value. If reducing number of items, old items will be deleted.

Return values:

true – on success, false on memory reallocation failure

inline void trimToSize()

Reduce capacity to match current size.

template<bool is_const>
class Iterator
HashMap
template<typename K, typename V>
class HashMap

HashMap class template.

Public Types

using Comparator = bool (*)(const K&, const K&)

Compare two keys for equality.

using SortCompare = bool (*)(const ElementConst &e1, const ElementConst &e2)

Return true if key1 < key2.

Public Functions

void sort(SortCompare compare)

Sort map entries.

template<bool is_const>
struct BaseElement
template<bool is_const>
class Iterator
Wiring String
Small String Optimisation

The String class is probably the most used class in Arduino and Sming.

Unfortunately it gets the blame for one of the most indidious problems in the embedded world, heap fragmentation.

To alleviate this problem, Sming uses a technique known as Small String Optimisation, which uses the available space inside the String object itself to avoid using the heap for small allocations of 10 characters or fewer.

This was lifted from the Arduino Esp8266 core. Superb work - thank you!

Configuration Variables
STRING_OBJECT_SIZE

minimum: 12 bytes (default) maximum: 128 bytes

Must be an integer multiple of 4 bytes.

This is an experimental feature which lets you increase the size of a String object to reduce heap allocations further. The effect of this will vary depending on your application, but you can see some example figures in Pull Request #1951.

Benefits of increasing STRING_OBJECT_SIZE:

  • Increase code speed

  • Fewer heap allocations

Drawbacks:

  • Increased static memory usage for global/static String objects or embedded within global/static class instances.

  • A String can use SSO or the heap, but not both together, so in heap mode any additional SSO space will remain unused.

Allows the size of a String object to be changed to increase the string length available before the heap is used.

Note

The current implementation uses one byte for a NUL terminator, and another to store the length, so the maximum SSO string length is (STRING_OBJECT_SIZE - 2) characters.

However, the implementation may change so if you need to check the maximum SSO string size in your code, please use String::SSO_CAPACITY.

API Documentation
class String

The String class.

Note that a string object’s default constructor creates an empty string. This is not the same as a null string. A null string evaluates to false, but an empty string evaluates to true.

Small String Optimisation means that heap is only used for strings longer than 10 characters, not including the NUL terminator. This is simply making use of existing storage within the String object.

This length can be increased using STRING_OBJECT_SIZE, but note the additional space remains unused when switching to heap storage for longer Strings.

Subclassed by CStringArray, StringSumHelper

Copy constructors

If the initial value is null or invalid, or if memory allocation fails, the string will be marked as invalid (i.e. “if (s)” will be false).

Copy operators

If the value is null or invalid, or if the memory allocation fails, the String will be marked as invalid (“if (s)” will be false).

Move operators

Move content from one String to another without any heap allocation.

Move operators are automatically selected by the compiler when it is able, such as when returning temporary String objects from functions.

In other situations, use std::move:

String original("A String");
String copy("This is the content for the copy");
copy = std::move(myString);
copy will now contain “A String”, whilst original will be invalidated.

Concatenation methods

Works with built-in types. On failure, the string is left unchanged. If the argument is null or invalid, the concatenation is considered unsuccessful.

retval bool:

true on success, false on failure

Concatenation operators

If there’s not enough memory for the concatenated value, the string will be left unchanged (but this isn’t signalled in any way)

Comparison methods

Works with String

and ‘c’ string

Comparisons are case-sensitive, binary comparison null strings (including cstr == nullptr) are treated as empty.

retval int:

Returns < 0 if String is lexically before the argument, > 0 if after or 0 if the same

Test for equality

Compares content byte-for-byte using binary comparison

null strings (including cstr == nullptr) are treated as empty.

retval bool:

Returns true if strings are identical

Equality operator ==

retval bool:

true if Strings are identical

In-equality operator !=

retval bool:

Returns true if strings are not identical

Test for equality, without case-sensitivity

null strings are treated as empty.

retval bool:

true if strings are considered the same

Array operators

If index is invalid, returns NUL \0

int indexOf(…)

Locate a character or String within another String

.

By default, searches from the beginning of the

String, but can also start from a given index, allowing for the locating of all instances of the character or String.
retval int:

Index if found, -1 if not found

int lastIndexOf(…)

Locate a character or String within another String By default, searches from the end of the String, but can also work backwards from a given index, allowing for the locating of all instances of the character or String.

retval int:

Index if found, -1 if not found

String substring(…)

Get a substring of a String

.

The starting index is inclusive (the corresponding character is included in the substring), but the optional ending index is exclusive (the corresponding character is not included in the substring).

param from:

Index of first character to retrieve

param to:

(optional) One-past the ending character to retrieve

If the ending index is omitted, the substring continues to the end of the String.

If you don’t need the original String, consider using remove() instead:

    String original("This is the original string.");
    String sub = original.substring(0, 13);
This produces the same result:
    original.remove(13);

replace(…)

Replace all instances of a given character or substring with another character or substring.

Replacing a single character always succeeds as this is handled in-place.

retval bool:

true on success, false on allocation failure

Where replace is longer than find the String may need to be re-allocated, which could fail. If this happens the method returns false and the String is left unchanged.

remove()

Remove characters from a String

.

If no count is provided then all characters from the given index to the end of the

String are removed.

Note

The String is modified in-situ without any reallocation

param index:

Index of the first character to remove

param count:

Number of characters to remove

Pad string to a minimum length

This is used, for example, when outputting tabular data. The string is modified in-situ to minimise memory reallocations.

Methods may be chained like this::

    Serial << String(value).padLeft(10, '.') << endl;

inline String &padLeft(uint16_t minWidth, char c = ' ')

Insert padding at start of string if length is less than given width.

inline String &padRight(uint16_t minWidth, char c = ' ')

Insert padding at end of string if length is less than given width.

String &pad(int16_t minWidth, char c = ' ')

Pad string if length is less than given width.

Parameters:

width – Left-padded if < 0, right-padded if > 0.

Public Functions

inline String()

Default constructor.

Note

Creates a null String which evaluates to false.

bool reserve(size_t size)

Pre-allocate String memory.

On failure, the String is left unchanged. reserve(0), if successful, will validate an invalid string (i.e., “if (s)” will be true afterwards)

Parameters:

size

Return values:

bool – true on success, false on failure

bool setLength(size_t length)

set the string length accordingly, expanding if necessary

Note

extra characters are undefined

Parameters:

length – required for string (nul terminator additional)

Return values:

true – on success, false on failure

inline size_t length(void) const

Obtain the String length in characters, excluding NUL terminator.

bool setBuffer(const Buffer &buffer)

Set String content using move semantics from external memory buffer.

Note

length MUST be < size - A NUL character is written at this location

Parameters:

buffer – We’ll take ownership of this buffer

Return values:

bool – true on success; on failure, ownership of buffer is not transferred

Buffer getBuffer()

Get String content using move semantics.

Note

String is invalidated by this call. Caller is responsible for buffer memory.

Return values:

Buffer

inline operator StringIfHelperType() const

Provides safe bool() operator.

Evaluates as false if String is null, otherwise evaluates as true

inline bool startsWith(const String &prefix) const

Compare the start of a String Comparison is case-sensitive, must match exactly.

Parameters:

prefix

Return values:

bool – true on match

bool startsWith(const String &prefix, size_t offset) const

Compare a string portion.

mis-named as does not necessarily compare from start

Note

Comparison is case-sensitive, must match exactly

Parameters:
  • prefix

  • offset – Index to start comparison at

Return values:

bool – true on match

bool endsWith(char suffix) const

Compare the end of a String.

Parameters:

suffix

Return values:

bool – true on match

bool endsWith(const String &suffix) const

Compare the end of a String.

Parameters:

suffix

Return values:

bool – true on match

inline char charAt(size_t index) const

Obtain the character at the given index.

Note

If index is invalid, returns NUL \0

Parameters:

index

Return values:

char

void setCharAt(size_t index, char c)

Sets the character at a given index.

Note

If index is invalid, does nothing

Parameters:
  • index

  • c

size_t getBytes(unsigned char *buf, size_t bufsize, size_t index = 0) const

Read contents of a String into a buffer.

Note

Returned data always nul terminated so buffer size needs to take this into account

Parameters:
  • buf – buffer to write data

  • bufsize – size of buffer in bytes

  • index – offset to start

Return values:

unsigned – number of bytes copied, excluding nul terminator

inline void toCharArray(char *buf, size_t bufsize, size_t index = 0) const

Read contents of String into a buffer.

See also

See getBytes()

inline const char *c_str() const

Get a constant (un-modifiable) pointer to String content.

Return values:

const – char* Always valid, even for a null string

inline char *begin()

Get a modifiable pointer to String content.

Note

If String is NUL, returns nullptr.

inline char *end()

Get a modifiable pointer to one-past the end of the String.

Note

Points to the terminating NUL character. If String is NUL, returns nullptr.

void toLowerCase(void)

Convert the entire String content to lower case.

void toUpperCase(void)

Convert the entire String content to upper case.

void trim(const char *set = " \t\n\v\f\r")

Remove all leading and trailing characters from the String.

Parameters:

Set – of characters to remove, defaults to whitespace set

Public Static Attributes

static const String nullstr

A null string evaluates to false.

static const String empty

An empty string evaluates to true.

static constexpr size_t SSO_CAPACITY = STRING_OBJECT_SIZE - 2

Max chars. (excluding NUL terminator) we can store in SSO mode.

struct Buffer

Used with setBuffer and getBuffer methods.

Public Members

char *data

Allocated using malloc.

size_t size

Size of memory allocation.

size_t length

Length of content, MUST be < size.

using FlashString = FSTR::String

Read-only String class stored in flash memory.

Sample Projects

Sming comes with a set of samples to illustrate and test various aspects of the framework. Some are related to specific Libraries.

MPU6050 Six-Axis (Gyro + Accelerometer)

MPU6050 sensor reader.

_images/mpu6050.jpg
References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

MMA7455 Accelerometer

Simple demonstration which reads and displays output from an I2C accelerometer.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic APA102

Demonstrates use of APA102 library for controlling smart LEDs via SPI.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic AWS

How to set up Amazon Web Services to use as an IOT gateway.

openssl rsa -in APP.private.key -out APP.private.key.der -outform DER openssl x509 -in APP.cert.pem -out APP.cert.der -outform DER

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Audio

Demonstrates how to set up I2S driver to produce audio output.

References
SoC support
  • esp8266

Basic Capsense

Simple demonstration showing raw output from a capacitive sensor.

References
SoC support
  • esp8266

Basic DateTime

A basic demonstration of Sming’s date and time handling functions.

Connect a serial terminal and enter a unix timestamp as a numeric value, then press ENTER to display it in various formats.

You can delete characters, or press ESC to clear the entry.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Delegates

Demonstrates the various ways you can use callbacks within Sming.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Ethernet

Demonstrates creating an ethernet connection.

Currently only supported for ESP32 using C++ wrappers around the ESP-IDF implementation.

See https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32/api-reference/network/esp_eth.html.

Embedded MAC

The standard ESP32 contains an embedded ethernet MAC but requires an external PHY. This demonstration uses a commonly available LAN8270 module.

See Components/Network/Arch/Esp32/include/Platform/EmbeddedEthernet.h for connections.

SPI ethernet

SPI connections depend on the device variant being used. See Sming/Libraries/HardwareSPI/src/Arch/Esp32/Controller.cpp for details.

See Sming/Components/Network/Arch/Esp32/Network/W5500.cpp for additional pin connections for W5500-based devices.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

Basic Hardware PWM

Demonstrates how to generate PWM signals on multiple pins using Sming’s HardwarePWM class.

References
SoC support
  • esp8266

Basic IFS

Simple Webserver demonstration using IFS.

View the filesystem using a web browser.

To see directory content in a different format, append ?format=XX, one of:

  • json

  • text

  • html

Use the format archive to retrieve an archive/backup of the directory tree as an FWFS image.

Building

By default, data is stored in a read-only FWFS (Firmware Filesystem) partition.

This sample also demonstrates how to store the data in a FlashString object:

make config-clean
make ENABLE_FLASHSTRING_IMAGE=1

Because the data is linked into the program image this is only suitable for small filesystem images. This could be used to store default recovery data, especially with OTA updates because each program image is self-contained.

To add support for SD Cards to this sample:

make ENABLE_SDCARD=1

See FatIFS for further details of SD Card and FAT filing system support.

To add support for a USB storage device:

make ENABLE_USB_STORAGE=1
References
Environment Variables
  • ENABLE_FLASHSTRING_IMAGE

  • ENABLE_SDCARD

  • ENABLE_USB_STORAGE

SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Interrupts

Simple example of how interrupts can be used within Sming.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic NFC

Sample application to read an RFID tag using a suitable reader connected via SPI interface.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Neopixel

Demonstration of the Adafruit Neopixel library for controlling single-wire-based LED pixels and strip.

References
SoC support
  • esp32

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • rp2040

Basic Ota

Introduction

This sample integrates Over-The-Air(OTA) Upgrader, Over-The-Air(OTA) Network Upgrader and Sming. It demonstrates dual rom booting, big flash support, OTA updates and dual spiffs filesystems. This sample should work on all supported architectures.

Esp8266

On Esp8266 we use rBoot as bootloader. When using rBoot big flash support with multiple 1MB slots only one rom image needs to be created. If you don’t want to use big flash support (e.g. for a device with smaller flash) see the separate instructions below. You can easily take the ota files and add them to your own project to add OTA support.

Building
  1. Set WIFI_SSID & WIFI_PWD environment variables with your wifi details.

  2. Edit the OTA server details defined in the application component.mk file.

  3. make && make flash

  4. Put rom0.bin and spiff_rom.bin in the root of your webserver for OTA.

  5. Interact with the sample using a terminal (make terminal). Sorry - no web-gui (yet).

Testing

For testing purposes we provide an Ota server that can be started on your desktop machine:

make otaserver

The server listens on port 9999 and all network interfaces. If your desktop has the following IP address 192.168.1.30 after connecting to your WIFI router then you can compile the sample to use this IP address and the testing OTA server:

make ROM_0_URL=http://192.168.1.30:9999/rom0.bin SPIFFS_URL=http://192.168.1.30:9999/spiff_rom.bin
make flash

Make sure to replace 192.168.1.30 with your WIFI IP address.

Flash layout considerations
Esp8266

If you want to use, for example, two 512k roms in the first 1MB block of flash (old style) then Sming will automatically create two separately linked roms. If you are flashing a single rom to multiple 1MB flash blocks, all using the same offset inside their 1MB blocks, only a single rom is created. See rBoot for further details.

  • If using a very small flash (e.g. 512k) there may be no room for a spiffs filesystem, so use HWCONFIG = standard

  • After building copy all the rom*.bin files to the root of your web server.

If you want more than two roms you must be an advanced user and should be able to work out what to copy and edit to achieve this!

Configuration
ROM_0_URL

Default: http://192.168.7.5:80/rom0.bin

The URL where the firmware for the first application partition can be downloaded.

ROM_1_URL

Default: http://192.168.7.5:80/rom1.bin

Used when RBOOT_TWO_ROMS is set. The URL where the firmware for the second application partition can be downloaded.

SPIFFS_URL

Default: http://192.168.7.5:80/spiff_rom.bin

The URL where the spiffs partition attached can be downloaded.

Credits

The initial sample was made possible with the assistance of piperpilot, gschmott and robotiko on the esp8266.com forum.

References
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

Basic ProgMem

This sample application demonstrates the various ways to access data stored in Flash memory.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic I2C Scanner

Classic Arduino I2C scanner with adaptations for Sming framework.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Serial

Demonstrates Sming’s asynchronous capabilities using the UART to send and receive simultaneously using two serial ports.

Use the primary serial port to enter commands:

cat

Send the contents of the Readme.md file to the second serial port.

text

Echo a block of text

Note that you can continue to type commands while serial data is being transmitted as all operations are fully asynchronous. This becomes more obvious if you try using a low baud rate, for example:

make COM_SPEED_SERIAL=9600 COM_SPEED_ESPTOOL=921600

We’d still like a decent speed for flashing though!

If you want to set the two ports to different speeds the code will need to be modified.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Servo

Demonstrates use of the Servo library for controlling multiple servo actuators.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Smart Config

Introduction

SmartConfig is a mechanism to more easily configure an ESP device using a smart phone.

Calling smartConfigStart() starts a search for an Access Point (AP) with a special signature. It then tries to extract data like SSID and password from it. The App on your smart phone sends out that information.

The example here shows how to use ESP_TOUCH method to do smart configuration on the device. It is ported from the C code that Espressif provides in their SDK examples.

You will need an App for your Smart Phone, such as:

See also https://www.espressif.com/en/products/software/esp-touch/overview.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

Basic SSL

Compilation

In Sming the SSL support is not enabled by default.

In order to enable it you should compile your project with the ENABLE_SSL =1 directive. This can be done using the following command:

make ENABLE_SSL=1

Now you can flash your application to your ESP8266 device.

Debug Information

If you want to see more debug information during compile type you should add the directive SSL_DEBUG =1, like this:

make ENABLE_SSL=1 SSL_DEBUG=1
Slow SSL negotiation

The initial SSL negotiation is CPU intensive. By default SmingFramework switches the CPU frequency from 80 to 160 MHz. After the negotiation the CPU is switched back to 80 MHz.

If your device is running on battery this can drain the battery much faster. If you do not want the switch from 80 to 160 MHz to happen then make sure to recompile SmingFramework with SSL_SLOW_CONNECT directive:

make USER_CFLAGS="SSL_SLOW_CONNECT=1"
Memory usage

SSL uses a significant amount of RAM. You can build this sample to track heap usage and output statistics every 5 seconds:

make ENABLE_MALLOC_COUNT=1
References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Storage

This sample application demonstrates various ways to manage and access flash memory using the Storage Management system.

It also shows how to create and partition custom storage devices.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Tasks

This sample demonstrates using the Task class to efficiently perform intensive processing.

Let’s suppose we need to continuously sample an analogue input as fast as possible, do some processing and output the results.

This sample attempts to do some (very rough) fourier analysis on sampled ADC data, displaying the average amplitudes in a set of frequency ‘buckets’ (ranges).

The timing jitter using this approach is quite bad, so it attempts to correct but generally this form of processing is best done with more capable hardware (e.g. ESP32, FPGA).

In this sample application, we just write the results to the debug terminal, but a real application might stream this to waiting network clients.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Templates

Simple demonstration of how to use Template streams.

The program uses two sets of data:

Templates are used to display filtered data from these in text, JSON and XML formats.

Although the data is just printed to the serial terminal, it can just as easily be sent over a network link as in the Basic IFS sample.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Utility

Demonstrates how to pass parameters to Host applications.

This allows creation of Host-only utility applications.

After building, try this:

make run HOST_PARAMETERS='command=testWebConstants'

To run the application directly, you can do this:

out/Host/debug/firmware/app --nonet command=testWebConstants

--nonet is optional.

You may find --debug=0 useful to suppress all but host error messages. Specify 1 to include warnings.

Use --help to see available emulator options.

See HOST_PARAMETERS for further details.

References
SoC support
  • host

Basic Web Skeleton

Introduction

Basic application that can be used as a start point for some useful App.

Features:

  • can setup wifi ssid and wifi password for STA (wifi client) mode either from own AP or as connected to some wifi network

  • demonstrate new way of catching wif-events with WifiEvents class

  • if preconfigured wifi network is unreachable start AP named TyTherm with hardcoded password (see source)

  • can enable/disable STA (wifi client) mode

  • own AP autodisable after successful connection to preconfigured wifi network

  • form population and sending is done with json+ajax

  • demonstrate usage of getting raw http request body to be processed as json

  • demonstrate how to fill html template on client side with more flexible than Smings Templating - JavaScript

App called TyTherm because it is base for TinY TermOmeter :)

FlashString

This sample also demonstrates how to use FlashString maps as an alternative to using SPIFFS for serving files.

To test this out, build the application without a filesystem image:

make HWCONFIG=standard ENABLE_FLASHSTRING_MAP=1

See webserver.cpp for the details.

References
Environment Variables
  • ENABLE_FLASHSTRING_MAP

SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Web Skeleton (LTS)

Introduction

This is a copy of the Basic_WebSkeletonApp from the Sming LTS branch, modified to build for Sming 4.0. Sming uses ArduinoJson version 6 by default, so this sample demonstrates how to continue using version 5.

This is how it was done:

  1. When including SmingCore.h, just use #include <SmingCore.h>.

  2. Remove the file include/user_config.h: Sming does not require it. If you have made changes for your project then move the changes into your project’s component.mk file.

  3. Remove any #include <user_config.h> statements from your files.

  4. Rename Makefile-user.mk file to component.mk.

  5. Replace the file Makefile with the one from the Basic_Blink sample project. If you’ve ignored the instructions and modified the file (!) then you’ll need to move those changes into your new component.mk file instead.

  6. Sming uses #pragma once style for header guards, so consider updating your own files if you’re not already doing this.

  7. We use the OneWire Arduino Library, so add ARDUINO_LIBRARIES := OneWire to our component.mk file. Sming now only builds libraries which are specifically requested for a project.

  8. We’re using ArduinoJson, version 6. Sming supports version 5 or 6, but you must specify which one:

    • Add ARDUINO_LIBRARIES=ArduinoJson6 to the project’s component.mk file.

    • Add #include <JsonObjectStream.h>. If you’re not using the stream class, add #include <ArduinoJson6.h> to code.

  9. Update callback function parameter lists for STADisconnect and STAGotIP. We can also get a description for disconnection reasons, so display that instead of just a number. See Sming/Platform/WifiEvents.h.

  10. In webserver.cpp, the onConfiguration() function uses request.getBody(). It is more efficient to use streams, so just replace with request.getBodyStream().

That’s it.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

Basic WiFi

Demonstrates WiFi network connection and scanning functionality.

Enables an unsecured Software Access Point called Sming InternetOfThings which you can connect to from your smart phone (or other WiFi device).

Prints details of any probe requests, so you can see who’s scanning your device.

Scans list of available Access Points and displays them.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

CANBUS

Demonstrates communication with MCP2515-compatible devices via SPI interface.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

HMC5883L Compass

HMC5883L sensor reader.

_images/hmc5883l.jpg
References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

DFPlayer Mini

Introduction

The DFPlayer Mini MP3 Player is a small and low-cost MP3 module with a simplified output directly to the speaker. The module can be used as a stand alone module with attached battery, speaker and push buttons or used in combination with an ESP8266/ESP32 via RX/TX.

For more details see this link: https://www.dfrobot.com/wiki/index.php/DFPlayer_Mini_SKU:DFR0299

Building

Steps to test this sample: * Connect DFPlayer Mini using RX/TX pins to TX/RX pins respectively of ESP8266. * Copy one or more mp3 files to your SD card. The example playes one file for some time and moves to the next one. * Insert your SD card in the slot of the DF Player module. * Flash and run the sample code.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

DS3232RTC NTP Setter

Connecting a real-time clock chip allows time to be maintained when OFF or in deep sleep modes without requiring an active network connection.

This example demonstrates how to do this using the popular DS3232 I2C device, and also how to set it up using a network time server.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

TM1637 Display

Demonstrates use of 7-segment LED displays using TM1637 controller. This only requires 2 GPIO connections.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Distance measurement with Vl530xl sensor

This sample demonstrates how to use an inexpensive laser sensor VL53L0X to measure distance via I2C interface.

In addition to the original library a “long range” mode is added to measure distances up to 2 meter.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

DNS Captive Portal

Demonstrates how to use Sming to create a default landing page when configured as an Access Point.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Echo SSL

This example talks to an SSL-enabled TCP server. If you do not have such you can use ncat (from the nmap package) and run something like this:

ncat -vvv  -l 0.0.0.0 4444 --ssl --ssl-key ~/Private/x.key --ssl-cert ~/Private/x.cert

See Basic SSL for information on compiling and configuring SSL.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

FTP Server Files

This example sets up an FTP server with a couple of files stored in SPIFFS. It mounts this on top of an FWFS volume (a hybrid filesystem).

The sample creates three users with different roles (guest, user and administrator).

User

Password

Role

guest

(none)

Guest

me

“123”

User

admin

“1234”

Admin

You’ll need to have WiFi configured. You can set this information when building like this:

make WIFI_SSID=ssid WIFI_PWD=password

substituting your actual Access Point details for ssid and password.

Flash to your device:

make flash

You should be able to connect using an FTP client:

ftp ipaddress

and when prompted log in with one of the above usernames.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

APDS-9660 Gesture

Gesture sensors allow ambient light and colour measuring, proximity detection and touchless gesture sensing.

This sample demonstrates how to use the APDS-9660 which is connected via I2C interface.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

HTTP Client

See Basic SSL for information on compiling and configuring SSL.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

HttpClient Instapush

Instapush is an event notification service created in 2014.

For details, see https://pushbots.com/.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

ThingSpeak Http Client

Example of a HttpClient pushing data to ThingSpeak .

_images/thing-speak.png
References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

AJAX Http Server

Demonstration of a simple web server with page updates using AJAX.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Bootstrap Http Server

Embedded web server.

This sample will download all required files from a remote server.

_images/esp8266-web-server.png
References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

HttpServer Config Network

Introduction

The HTTP server coming with Sming is quite powerful but it is limited from the available resources of the underlining hardware (your favorite ESP8266 microcontroller). Serving multiple files at once can be problematic. It is not the size of the files that can cause problems, but the number of simultaneous files that need to be delivered. Therefore if you serve multiple CSS or JS files you can optimize your web application before uploading it into your ESP8266 using the advice below.

Optimizing File Delivery

In this example you will see how to combine CSS and JS files, compress them and deliver the optimized content via the HTTP server.

Installation

The file optimization uses gulp. To install it and the needed gulp packages you need to install first npm. Npm is the Node.JS package manager. Once you are done with the installation you can run from the command line the following:

npm install

The command above will install gulp and its dependencies.

Usage

During the development of your web application you should work only in the web/dev/ folder. Once you are ready with the application you can pack the resources and upload them to your device. The commands are

make web-pack make web-upload

That should be it.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

HttpServer Firmware Upload

This example combines the libraries Multipart Parser for file uploads and Over-the-Air Firmware Upgrade, to create a browser based firmware upgrade solution akin to what’s found in many consumer devices. The example is kept as minimal as possible to serve as a starting point for your own applications.

About HTTP server file upload

The HTTP server coming with Sming is quite powerful but it is limited by the available resources of the underlining hardware (your favorite ESP8266 microcontroller).

This sample demonstrates how to use the Multipart Parser library to enable file upload of the HTTP server. On a normal computer the file uploads are usually using temporary space on the hard disk or in memory to store the incoming data.

On an embedded device that is a luxury that we can hardly afford. In this sample we demonstrate how to define which file upload fields should be recognized and what (file) streams are responsible for processing and storing the data. If a field is not specified then its content will be discarded.

About OtaUpgrade

The OtaUpgrade component provides the OtaUpgradeStream class which is hooked up to the web server to process a firmware upgrade file uploaded to the device. The component is also responsible for creating the upgrade files during the build process. A single upgrade file conveniently encapsulates all ROM images, thereby relieving the user from having to know the slot that is updated and manually selecting the corresponding ROM image in a Two-ROM configuration. The file format also supports state-of-the-art security features like a digital signature, encryption and downgrade protection. You are invited to play with them and observe their impact on code size. See also the Over-the-Air Firmware Upgrade documentation for further advice on how to use the security features properly.

Usage instructions
  1. Configure your flash memory layout. See Hardware configuration.

  2. Build the example by running:

    make
    
  3. Connect your device via USB/Serial cable and run:

    make flashconfig
    

    to clear any remains of the previous flash layout configuration, followed by:

    make flash
    

    to install the example firmware. You need to do this only once. Subsequent updates can be performed wirelessly using the web interface.

  4. Point the browser to your ESP’s IP address to open the firmware upgrade page.

  5. Select the upgrade file, which has been automatically created alongside step 2 from out/Esp8266/<build-type>/firmware/firmware.ota and hit the “Update” button.

    After a few seconds, you should see a confirmation the the upgrade was successful. The device now reboots into the upgraded firmware.

    If the upgrade is not successful, rebuild with debug output enabled and check the serial console for error messages.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

HttpServer Plugins

Simple example that demonstrate extending the HttpServer functionality with additional plugins. Using such examples one can add:

  • URL Rewriting

  • Authentication. Sming has already existing plugins for Basic Authentication, IP Limiting

  • Content extracting/processing. For example a gzip decoder plugin can be implemented.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

HttpServer Websockets

Simple example of how to support persistent websocket connections in a server.

References
Environment Variables
  • ENABLE_CMD_HANDLER

SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

AM2321 Humidity Sensor

Example application demonstrating the use of the AM2321 Temperature/Humidity Sensor library.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

BME280 Humidity Sensor

Example application demonstrating the use of the Adafruit BME280 Library library.

This is a port of the bme280test example from the library.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

DHT22 Humidity Sensor

Example application demonstrating the use of the DHT ESP Temperature/Humidity Sensors library with a DHT22 humidity sensor.

_images/dht22.jpg
References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

SI7021 Humidity Sensor

Introduction

Example code for I2C SI7021 sensor board

This code uses the following GPIO:

  • #define I2C_SCL 5 // SCL

  • #define I2C_SDA 4 // SCA

Build instructions

Use make and make flash to build and flash the firmware to the ESP8266 board.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

IR Library

Example application demonstrating how to receive IR remote signals using the ESP8266.

References
SoC support
  • esp8266

WS2812 LEDs

Example application to demonstrate control of WS2812 RGB pixel-strip LEDs.

References
SoC support
  • esp8266

Yeelight LED bulbs

Demonstrates how to control Yeelight WiFi devices.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

BH1750 Light Sensor

ESP8266 BH1750 sensor reader.

_images/bh1750.jpg
References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Liquid Crystal 44780

Introduction

Example code for I2C LiquidCrystal LCDs.

_images/liquid-crystal.jpg

This code uses the following GPIO:

  • GPIO0 SCL

  • GPIO2 SDA

Build instructions

Use make and make flash to build and flash the firmware to the ESP8266 board.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Live Debug

This project is an example of how to integrate GDB debugging into your project. It provides a basic command interface which you can use via regular serial terminal or with the GDB application.

To use this sample application with the command-line GDB application, simply build and flash the project as usual:

make clean
make flash

You should be presented with the GDB command prompt. Enter ‘c’ to continue running the application:

(gdb) c
Continuing.
(attached)

The (attached) prompt is displayed by the LiveDebug application. Type help to get a list of available commands.

Note that if you run this application via serial terminal (make terminal) you’ll get the (Detached) prompt instead.

  1. Debugging under eclipse

Interacting with the GDB console causes problems for eclipse, so compile with the ENABLE_GDB_CONSOLE=0 option:

make clean
make flash ENABLE_CONSOLE=0

Alternatively, use the consoleOff command from the GDB command prompt, then quit the debugger and connect via eclipse.

Exception Handling

Sming comes with a built-in exception handling that takes care to display the stack trace leading to the issue. Usually it looks like this:

***** Fatal exception 28 (LOAD_PROHIBITED)
pc=0x40100e96 sp=0x3ffff640 excvaddr=0x000015b8
ps=0x00000033 sar=0x00000018 vpri=0x000000f0
r00: 0x40100d69=1074793833 r01: 0x3ffff640=1073739328 r02: 0x3fff3900=1073690880
r03: 0x2b265ed4= 723934932 r04: 0x3fffbff0=1073725424 r05: 0x000015b8=      5560
r06: 0x000015b8=      5560 r07: 0x14a8433b= 346571579 r08: 0x00000008=         8
r09: 0x14a842f3= 346571507 r10: 0x3fff22d0=1073685200 r11: 0x00000003=         3
r12: 0x00000048=        72 r13: 0x3fff38c0=1073690816 r14: 0x3ffe9da0=1073651104
r15: 0x3fff1138=1073680696

Stack dump:
To decode the stack dump call from command line:
   make decode-stacktrace
and copy & paste the text enclosed in '===='.
================================================================
3ffff640:  40100e96 00000033 00000018 000000f0
3ffff650:  40100d69 3fff3900 2b265ed4 3fffbff0
3ffff660:  000015b8 000015b8 14a8433b 00000008
3ffff670:  14a842f3 3fff22d0 00000003 00000048
3ffff680:  3fff38c0 3ffe9da0 3fff1138 0000001c
3ffff690:  002222fb c0fb5c01 0bc10000 facfd1fb
3ffff6a0:  620020c0 6162802d 0020c004 59062c52
3ffff6b0:  0020c051 61492c48 210020c0 7c38fb50
...

================================================================

Enter make decode-stacktrace then copy & paste the text to produce something readable like this:

0x40100e96: pvPortRealloc at ??:                                                       ?
0x40100d69: pvPortMalloc at ??:?
0x402455f0: ax_port_malloc at C:\tools\Sming-3.1.2\Sming/third-party/axtls-8266/       replacements/mem.c:51
0x4024561a: ax_port_calloc at C:\tools\Sming-3.1.2\Sming/third-party/axtls-8266/       replacements/mem.c:63
0x40230acc: x509_new at c:\tools\Sming-3.1.2\Sming\third-party\axtls-8266/ssl/x5       09.c:81
0x4023d3e4: m_vsnprintf at C:\tools\Sming-3.1.2\Sming/system/m_printf.cpp:69
0x4023d4a6: m_vprintf at C:\tools\Sming-3.1.2\Sming/system/m_printf.cpp:83
0x40000a39: ?? ??:0
0x4021418a: pp_attach at ??:?
0x40221d60: pbuf_alloc at ??:?
0x40221f0a: pbuf_copy at ??:?
0x4023d3e4: m_vsnprintf at C:\tools\Sming-3.1.2\Sming/system/m_printf.cpp:69

Note that you can also put the stack trace into a file, for example dump.txt then enter:

make decode-stacktrace TRACE=dump.txt

Using the information about the type of the exception (ex: ***** Fatal exception 28) and the sequence of commands might help us figure out the issue.

But that information might not be enough. And finding the root cause may take quite some time.

GDB Debugging

Debugging is a powerful technique allowing you to interactively run your code and be able to see much more information about the things that went wrong.

To use, (Re)compile your project with the ENABLE_GDB option and flash it to the board:

make clean
make ENABLE_GDB=1
make flash

Instead of a terminal, the GDB console will be opened automatically.

If you need to run GDB manually after resetting the board or after it has run into an exception, use the provided script:

make gdb

Note that software breakpoints (‘br’) only work on code that is in RAM. During development you can use the GDB_IRAM_ATTR attribute in your function declarations. Code in flash can only have a hardware breakpoint (‘hbr’).

Read the GDB stub Notes for more information.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

MeteoControl

More complex example of Internet of Things device. Can read humidity and temperature data from sensor and output it to screen with actual time. Time loaded directly from Google. Also with device can automatically control any external load.

Features:

  • temperature

  • humidity

  • actual time from internet

  • built-in display

  • web control interface

  • automatically control external load

  • HTTP API for data reading & writing

  • initialization from internet at first run

_images/meteo-control-iot-device-1.jpg _images/meteo-control-iot-device-2.jpg _images/meteo-control-iot-device-4.jpg

How web interface looks like:

_images/meteo-control-iot-device-config.png
References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

MeteoControl MQTT

Similar to MeteoControl sample without LCD interface and instead controlled via MQTT.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

MQTT Client Hello

Simple MQTT client example.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Network Ping

Sample demonstrating the usage of a network ping to check for connectivity issues.

References
SoC support
  • esp8266

Nextion Button

See this example in action: https://youtu.be/lHk6fqDBHyI

The HMI file included in this example needs to be compiled with the Nextion Editor and uploaded to the Nextion display using standard method.

On WEMOS mini D1 (where this example was tested), the Nextion device is connected to RX/TX pins as required. BUT it needs to be disconnected when uploading the firmware.

So the process is:

  1. Make changes to the cpp code

  2. Build it using “make”

  3. Disconnect the Nextion display if it is connected to Rx/Tx.

  4. Upload firmware (built in step 2) using “make flash”.

  5. Connect Nextion display back again to Rx/Tx.

Note

Always unplug the ESP8266 from USB (connecting with computer) or disconnect power before fiddling with the connections between ESP8266 and Nextion display.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

MCP23017 I2C Port Expander

You can easily add more GPIO connections for an ESP8266 using a port expander. This example demonstrates how to do that with a common I2C port expander.

If you need a faster connection, see MCP23S17 SPI Port Expander.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

MCP23S17 SPI Port Expander

You can easily add more GPIO connections for an ESP8266 using a port expander. This example demonstrates how to do that with a common SPI port expander.

These devices are perhaps a little less common than their I2C equivalents, see MCP23017 I2C Port Expander.

References
SoC support
  • esp8266

BMP180 Pressure Sensor

Introduction

Example code for I2C BMP180 sensor board

_images/bmp180.jpg

This code uses the following GPIO:

  • GPIO0 SCL

  • GPIO2 SDA

Build instructions

Use make and make flash to build and flash the firmware to the ESP8266 board.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

RCSwitch Library

Demonstration of how to control wireless (radio-controlled) devices such as switched mains sockets, TVs, etc.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

nRF24L01 Radio

Example application showing how to interface with the popular nRF24L01 2.4GHz radio transceivers.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

SI443 Radio

Example application for radio module Si4432 aka RF22 driver. Link: http://www.electrodragon.com/w/SI4432_433M-Wireless_Transceiver_Module_%281.5KM_Range,_Shield-Protected%29

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

SD Card

SDCard/FAT file usage and write benchmark.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

5110 LCD Screen

Demonstration of how to interface to Monochrome Nokia 5110 LCD Displays.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • rp2040

SSD1306 OLED Screen

Example of direct work with SSD1306 OLED screen on ESP8266.

Minimal requirements: ESP-03

_images/ssd1306.jpg
References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

ILI9163C TFT Screen

Example of direct work with ILI9163C 1.44” TFT screen on ESP8266.

Minimal requirements: ESP-03

_images/ili9163c.jpg
References
SoC support
  • esp8266

ILI9340 and ILI9341 TFT Screens

Demonstration of how to interface with displays such as the Adafruit 2.8” Touch Shield V2 (SPI).

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

ST7735 TFT Screen

Demonstration of how to interface with various SPI displays using the Adafruit ST7735 library.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

SMTP Client

Purpose

To show the basics of sending an email via SMTP

SMTP

When an IOT device detects a malfunction, it is good to be able to notify the user. We can do that either by sending them an email or via a service like MQTT.

This sample shows how to send an email via SMTP directly from the ESP8266.

smtp2go conveniently provides a free account.

Create an account here: https://www.smtp2go.com/setupguide/arduino/ .

It needs some configuration: * the name of the SMTP server, eg “mail.smtp2go.com” * a username and password * the name and email of the person from whom the email will be sent * the name and email of the person to send the email to

Edit the sample to replace these values and the SSID etc.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

SystemClock NTP

Demonstrates various methods for keeping the system clock updated from a Network Time Server using the NtpClient class.

The Timezone class is used to convert between UTC and local times, accounting for daylight savings.

The SolarCalculator class is used to calculate times of next sunrise and sunset, which you might want to automate activation of lights, for example.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

TCP Client NarodMon

An example of sending data to narodmon.ru using a TCP client.

https://narodmon.ru is a geo-information project to display on the world map and control (on PCs, smartphones and other gadgets) the sensor readings of its members (temperature, humidity, pressure, wind speed and direction, radiation, energy consumption and any other values), as well as private and urban webcams for public or private viewing.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

DS1820 Temperature Sensor

DS1820/DS18B20 sensor reader.

_images/ds1820.jpg
References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

UDP Server Echo

Sets up a UDP server which echoes back incoming packets. By default listens on port 1234.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

UDP Server mDNS

Instructions

The multicast Domain Name System (mDNS) resolves host names to IP addresses within small networks that do not include a local name server.

More info on mDNS can be found at https://en.wikipedia.org/wiki/Multicast_DNS

mDNS has two parts:

  1. Advertise

  2. Discover

This example just does Advertise. See Basic MDNS for discovery example.

In short this code will advertise other machines about its ipaddress.

How to use mDNS:

  1. ADD your WIFI_SSID / WIFI_PWD

  2. Flash the Complied code to your ESP8266/ESP32 device

  3. Open a web browser and go to “http://sming.local/” to open a sample webpage.

You should also be able to ping using the advertised name:

ping UDP_Server.local

Linux

You need to install Avahi mDNS/DNS-SD daemon.

In your browser type “http://sming.local/” to open a sample webpage.

Android

You need to install ZeroConf Browser or Bonjour Browser.

In those app you would be able to see IP address of your ESP module.

In android Chrome “http://sming.local/” does not translate to IP address, so android Chrome is not supporting mDNS.

But you can make your own app using Network Service Discovery. See http://developer.android.com/training/connect-devices-wirelessly/nsd.html for details.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

HCSR04 Ultrasonic Transducer

Shows how to use an HCSR04 module to measure distances.

Warning

Ultrasonic_HCSR04 modules usually work with 5v power and TTL levels, so you NEED voltage divider or level shifter for ECHO pin.

Trigger pin is tolerant to 3.3v and should work without problems.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

WebcamServer sample

A sample providing access to webcams via HTTP server.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Websocket Client

This is a simple demo of the WebsocketClient class. It shows connection, closing and reconnection methods of WebsocketClient.

The client tries to connect to a websocket echo server. It sents 10 messages then client connection is closed. This sequence repeats after 20 seconds.

The sample was originally written to communicate with echo.websocket.org but that service no longer exists. Instead, run make wsserver to run a local test server. This has the advantage of showing detailed diagnostic information which may be helpful.

Build with WS_URL set to the server address. For example:

make WS_URL=ws://192.168.1.10:8000

References
Environment Variables
  • WS_URL

SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

WiFi Sniffer

Introduction

This is an adaptation of the WiFi promiscuous mode demo code from Arduino

See https://www.hackster.io/rayburne/esp8266-mini-sniff-f6b93a

// Notes.h tab in Arduino IDE is only for comments and references!

// based on RandDruid/esp8266-deauth (MIT) https://github.com/RandDruid/esp8266-deauth
// inspired by kripthor/WiFiBeaconJam (no license) https://github.com/kripthor/WiFiBeaconJam
// https://git.schneefux.xyz/schneefux/jimmiejammer/src/master/jimmiejammer.ino
// requires SDK v1.3: install esp8266/Arduino from git and checkout commit 1c5751460b7988041fdc80e0f28a31464cdf97a3
// Modified by M. Ray Burnette for publication as WiFi Sniffer 20161013
/*
   Arduino 1.6.12 on Linux Mint 17.3
    Sketch uses 227,309 bytes (21%) of program storage space. Maximum is 1,044,464 bytes.
    Global variables use 45,196 bytes (55%) of dynamic memory, leaving 36,724 bytes for local variables. Maximum is 81,920 bytes.

*/

/*
  // beacon template
  uint8_t template_beacon[128] = { 0x80, 0x00, 0x00, 0x00,
                                /*4*/
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    /*10*/ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
    /*16*/ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
    /*22*/ 0xc0, 0x6c,
    /*24*/ 0x83, 0x51, 0xf7, 0x8f, 0x0f, 0x00, 0x00, 0x00,
    /*32*/ 0x64, 0x00,
    /*34*/ 0x01, 0x04,
    /* SSID */
    /*36*/ 0x00, 0x06, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x24, 0x30, 0x48, 0x6c,
    0x03, 0x01,
    /*56*/ 0x04
}
;
* /

    /*    Notes:
  Ref: http://www.esp8266.com/viewtopic.php?f=32&t=7025
  In the ESP8266WiFi.h, there is the function getNetworkInfo() which I presume allows you to get
  info for hidden AP.

  bool getNetworkInfo(uint8_t networkItem, String &ssid, uint8_t &encryptionType, int32_t &RSSI, uint8_t* &BSSID, int32_t &channel, bool &isHidden);
  CODE: SELECT ALL
    /**
       loads all infos from a scanned wifi in to the ptr parameters
       @param networkItem uint8_t
       @param ssid  const char*
       @param encryptionType uint8_t
       @param RSSI int32_t
       @param BSSID uint8_t *
       @param channel int32_t
       @param isHidden bool
       @return (true if ok)
*/

    /*    Serial Console Sample Output:
  ESP8266 mini-sniff by Ray Burnette http://www.hackster.io/rayburne/projects
  Type:   /-------MAC------/-----WiFi Access Point SSID-----/  /----MAC---/  Chnl  RSSI
  BEACON: <=============== [                      TardisTime]  1afe34a08bc9    8    -76
  BEACON: <=============== [                     xfinitywifi]  56571a0730c0   11    -90
  BEACON: <=============== [                                ]  52571a0730c0   11    -91
  BEACON: <=============== [                      ATTGH6Gs22]  1005b1d6ff90   11    -95
  BEACON: <=============== [                      ATT4P3G9f8]  1c1448777420   11    -92
  BEACON: <=============== [                       HOME-30C2]  5c571a0730c0   11    -91
  BEACON: <=============== [                      ATT8Q4z656]  b077acc4dfd0   11    -92
  BEACON: <=============== [                       HOME-B1C2]  94877c55b1c0   11    -94
  BEACON: <=============== [                        HUXU2012]  0c54a5d6e480    6    -94
  BEACON: <=============== [                     xfinitywifi]  0c54a5d6e482    6    -97
  BEACON: <=============== [                                ]  0c54a5d6e481    6    -96
  DEVICE: 18fe34fdc2b8 ==> [                      TardisTime]  1afe34a08bc9    8    -79
  DEVICE: 18fe34f977a0 ==> [                      TardisTime]  1afe34a08bc9    8    -94
  DEVICE: 6002b4484f2d ==> [                      ATTGH6Gs22]  0180c2000000   11    -98
  BEACON: <=============== [                   HOME-01FC-2.4]  84002da251d8    6   -100
  DEVICE: 503955d34834 ==> [                      ATT8Q4z656]  01005e7ffffa   11    -87
  BEACON: <=============== [                                ]  84002da251d9    6    -98
  BEACON: <=============== [                     xfinitywifi]  84002da251da    6    -95
  BEACON: <=============== [                                ]  fa8fca34e26c   11    -94
  DEVICE: cc0dec048363 ==> [                      ATT8Q4z656]  01005e7ffffa   11    -88
  BEACON: <=============== [                                ]  fa8fca95bad3   11    -92
  BEACON: <=============== [                       HOME-5475]  58238c3b5475    1    -96
  BEACON: <=============== [                     xfinitywifi]  5a238c3b5477    1    -94
  BEACON: <=============== [                                ]  5a238c3b5476    1    -96
  DEVICE: 1859330bf08e ==> [                      ATT8Q4z656]  01005e7ffffa   11    -92
  BEACON: <=============== [                                ]  92877c55b1c0   11    -92
  DEVICE: f45fd47bd5e0 ==> [                      ATTGH6Gs22]  ffffffffffff   11    -93
  BEACON: <=============== [                           Lynch]  744401480a27   11    -96
  BEACON: <=============== [                     xfinitywifi]  96877c55b1c0   11    -93
  DEVICE: f43e9d006c10 ==> [                     xfinitywifi]  8485066ff726    6    -96
  DEVICE: 285aeb4f16bf ==> [                      ATTGH6Gs22]  3333ffb3c678   11    -94
  DEVICE: 006b9e7fab90 ==> [                      ATTGH6Gs22]  01005e7ffffa   11    -91
  DEVICE: 78456155b9f0 ==> [                           Lynch]  01005e7ffffa   11    -95
  DEVICE: 6cadf84a419d ==> [                       HOME-30C2]  88cb8787697a   11    -89
  BEACON: <=============== [           Verizon-SM-G935V-6526]  a608ea306526   11    -92


*/
References
SoC support
  • esp8266

Hosted RPC Server over Serial

Overview

This application creates a RPC server that will communicate over serial interface. To compile it for an Esp8266 microcontroller you can use the following command:

make SMING_ARCH=Esp8266
References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Hosted RCP Server over TCP

Overview

This application creates a RPC server that will communicate over TCP. You can either start an Access Point from the controller or connect the application to an existing WIFI Access point. The latter can be compiled using the following command:

make SMING_ARCH=Esp8266 CONNECT_TO_WIFI=1 WIFI_SSID="MySSID" WIFI_PWD="Secr3tP4Ssw0rd"
Configuration
CONNECT_TO_WIFI

Default: 0 (disabled)

If set to 1 the application will try to connect to a WIFI access point. Make sure to provide also the WIFI_SSID and WIFI_PWD values.

If set to 0 the application will start an access point to which the Host application can connect.

References
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic AnimatedGIF

Sample demonstating the usage of the optimized AnimatedGIF library together with Sming Graphics Library library.

You should be able to see the following animated image:

_images/frog.gif

Image source: Animated Images Dot Org.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

TensorFlow_HelloWorld

The power of Machine Learning (ML) inside you microcontroller! This sample is a remake of the standard HelloWorld example from TensorFlowLite for Arduino. It demonstrated how a ML “model” can be used inside an ESP8266 mircocontroller with the help of Sming.

If you are not familiar with TensorFlow take a look at their documentation.

Compilation

This sample can be compiled and run on a ESP8266 microcontroller or on the Host architecture. In order to compile it for ESP8266 you must use ESP Quick Toolchain. The ESP Quick Toolchain is more feature-complete and allows the compilation to run without many modifications to the original library.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Webcam

Sample demonstrating communication with a web camera via AT commands using At Client library. The complete set of commands for the sample web camera are in the docs/ directory.

More about the web camera module and commands can be found here.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Bluetooth Gamepad

Introduction

This sample turns the ESP32 into a Bluetooth LE gamepad that presses buttons and moves axis

Possible buttons are: BUTTON_1 through to BUTTON_16 (16 buttons supported by default. Library can be configured to support up to 128)

Possible DPAD/HAT switch position values are: DPAD_CENTERED, DPAD_UP, DPAD_UP_RIGHT, DPAD_RIGHT, DPAD_DOWN_RIGHT, DPAD_DOWN, DPAD_DOWN_LEFT, DPAD_LEFT, DPAD_UP_LEFT (or HAT_CENTERED, HAT_UP etc)

bleGamepad.setAxes takes the following int16_t parameters for the Left/Right Thumb X/Y, Left/Right Triggers plus slider1 and slider2, and hat switch position as above: (Left Thumb X, Left Thumb Y, Right Thumb X, Right Thumb Y, Left Trigger, Right Trigger, Hat switch position ^ (1 hat switch (dpad) supported by default. Library can be configured to support up to 4)

Library can also be configured to support up to 5 simulation controls (can be set with setSimulationControls) (rudder, throttle, accelerator, brake, steering), but they are not enabled by default.

Testing

You can use one of the following applications on your PC to test and see all buttons that were clicked.

On Linux install jstest-gtk to test the ESP32 gamepad. Under Ubuntu this can be done by typing the following command:

sudo apt install jstest-gtk

On Windows use this Windows test application.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Bluetooth Keyboard

This sample demonstrates how to turn an Esp32 device into external keyboard. The “keyboard” and your PC will be communicating using Bluetooth Low Energy (BLE). The “keyboard” will write words, press Enter, press a media key and, if enabled in the sample code, Ctrl+Alt+Delete.

Usage

Once this sample is flashed and running on your ESP32 you can test it. Open a new text editor on your PC. Then search from your PC for new bluetooth devices. A device named “Sming BLE Keyboard” should show up. Connect to it and focus/open you text editor window. Be fast. Soon enough a “Hello World” text will start to be “magically” typed inside your text editor.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

CS5460 generic sample

The sample prints the measured RMS voltage each second (with CS5460 voltage and current filters enabled).

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Arducam

A demonstration application for controlling ArduCAM camera modules via web or telnet interface.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

CommandLine

Demonstrates Sming’s CommandProcessing capability via serial interface.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

TelnetServer

A demonstration of a telnet server built using CommandProcessing.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Control your DIAL-enabled smart monitor/TV using Sming

Demonstrates starting of YouTube, playing a video and closing YouTube after a small period of time.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic FlashIP

Introduction

This is a simplified version of the Basic_OTA sample using the FlashIP approach instead.

There is one application partition and one LittleFS filing system partition. NB. You can also use SPIFFS and FWFS.

Building
  1. Set WIFI_SSID & WIFI_PWD environment variables with your wifi details

  2. Edit the OTA server details defined in the application component.mk file

  3. make flash

  4. Put app.bin in the root of your webserver for OTA

  5. Interact with the sample using a terminal (make terminal)

Testing

For testing purposes Sming offers a simple python webserver that can be run on your development machine:

make otaserver

The server listens on port 9999 and all network interfaces.

The current directory is changed to the firmware directory for the current project, so you could, for example, open a separate terminal window in the Basic_Serial directory and do this:

make SMING_SOC=rp2040
make otaserver

This will then be ready to serve up the app.bin image file for the Basic_Serial sample.

This sample should be compiled and flashed as follows, replacing 192.168.1.30 with your development machine’s IP address and inserting details for your local WiFi:

make flash WIFI_SSID=... WIFI_PWD=... FIRMWARE_URL=http://192.168.1.30:9999/app.bin

Once connected to WiFi, you can enter ota to download and re-program the new firmware.

Configuration
FIRMWARE_URL

The URL for the application image to be downloaded

References
Environment Variables
SoC support
  • host

  • rp2040

Google Cast Client

Sample application that demonstrates how to send information directly from your application to your Smart TV using the Google Cast protocol.

A media file is started playing. From the terminal, you can control as follows:

‘f’: Skip forward 10 seconds ‘r’: Skip back 10 seconds space: Toggle between pause and play ‘q’: Quit playback

The chromecast device must be online at startup, or a connection error will result. Re-start the application to retry.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Advanced Animation

Lots of rectangles flying around the screen. Again.

Does the same thing as the Basic Animation sample but uses the display driver directly which eliminates the Scene construction step.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Animation

Lots of rectangles flying around the screen.

Port of Bob’s animation tutorial code. Thanks Bob!

https://www.youtube.com/watch?v=q9xWvLZg7Lc

https://bytesnbits.co.uk/basic-animation-spi-touchscreen/

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Graphics

Demonstration of how to use the Sming Graphics Library library with the Host Virtual Screen or displays such as the Adafruit 2.8” Touch Shield V2 (SPI).

To try this out from the Host Emulator you can use the virtual screen:

make virtual-screen

The IP address:port will be shown at the command prompt and in the title bar, for example 192.168.1.105:7780. Build the application:

make SMING_ARCH=Host VSADDR=192.168.1.105 VSPORT=7780

Note: The screen server address and port are passed by command line so the application doesn’t require rebuilding. For example:

out/Host/debug/firmware/app vsaddr=192.168.1.105 vsport=7780
References
Environment Variables
  • GUITIMER_INTERVAL

  • MAX_LOOP_COUNT

  • SHOWFONT_INTERVAL

SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Touch

Demonstration of how to use the Sming Graphics Library library with touch screens such as the Adafruit 2.8” Touch Shield V2 (SPI).

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Bresenham

Exploration of advanced anti-aliased line and curve rendering algorithms.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Color Test

Simple test that should draw from left to right rectangles with red, green, blue and white color.

Useful for display driver diagnostics. The TFT_eSPI project describes quite nicely the different switches that can affect the colors in MIPI displays:

Different hardware manufacturers use different colour order
configurations at the hardware level.  This may result in
incorrect colours being displayed.
Incorrectly displayed colours could also be the result of
using the wrong display driver...
Typically displays have a control register (MADCTL) that can
be used to set the Red Green Blue (RGB) colour order to RGB
or BRG so that red and blue are swapped on the display.
This control register is also used to manage the display
rotation and coordinate mirroring. The control register
typically has 8 bits, for the ILI9341 these are:
Bit Function
7   Mirror Y coordinate (row address order)
6   Mirror X coordinate (column address order)
5   Row/column exchange (for rotation)
4   Refresh direction (top to bottom or bottom to top in portrait orientation)
3   RGB order (swaps red and blue)
2   Refresh direction (top to bottom or bottom to top in landscape orientation)
1   Not used
0   Not used
The control register bits can be written with this example command sequence:

   tft.writecommand(TFT_MADCTL);
   tft.writedata(0x48);          // Bits 6 and 3 set

0x48 is the default value for ILI9341 (0xA8 for ESP32 M5STACK)
in rotation 0 orientation.

Another control register can be used to "invert" colours,
this swaps black and white as well as other colours (e.g.
green to magenta, red to cyan, blue to yellow).

Source: https://github.com/Bodmer/TFT_eSPI/blob/master/examples/Test%20and%20diagnostics/Colour_Test/Colour_Test.ino

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Custom Object

Demonstates how to create a custom object and render it.

This sample draws a Mandelbrot set which is particular CPU intensive with lots of iteration and floating point calculations.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Alexa

If you have an Echo Dot or other Amazon Alexa device, this shows how to provide simple support by emulating a Philips Hue lighting bridge.

Pairing is permanently enabled in this sample application so all you need to do is ask Alexa to discover devices and the lights should appear.

You can verify this is working by requesting a list of registered lights via HTTP:

http://IP_ADDRESS/api/user/lights

Turning the ESP LED on and off can be done by sending a POST request to light 101. The RESTED plugin for firefox is very useful for this sort of thing. The endpoint URL is:

http://IP_ADDRESS/api/user/lights/101/state

To turn the LED ON, the body of the request would contain:

{"on":true}

And to turn it off again:

{"on":false}

Remember to set the Content-Type header to application/json.

Here’s how to do it with CURL:

curl -X POST -H "Content-Type: application/json" -d "{on: true}" http://IP_ADDRESS/api/user/lights/101/state
References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic RS485

Demonstrates basic use of IO Control library to set up one MODBUS device and one DMX device on serial port 0.

Debug output is moved to serial port 1.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Fan Controller

Using RPi Pico to control three 4-pin PWM fans for inverter.

  • Output 20kHz PWM to each fan independently

  • Measure sync pulses from each fan to confirm rotation rate and detect failure

  • Provide modbus slave control

References
SoC support
  • rp2040

LittleFS inspector

Builds a basic filesystem and includes simple code to display commits by reading partition directly. Helps with understanding structure and evaluating performance.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic MDNS

Demonstrates use of the MDNS library to discover local devices.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

MHZ19 CO2 sensor reading

Example application demonstrating the use of the MHZ19 CO2 Sensor library.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

MHZ19 Span-calibration

Example demonstrating span-calibration procedure.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

MHZ19 Zero-Calibration

Example demonstrating Zero-calibration procedure.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

ModbusMaster RTU generic sample

The generic sample calls mbLoop() each second. In mbLoop() the globalSeconds variable is incremented and sent to a slave device. Then the same register address of the same slave device is read and the result is output via debugf() using UART1.

The modbus response timeout can be changed using MB_RESPONSE_TIMEOUT.

Check out my video.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

HttpServer Upload

This example demonstrates how to upload multiple files on the server.

About HTTP server file upload

The HTTP server coming with Sming is quite powerful but it is limited by available hardware resources.

This sample demonstrates how to use the Multipart Parser library to enable file upload of the HTTP server. On a normal computer the file uploads are usually using temporary space on the hard disk or in memory to store the incoming data.

On an embedded device that is a luxury that we can hardly afford. In this sample we demonstrate how to define which file upload fields should be recognized and what (file) streams are responsible for processing and storing the data. If a field is not specified then its content will be discarded.

Usage instructions
  1. Configure your flash memory layout. See Hardware configuration.

  2. Build the example by running:

    make
    
  3. Connect your device via USB/Serial cable and run:

    make flash
    

    to install the example firmware.

  4. Point the browser to your device’s IP address to open the firmware upgrade page.

  5. Select the upload file and hit the “Update” button.

    If the file size isn’t bigger than the MAX_FILE_SIZE defined in the application code you should see a confirmation that the upload was successful.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

OTA over MQTT

Introduction

This example demonstrates how you can create an application that updates its firmware Over The Air (OTA) using the MQTT protocol. This application uses OTA Firmware Upgrade via MQTT and follows the recommended versioning principles.

Based on ENABLE_OTA_ADVANCED the firmware data can be either without any encoding or be signed and encrypted.

Tools

There are two tools that facilitate the packiging and deployment of a new firmware.

For more information read Firmware packaging in the documentation of the OTA Firmware Upgrade via MQTT component.

Security

Depending on ENABLE_SSL a standard SSL/TLS can be enabled:

  1. The communication between the application and the server will be encrypted using standard SSL.

  2. To prove that the server is the correct one: The MQTT clients should pin the public key fingerprint on the server. OR have a list of public key fingerprints that are allowed.

  3. Depending on ENABLE_CLIENT_CERTIFICATE the application can send a client certificate that is signed by the server.

Configuration

If ENABLE_SSL is enabled (highly recommended), OTA upgrade files will be transferred securely over TLS/SSL.

APP_ID

Default: “test”

This variable contains the unique application name.

APP_VERSION

Default: not set

Contains the application major and minor versions separated by comma. Example “4.2”. If not set will use the current major and minor version from Sming.

APP_VERSION_PATCH

Default: not set

Contains the application patch version as integer. For stable versions you can use 0 until 255. For unstable versions the current timestamp can be used as a patch version.

ENABLE_CLIENT_CERTIFICATE

Default: 0 (disabled)

Used in combination with ENABLE_SSL. Set to 1 if the remote server requires the application to authenticate via client certificate.

MQTT_URL

Default: depends on ENABLE_SSL and ENABLE_CLIENT_CERTIFICATE values

Url containing the location of the firmware update MQTT server.

References
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

RingTone Player

Introduction

You may find the need to include basic audio features such as haptic feedback or tune generation to support an existing application. The advantage of using I2S for this is the very low CPU overhead.

This sample demonstrates how simple audio can be added to an application using the I2S ToneGenerator and RingTone libraries. The sample contains a selection of tunes.

The sample may be controlled via serial terminal, or by web interface.

Serial connection

Because I2S uses the serial RX pin, the serial port uses the alternate pin mappings. See Tone Generator for connection details.

You’ll still need the regular serial port for programming. Here’s a sample setup for Windows:

  • Using NodeMCU with standard serial port connected to COM4

  • Second USB/UART converter connected to alternate serial pins as COM8

The command line to build, flash and open a terminal would be:

make -j COM_PORT=COM8 COM_SPEED=115200 COM_PORT_ESPTOOL=COM4 COM_SPEED_ESPTOOL=921600
make flashinit
make flash
Web interface

This is built using Bootstrap 4.3.1 and jQuery 3.4.1.

_images/ringtone-player.png
img-reboot Reboot

Un-installs the I2S driver then restarts the system after 5-seconds. The memory consumption drops as the DMA buffers are de-allocated.

img-play Play controls
▢ Stop

Tune playback stops at the current position, and the I2S driver is stopped so I2S interrupts are disabled. DMA buffers remain allocated.

|| Pause

Tune playback stops at the current position, but I2S interrupts remain active.

▷ Play

Resumes tune playback from stop/pause states.

img-mode Voice and Mode

Selects available voice for the tone generator (Sine, Triangular, Sawtooth or Square wave).

Playback mode

  • ⇢ Sequential

  • 🔀 Random

Playback speed

img-speed

Tune selection
img-select
↤ Previous

Play previous tune

↲ Rewind

Rewind to start of current tune

↦ Next

Play next tune

→ Skip

Skip to a random tune

img-current

Shows the current tune and elapsed play time. A drop-down list shows all available tunes.

Graphs

In addition to play controls, there are three graphs showing some statistics over a two-minute period. The data is sent from the ESP8266 via websocket once per second.

CPU Usage
CPU Usage graph

Gives an indication of processor loading. Try connecting a second web client to see what happens.

Fill Time
Fill time graph

Shows the time taken by the Tone Generator to create and buffer tone waveforms.

This graph has three traces, indicating time in milliseconds over the 1-second update period:

  • Red: Longest time taken

  • Green: Shortest time

  • Black: Average time

You’ll see more activity during complex, fast tunes but also for lower notes which require more samples (and larger buffers).

The progress bar indicates the total time taken over the update period.

Memory Usage
Memory usage graph

The graph has three traces:

  • Black: Available memory

  • Red: Maximum memory used during the update period

  • Green: Memory in use at the time of update

The red/green traces generally follow each other and represent the allocation/deallocation of tone buffers.

References
SoC support
  • esp8266

  • host

Switch Joycon

Introduction

This sample turns the ESP32 into a Switch Joycon (Bluetooth LE gamepad) that presses buttons and moves axis

Possible buttons are 0 through to 15.

Possible HAT switch position values are: Centered, Up, UpRight, Right, DownRight, Down, DownLeft, Left, UpLeft.

Testing

You can use one of the following applications on your PC to test and see all buttons that were clicked.

On Linux install jstest-gtk to test the ESP32 gamepad. Under Ubuntu this can be done by typing the following command:

sudo apt install jstest-gtk

On Windows use this Windows test application.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Screen TFT_S1D13781

This is a port of the demo from the Epson S1D13781 display controller. See TFT_S1D13781.

Evaluation boards are inexpensive and is a useful way to evaluate display modules with TFT interfaces.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic ControlPoint

Demonstrates use of UPnP library to create and use control points.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic UPnP

Demonstrates use of UPnP library.

The sample devices here can be fully enumerated over the network.

Services

UPnP devices may also provide services which can be enumerated and used to control it.

The Wemo device Wemo::Controllee has two services for events and metadata.

The device and service descriptions are stored in the schema directory. This is used by UPnP to generate class code, so all we need to do is implement the action methods.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Device

Application demonstrating how to set up USB device with multiple interfaces.

The USB configuration is described in basic_device.usbcfg, and the corresponding TinyUSB configuration files generated in, for example, out/Rp2040/debug/USB.

C++ classes are provided for some of the standard interfaces

The MIDI device can be tested using the linux aplaymidi utility. Type aplaymidi -l to show available devices. Play a MIDI file with aplaymidi -p 24:0 test.mid.

Note

This sample works as-is for the rp2040. The esp32s2 has limited endpoints so some of the interfaces need to be commented-out in basic_device.usbcfg. Perhaps try starting with just midi interface.

References
SoC support
  • esp32s2

  • esp32s3

  • host

  • rp2040

Basic Host

Application demonstrating how to set up USB host with multiple interfaces.

References
SoC support
  • host

  • rp2040

CDC MSC HID Host sample

This is a port of the hid_composite example from TinyUSB (https://github.com/hathach/tinyusb/tree/master/examples/host/cdc_msc_hid).

References
SoC support
  • rp2040

HID Composite Device sample

This is a port of the hid_composite example from TinyUSB (https://github.com/hathach/tinyusb/tree/master/examples/device/hid_composite).

Once connected, the PICO LED will respond to the state of keyboard CAPS LOCK.

References
SoC support
  • esp32s2

  • esp32s3

  • host

  • rp2040

Basic FlatBuffers Sample

Basic sample demonstrating encoding and decoding of data using flatbuffers. For more complicated examples take a look at the official flatbuffers tutorial.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Advanced_Jsvm

To save space and be able to run JerryScript on an embedded device, this library builds without parser support.

This means that the JavaScript files have to be compiled into ‘snap’ files before landing on the device.

This can be done during normal compilation and flashing, or indirectly via web application.

Manual compilation method

You should have your device connected physically to your computer via USB cable.

In this case the compilation will be done on your computer and the compiled files will be saved to your device.

In order to configure the JavaScript file compilation one can use APP_JS_SOURCE_DIR and APP_JS_SOURCE_DIR variables.

All .js files in the APP_JS_SOURCE_DIR directory will be compiled into .snap files and written to APP_JS_SNAPDIR directory. Example:

make APP_JS_SOURCE_DIR=files/js APP_JS_SNAP_DIR=out/web

In this advanced sample the variables are directly stored in component.mk file.

The generated .snap files may be flashed or uploaded to your device for execution via any appropriate mechanism.

Web Browser compilation method

Quite often we don’t have physical access to our devices. And here comes the true beauty of this sample. With only network access to the device and a modern browser one can still compile JavaScript files and upload them to the device. No external tools are required.

The advanced sample starts a web server with a simple page. In it you will see the initial source code of the main.js file.

There are two buttons in the HTML page. The first one starts and stops the JavaScript VM. If you press Run you will be able to see the result from the execution in the device terminal. If you have your device connected to your computer then type the following to see the output:

make terminal

If you want to change the JavaScript code and test it again on your device you have to press Stop. Do directly in the browser the desired changes and press Compile. If the modified JavaScript is valid then it will be compiled directly in your browser and sent to your device. Again no external tools are required - just a modern browser. After that you can press Run in the browser and enjoy the modified result in the terminal.

Credits

The initial work on the JerryScript library for Sming was done as part of the U:Kit project.

References
Environment Variables
  • GZIP

SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic Context

Jerryscript provides the optional External context feature to allow applications to provide an external buffer or isolated engine contexts. The heap size is configured at runtime and enables loading of multiple javascript applications.

This sample demonstrates how to use the Jerryscript JS::Context class to implement containerised applications.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic_Jsvm

This sample demonstrates running JavaScript applications in a sandbox inside a microcontroller. The sample uses the jerryscript library for Sming.

To save space and be able to run JerryScript on an embedded device Sming compiles this library without a parser. This means that the JavaScript files have to be compiled before landing on the device. This sample demonstrates how this is done automatically for you.

Snapshots

The initial JavaScript code used in this sample is located in files/main.js. The code has one global variable x and two functions setup and loop. As shown below:

var x;

/**
 * This is a function that will be called once.
 */
function setup() {
        x = 0;
        print ('Setup: X='+x);
}

/**
 * This is a function that will be called every half a second
 */
function loop() {
        x++;
        print('Loop: X='+x);
}

Once compiled the source code will be converted into a byte-code that will be executed inside of your microcontroller. In JerryScript this final byte-code is called snapshot or just snap.

Calls

This sample will run first the setup JavaScript function and then execute in an endless loop the loop function.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Event_Jsvm

This sample demonstrates how to run your JavaScript application as performing some action in response to an event, input or stimulus. Since Sming framework itself has an event-driven architecture such JavaScript applications will be able to use all the advantages that Sming has in terms of CPU, RAM and power usage.

JavaScript Code

Similar to Sming this sample calls the init JavaScript function. And in this function we register the events that we are interested in. As shown below.

/**
 * This is a function that will be called once.
 */
function init() {
        x = 0;
        print('Init: X=' + x);

        // This is how we register an event listener in JavaScript
        addEventListener("EVENT_TEMP", function(event) {
                // An event has a name and multiple params
                // the params have string keys and string values
                print('Event name: ' + event.name + ', value: ' + event.params['temp']);
        });

        // ...
}

The addEventListener mimics the browser addEventListener function. It accepts two parameters - a unique event name and a callable function. Similar to the browser function you can add multiple callable functions to one event.

C/C++ Code

The addEventListener function is not present in the standard JerryScript VM. In this sample we register it as a built-in JavaScript function. Excerpt from the application.cpp file:

void startJsvm()
{
        /*
         * This is how we register a new function in JavaScript
         * that will communicate directly with our C/C++ code.
         */
        jsVm.registerFunction("addEventListener", addEventListener);

        // ...

The actual implementation of the addEventListener C++ function is in the vm_functions.cpp file. There you will find also the triggerEvent function which is available only in the C/C++ code. The latter is used to trigger events from the C/C++ code inside the JavaScript application:

JsEventData params;
params["temp"]="20";
triggerEvent("EVENT_TEMP", params);

The code above simulates a temperature sensor sending the temperature in Celsius (C).

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Modbusino RTU generic sample

The sample code provides three element uint16_t array used for the modbus slave registers. These values are printed each second through UART1. mbPrint() is triggered on valid write multiple registers (0x10) command.

Several environment variables (envvar:RS485_RE_PIN RS485_TX_LEVEL) can be used for configuration. The slave address is defined with envvar:MB_SLAVE_ADDR. For example:

make MB_SLAVE_ADDR=2

These variables can be listed with:

make list-config
References
Environment Variables
  • MB_SLAVE_ADDR

SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Basic_VM

This sample is a starting point for working with the rBPF system on Sming.

See rBPF Femto-Container support for toolchain setup.

The sample contains three functions:

  • increment() simply adds 1 to the parameter value and returns it.

  • multiply() instead stores the output value in the context parameters.

  • store() demonstrates parameter passing using the stores.

The source code for these can be found in the container subdirectory. Each file contains a single function which is compiled into eRBF code for execution by the virtual machine.

Build the sample like any regular Sming application by running make. You can try it out without any hardware using the Host Emulator:

make SMING_ARCH=Host
make run

You should see this:

All up, running the Femto-Container application now
Calling 'increment()' in VM
input 0, result 1, expected 1
Calling 'multiply()' in VM
input (120000005, 120000023), output 14400003360000115, expected 14400003360000115, result 0
Calling 'store()' in VM
output (1001234, 2005678), result (1234, 5678)

Note that if a runtime error occurs then an appropriate error message is displayed.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

UserCalls

This sample demonstrates how to provide application functions for use by VM containers.

  1. Declare calls

    This sample provides three application calls:

    int bpf_user_get_magic();
    void bpf_user_set_magic(int value);
    size_t bpf_user_send_packet(char* data, size_t len)                                                   \
    size_t bpf_user_read_packet(char* buffer, size_t len)
    

    These are declared using the BPF_SYSCALL_APP macro.

    The include/bpf_appcalls.h must be provided in the container directory.

  2. Implement calls

    The application must provide implementations for the declared functions in the rBPF::VM namespace. Implementations are identical to the VM calls but with an additional pointer to the VM :cpp:typedef:`bpf_t` instance:

    int bpf_user_get_magic(bpf_t* bpf);
    void bpf_user_set_magic(bpf_t* bpf, int value);
    size_t bpf_user_send_packet(bpf_t* bpf, char* data, size_t len)                                                   \
    size_t bpf_user_read_packet(bpf_t* bpf, char* buffer, size_t len)
    

    Note that when passing pointers these must be checked using either bpf_store_allowed() or bpf_load_allowed().

    See appcalls.cpp for the implementations.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Libraries

Sming comes with a number of ported libraries that cover many areas of embedded programming but for sure not all.

To reduce the size of the framework, some libraries are not included directly and must be retrieved if you wish to access any sample applications there.

For example, the Basic UPnP sample application is contained in the UPnP library, which can be retrieved like this:

cd $SMING_HOME
make fetch UPnP

You should then get UPnP: found in ‘Libraries/UPnP’, so now we can build the sample:

cd Libraries/UPnP/samples/Basic_UPnP
make

Note

If your project references any libraries they will automatically be pulled in during the build.

These are all the libraries included with Sming:

AM2321 Temperature/Humidity Sensor

Example:
#include <AM2321.h>

void readByAM2321()
{
    AM2321 am2321;
    am2321.read();

    Serial.print("(");
    Serial.print(am2321.temperature/10.0);
    Serial.print(", ");
    Serial.print(am2321.humidity/10.0);
    Serial.println(')');
}
References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

APA102 LED

APA102 library by HappyCodingRobot@github.com

APA102 class enhanced to work with hardware SPI and software SPI. Changed to work with the new SPI implementation.

See Pull Request #787.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Adafruit BME280 Library

Build Status

This is a library for the Adafruit BME280 Humidity, Barometric Pressure + Temp sensor

Designed specifically to work with the Adafruit BME280 Breakout.

Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!

About this Driver

Written by Ladyada for Adafruit Industries.

BSD license, check license.txt for more information

All text above must be included in any redistribution

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Adafruit Bus IO Library

Build Status

This is a helper library to abstract away I2C & SPI transactions and registers

Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!

MIT license, all text above must be included in any redistribution

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Adafruit GFX Library

Build Status

This is the core graphics library for all our displays, providing a common set of graphics primitives (points, lines, circles, etc.). It needs to be paired with a hardware-specific library for each display device we carry (to handle the lower-level functions).

Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!

Written by Limor Fried/Ladyada for Adafruit Industries. BSD license, check license.txt for more information. All text above must be included in any redistribution.

Recent Arduino IDE releases include the Library Manager for easy installation. Otherwise, to download, click the DOWNLOAD ZIP button, uncompress and rename the uncompressed folder Adafruit_GFX. Confirm that the Adafruit_GFX folder contains Adafruit_GFX.cpp and Adafruit_GFX.h. Place the Adafruit_GFX library folder your ArduinoSketchFolder/Libraries/ folder. You may need to create the Libraries subfolder if its your first library. Restart the IDE.

You will also need to install the latest Adafruit BusIO library. Search for “Adafruit BusIO” in the library manager, or install by hand from https://github.com/adafruit/Adafruit_BusIO

Useful Resources

  • Image2Code: This is a handy Java GUI utility to convert a BMP file into the array code necessary to display the image with the drawBitmap function. Check out the code at ehubin’s GitHub repository: https://github.com/ehubin/Adafruit-GFX-Library/tree/master/Img2Code

  • drawXBitmap function: You can use the GIMP photo editor to save a .xbm file and use the array saved in the file to draw a bitmap with the drawXBitmap function. See the pull request here for more details: https://github.com/adafruit/Adafruit-GFX-Library/pull/31

  • ‘Fonts’ folder contains bitmap fonts for use with recent (1.1 and later) Adafruit_GFX. To use a font in your Arduino sketch, #include the corresponding .h file and pass address of GFXfont struct to setFont(). Pass NULL to revert to ‘classic’ fixed-space bitmap font.

  • ‘fontconvert’ folder contains a command-line tool for converting TTF fonts to Adafruit_GFX header format.

  • You can also use this GFX Font Customiser tool (*web version here*) to customize or correct the output from fontconvert, and create fonts with only a subset of characters to optimize size.


Roadmap

The PRIME DIRECTIVE is to maintain backward compatibility with existing Arduino sketches – many are hosted elsewhere and don’t track changes here, some are in print and can never be changed! This “little” library has grown organically over time and sometimes we paint ourselves into a design corner and just have to live with it or add progressively more ungainly workarounds.

We are grateful for everyone’s contributions, but pull requests for the following will NOT be merged:

  • Additional or incompatible font formats (see Prime Directive above). There are already two formats and the code is quite bloaty there as it is. This also creates liabilities for tools and documentation. What’s there isn’t perfect but it does the job.

  • Additional or incompatible bitmap formats, for similar reasons. It’s getting messy.

  • Adding background color to custom fonts to erase prior screen contents. The ONLY acceptable methods are to clear the area with a filled rect, or (to avoid flicker) draw text into a GFXcanvas1 and copy to screen with drawBitmap() w/background color. This is on purpose and by design. We’ve discussed this. Glyphs can overlap.

  • Scrolling, whether hardware- or software-based. Such implementations tend to rely on hardware-specific features (not universally available), read access to the screen’s framebuffer (ditto) and/or the addition of virtual functions in GFX which them must be added in every subclass, of which there are many. The GFX API is largely “set” at this point and this is just a limitation we live with now.

  • Please don’t reformat code for the sake of reformatting code. The resulting large “visual diff” makes it impossible to untangle actual bug fixes from merely rearranged lines. clang-format will be the final arbiter.

  • Please no more pentagram-drawing PRs. Any oddly-specific drawing functions can go in your own code and aren’t helpful in a library context.

If you must have one of these features, consider creating a fork with the features required for your project…it’s easy to keep synced with the upstream code.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Adafruit ILI9341 Arduino Library

Build Status

This is a library for the Adafruit ILI9341 display products

This library works with the Adafruit 2.8” Touch Shield V2 (SPI)

Adafruit 2.4” TFT LCD with Touchscreen Breakout w/MicroSD Socket - ILI9341

2.8” TFT LCD with Touchscreen Breakout Board w/MicroSD Socket - ILI9341

2.2” 18-bit color TFT LCD display with microSD card breakout - ILI9340

TFT FeatherWing - 2.4” 320x240 Touchscreen For All Feathers

Check out the links above for our tutorials and wiring diagrams. These displays use SPI to communicate, 4 or 5 pins are required to interface (RST is optional).

BMP image-loading examples have been moved to the Adafruit_ImageReader library: https://github.com/adafruit/Adafruit_ImageReader

Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!

Written by Limor Fried/Ladyada for Adafruit Industries. MIT license, all text above must be included in any redistribution

To download. click the DOWNLOADS button in the top right corner, rename the uncompressed folder Adafruit_ILI9341. Check that the Adafruit_ILI9341 folder contains Adafruit_ILI9341.cpp and Adafruit_ILI9341.

Place the Adafruit_ILI9341 library folder your arduinosketchfolder/libraries/ folder. You may need to create the libraries subfolder if its your first library. Restart the IDE

Also requires the Adafruit_GFX library for Arduino.

References
Used by
Environment Variables
  • TFT_CS_PIN

  • TFT_DC_PIN

  • TFT_RESET_PIN

SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Adafruit NeoPixel Library

Build Status

Arduino library for controlling single-wire-based LED pixels and strip such as the Adafruit 60 LED/meter Digital LED strip, the Adafruit FLORA RGB Smart Pixel, the Adafruit Breadboard-friendly RGB Smart Pixel, the Adafruit NeoPixel Stick, and the Adafruit NeoPixel Shield.

After downloading, rename folder to ‘Adafruit_NeoPixel’ and install in Arduino Libraries folder. Restart Arduino IDE, then open File->Sketchbook->Library->Adafruit_NeoPixel->strandtest sketch.

Compatibility notes: Port A is not supported on any AVR processors at this time


Features
  • ### Simple to use

    Controlling NeoPixels “from scratch” is quite a challenge, so we provide a library letting you focus on the fun and interesting bits.

  • ### Give back

    The library is free; you don’t have to pay for anything. Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!

  • ### Supported Chipsets

    We have included code for the following chips - sometimes these break for exciting reasons that we can’t control in which case please open an issue!

    • AVR ATmega and ATtiny (any 8-bit) - 8 MHz, 12 MHz and 16 MHz

    • Teensy 3.x and LC

    • Arduino Due

    • Arduino 101

    • ATSAMD21 (Arduino Zero/M0 and other SAMD21 boards) @ 48 MHz

    • ATSAMD51 @ 120 MHz

    • Adafruit STM32 Feather @ 120 MHz

    • ESP8266 any speed

    • ESP32 any speed

    • Nordic nRF52 (Adafruit Feather nRF52), nRF51 (micro:bit)

    • Infineon XMC1100 BootKit @ 32 MHz

    • Infineon XMC1100 2Go @ 32 MHz

    • Infineon XMC1300 BootKit @ 32 MHz

    • Infineon XMC4700 RelaxKit, XMC4800 RelaxKit, XMC4800 IoT Amazon FreeRTOS Kit @ 144 MHz

    Check forks for other architectures not listed here!

  • ### GNU Lesser General Public License

    Adafruit_NeoPixel is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

Functions
  • begin()

  • updateLength()

  • updateType()

  • show()

  • delay_ns()

  • setPin()

  • setPixelColor()

  • fill()

  • ColorHSV()

  • getPixelColor()

  • setBrightness()

  • getBrightness()

  • clear()

  • gamma32()

Examples

There are many examples implemented in this library. One of the examples is below. You can find other examples here

Simple
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif
#define PIN        6
#define NUMPIXELS 16

Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
#define DELAYVAL 500

void setup() {
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
  clock_prescale_set(clock_div_1);
#endif

  pixels.begin();
}

void loop() {
  pixels.clear();

  for(int i=0; i<NUMPIXELS; i++) {

    pixels.setPixelColor(i, pixels.Color(0, 150, 0));
    pixels.show();
    delay(DELAYVAL);
  }
}
Contributing

If you want to contribute to this project:

  • Report bugs and errors

  • Ask for enhancements

  • Create issues and pull requests

  • Tell others about this library

  • Contribute new protocols

Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.

Roadmap

The PRIME DIRECTIVE is to maintain backward compatibility with existing Arduino sketches – many are hosted elsewhere and don’t track changes here, some are in print and can never be changed!

Please don’t reformat code for the sake of reformatting code. The resulting large “visual diff” makes it impossible to untangle actual bug fixes from merely rearranged lines. (Exception for first item in wishlist below.)

Things I’d Like To Do But There’s No Official Timeline So Please Don’t Count On Any Of This Ever Being Canonical:

  • For the show() function (with all the delicate pixel timing stuff), break out each architecture into separate source files rather than the current unmaintainable tangle of #ifdef statements!

  • Please don’t use updateLength() or updateType() in new code. They should not have been implemented this way (use the C++ ‘new’ operator with the regular constructor instead) and are only sticking around because of the Prime Directive. setPin() is OK for now though, it’s a trick we can use to ‘recycle’ pixel memory across multiple strips.

  • In the M0 and M4 code, use the hardware systick counter for bit timing rather than hand-tweaked NOPs (a temporary kludge at the time because I wasn’t reading systick correctly). (As of 1.4.2, systick is used on M4 devices and it appears to be overclock-compatible. Not for M0 yet, which is why this item is still here.)

  • As currently written, brightness scaling is still a “destructive” operation – pixel values are altered in RAM and the original value as set can’t be accurately read back, only approximated, which has been confusing and frustrating to users. It was done this way at the time because NeoPixel timing is strict, AVR microcontrollers (all we had at the time) are limited, and assembly language is hard. All the 32-bit architectures should have no problem handling nondestructive brightness scaling – calculating each byte immediately before it’s sent out the wire, maintaining the original set value in RAM – the work just hasn’t been done. There’s a fair chance even the AVR code could manage it with some intense focus. (The DotStar library achieves nondestructive brightness scaling because it doesn’t have to manage data timing so carefully…every architecture, even ATtiny, just takes whatever cycles it needs for the multiply/shift operations.)

Credits

This library is written by Phil “Paint Your Dragon” Burgess for Adafruit Industries, with contributions by PJRC, Michael Miller and other members of the open source community.

License

Adafruit_NeoPixel is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Adafruit_NeoPixel is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with NeoPixel. If not, see this

References
Used by
SoC support
  • esp32

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • rp2040

Adafruit PCD8544 Nokia 5110 LCD Library

Build Status

This is a library for our Monochrome Nokia 5110 LCD Displays

Pick one up today in the Adafruit shop! ——> https://www.adafruit.com/product/338

These displays use SPI to communicate, 4 or 5 pins are required to
interface.

Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!

Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, check license.txt for more information. All text above must be included in any redistribution.

To install, use the Arduino Library Manager to search for ‘Adafruit VCNL4010’ and install the library.

You will also need the Adafruit GFX Graphics core which does all the circles, text, rectangles, etc. You can get it by searching for ‘Adafruit GFX’ in the Arduino Library Manager or check it out here: https://github.com/adafruit/Adafruit-GFX-Library

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • rp2040

Adafruit_SSD1306

Build Status Documentation

This is a library for our Monochrome OLEDs based on SSD1306 drivers

Pick one up today in the adafruit shop! ——> http://www.adafruit.com/category/63_98

These displays use I2C or SPI to communicate, 2 to 5 pins are required to interface.

Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!

Written by Limor Fried/Ladyada for Adafruit Industries, with contributions from the open source community. Scrolling code contributed by Michael Gregg. Dynamic buffer allocation based on work by Andrew Canaday. BSD license, check license.txt for more information. All text above must be included in any redistribution

Preferred installation method is to use the Arduino IDE Library Manager. To download the source from Github instead, click “Clone or download” above, then “Download ZIP.” After uncompressing, rename the resulting folder Adafruit_SSD1306. Check that the Adafruit_SSD1306 folder contains Adafruit_SSD1306.cpp and Adafruit_SSD1306.h.

You will also have to install the Adafruit GFX library which provides graphics primitves such as lines, circles, text, etc. This also can be found in the Arduino Library Manager, or you can get the source from https://github.com/adafruit/Adafruit-GFX-Library

Changes
Pull Request:

(November 2021)

  • Added define SSD1306_NO_SPLASH to opt-out of including splash images in PROGMEM and drawing to display during begin.

Pull Request:

(September 2019)

  • new #defines for SSD1306_BLACK, SSD1306_WHITE and SSD1306_INVERSE that match existing #define naming scheme and won’t conflict with common color names

  • old #defines for BLACK, WHITE and INVERSE kept for backwards compat (opt-out with #define NO_ADAFRUIT_SSD1306_COLOR_COMPATIBILITY)

Version 1.2 (November 2018) introduces some significant changes:

  • Display dimensions are now specified in the constructor…you no longer need to edit the .h file for different screens (though old sketches can continue to work that way).

  • SPI transactions are used and SPI bitrate can be specified (both require Arduino 1.6 or later).

  • SPI and Wire (I2C) interfaces other than the defaults are supported.

Compatibility

MCU

Tested Works

Doesn’t Work

Not Tested

Notes

Atmega328

X

Atmega32u4

X

Atmega2560

X

ESP8266

X

Change OLED_RESET to different pin if using default I2C pins D4/D5.

ESP32

X

ATSAM3X8E

X

ATSAMD21

X

Intel Curie

X

WICED

X

No hardware SPI - bitbang only

ATtiny85

X

Particle

X

  • ATmega328 : Arduino UNO, Adafruit Pro Trinket, Adafruit Metro 328, Adafruit Metro Mini

  • ATmega32u4 : Arduino Leonardo, Arduino Micro, Arduino Yun, Teensy 2.0, Adafruit Flora, Bluefruit Micro

  • ATmega2560 : Arduino Mega

  • ESP8266 : Adafruit Huzzah

  • ATSAM3X8E : Arduino Due

  • ATSAMD21 : Arduino Zero, M0 Pro, Adafruit Metro Express, Feather M0

  • ATtiny85 : Adafruit Gemma, Arduino Gemma, Adafruit Trinket

  • Particle: Particle Argon

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Adafruit ST7735 Display

This is a library for several Adafruit displays based on ST77* drivers.

Works with the Adafruit 1.8” TFT Breakout w/SD card

—-> http://www.adafruit.com/products/358

The 1.8” TFT shield

—-> https://www.adafruit.com/product/802

The 1.44” TFT breakout

—-> https://www.adafruit.com/product/2088

as well as Adafruit raw 1.8” TFT display

—-> http://www.adafruit.com/products/618

Check out the links above for our tutorials and wiring diagrams. These displays use SPI to communicate, 4 or 5 pins are required to interface (RST is optional).

Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!

Written by Limor Fried/Ladyada for Adafruit Industries. MIT license, all text above must be included in any redistribution.

Recent Arduino IDE releases include the Library Manager for easy installation. Otherwise, to download, click the DOWNLOAD ZIP button, uncompress and rename the uncompressed folder Adafruit_ST7735. Confirm that the Adafruit_ST7735 folder contains Adafruit_ST7735.cpp, Adafruit_ST7735.h and related source files. Place the Adafruit_ST7735 library folder your ArduinoSketchFolder/Libraries/ folder. You may need to create the Libraries subfolder if its your first library. Restart the IDE.

Also requires the Adafruit_GFX library for Arduino.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Adafruit Unified Sensor Driver

Many small embedded systems exist to collect data from sensors, analyse the data, and either take an appropriate action or send that sensor data to another system for processing.

One of the many challenges of embedded systems design is the fact that parts you used today may be out of production tomorrow, or system requirements may change and you may need to choose a different sensor down the road.

Creating new drivers is a relatively easy task, but integrating them into existing systems is both error prone and time consuming since sensors rarely use the exact same units of measurement.

By reducing all data to a single sensors_event_t ‘type’ and settling on specific, standardised SI units for each sensor family the same sensor types return values that are comparable with any other similar sensor. This enables you to switch sensor models with very little impact on the rest of the system, which can help mitigate some of the risks and problems of sensor availability and code reuse.

The unified sensor abstraction layer is also useful for data-logging and data-transmission since you only have one well-known type to log or transmit over the air or wire.

Unified Sensor Drivers

The following drivers are based on the Adafruit Unified Sensor Driver:

Accelerometers

Gyroscope

Light

Magnetometers

Barometric Pressure

Humidity & Temperature

Humidity, Temperature, & Barometric Pressure

Orientation

All in one device

How Does it Work?

Any driver that supports the Adafruit unified sensor abstraction layer will implement the Adafruit_Sensor base class. There are two main typedefs and one enum defined in Adafruit_Sensor.h that are used to ‘abstract’ away the sensor details and values:

Sensor Types (sensors_type_t)

These pre-defined sensor types are used to properly handle the two related typedefs below, and allows us determine what types of units the sensor uses, etc.

/** Sensor types */
typedef enum
{
  SENSOR_TYPE_ACCELEROMETER         = (1),
  SENSOR_TYPE_MAGNETIC_FIELD        = (2),
  SENSOR_TYPE_ORIENTATION           = (3),
  SENSOR_TYPE_GYROSCOPE             = (4),
  SENSOR_TYPE_LIGHT                 = (5),
  SENSOR_TYPE_PRESSURE              = (6),
  SENSOR_TYPE_PROXIMITY             = (8),
  SENSOR_TYPE_GRAVITY               = (9),
  SENSOR_TYPE_LINEAR_ACCELERATION   = (10),
  SENSOR_TYPE_ROTATION_VECTOR       = (11),
  SENSOR_TYPE_RELATIVE_HUMIDITY     = (12),
  SENSOR_TYPE_AMBIENT_TEMPERATURE   = (13),
  SENSOR_TYPE_VOLTAGE               = (15),
  SENSOR_TYPE_CURRENT               = (16),
  SENSOR_TYPE_COLOR                 = (17)
} sensors_type_t;

Sensor Details (sensor_t)

This typedef describes the specific capabilities of this sensor, and allows us to know what sensor we are using beneath the abstraction layer.

/* Sensor details (40 bytes) */
/** struct sensor_s is used to describe basic information about a specific sensor. */
typedef struct
{
    char     name[12];
    int32_t  version;
    int32_t  sensor_id;
    int32_t  type;
    float    max_value;
    float    min_value;
    float    resolution;
    int32_t  min_delay;
} sensor_t;

The individual fields are intended to be used as follows:

  • name: The sensor name or ID, up to a maximum of twelve characters (ex. “MPL115A2”)

  • version: The version of the sensor HW and the driver to allow us to differentiate versions of the board or driver

  • sensor_id: A unique sensor identifier that is used to differentiate this specific sensor instance from any others that are present on the system or in the sensor network

  • type: The sensor type, based on sensors_type_t in sensors.h

  • max_value: The maximum value that this sensor can return (in the appropriate SI unit)

  • min_value: The minimum value that this sensor can return (in the appropriate SI unit)

  • resolution: The smallest difference between two values that this sensor can report (in the appropriate SI unit)

  • min_delay: The minimum delay in microseconds between two sensor events, or ‘0’ if there is no constant sensor rate

Sensor Data/Events (sensors_event_t)

This typedef is used to return sensor data from any sensor supported by the abstraction layer, using standard SI units and scales.

/* Sensor event (36 bytes) */
/** struct sensor_event_s is used to provide a single sensor event in a common format. */
typedef struct
{
    int32_t version;
    int32_t sensor_id;
    int32_t type;
    int32_t reserved0;
    int32_t timestamp;
    union
    {
        float           data[4];
        sensors_vec_t   acceleration;
        sensors_vec_t   magnetic;
        sensors_vec_t   orientation;
        sensors_vec_t   gyro;
        float           temperature;
        float           distance;
        float           light;
        float           pressure;
        float           relative_humidity;
        float           current;
        float           voltage;
        sensors_color_t color;
    };
} sensors_event_t;

It includes the following fields:

  • version: Contain ‘sizeof(sensors_event_t)’ to identify which version of the API we’re using in case this changes in the future

  • sensor_id: A unique sensor identifier that is used to differentiate this specific sensor instance from any others that are present on the system or in the sensor network (must match the sensor_id value in the corresponding sensor_t enum above!)

  • type: the sensor type, based on sensors_type_t in sensors.h

  • timestamp: time in milliseconds when the sensor value was read

  • data[4]: An array of four 32-bit values that allows us to encapsulate any type of sensor data via a simple union (further described below)

Required Functions

In addition to the two standard types and the sensor type enum, all drivers based on Adafruit_Sensor must also implement the following two functions:

bool getEvent(sensors_event_t*);

Calling this function will populate the supplied sensors_event_t reference with the latest available sensor data. You should call this function as often as you want to update your data.

void getSensor(sensor_t*);

Calling this function will provide some basic information about the sensor (the sensor name, driver version, min and max values, etc.

Standardised SI values for sensors_event_t

A key part of the abstraction layer is the standardisation of values on SI units of a particular scale, which is accomplished via the data[4] union in sensors_event_t above. This 16 byte union includes fields for each main sensor type, and uses the following SI units and scales:

  • acceleration: values are in meter per second per second (m/s^2)

  • magnetic: values are in micro-Tesla (uT)

  • orientation: values are in degrees

  • gyro: values are in rad/s

  • temperature: values in degrees centigrade (Celsius)

  • distance: values are in centimeters

  • light: values are in SI lux units

  • pressure: values are in hectopascal (hPa)

  • relative_humidity: values are in percent

  • current: values are in milliamps (mA)

  • voltage: values are in volts (V)

  • color: values are in 0..1.0 RGB channel luminosity and 32-bit RGBA format

The Unified Driver Abstraction Layer in Practice

Using the unified sensor abstraction layer is relatively easy once a compliant driver has been created.

Every compliant sensor can now be read using a single, well-known ‘type’ (sensors_event_t), and there is a standardised way of interrogating a sensor about its specific capabilities (via sensor_t).

An example of reading the TSL2561 light sensor can be seen below:

Adafruit_TSL2561 tsl = Adafruit_TSL2561(TSL2561_ADDR_FLOAT, 12345);
...
/* Get a new sensor event */
sensors_event_t event;
tsl.getEvent(&event);

/* Display the results (light is measured in lux) */
if (event.light)
{
  Serial.print(event.light); Serial.println(" lux");
}
else
{
  /* If event.light = 0 lux the sensor is probably saturated
     and no reliable data could be generated! */
  Serial.println("Sensor overload");
}

Similarly, we can get the basic technical capabilities of this sensor with the following code:

sensor_t sensor;

sensor_t sensor;
tsl.getSensor(&sensor);

/* Display the sensor details */
Serial.println("------------------------------------");
Serial.print  ("Sensor:       "); Serial.println(sensor.name);
Serial.print  ("Driver Ver:   "); Serial.println(sensor.version);
Serial.print  ("Unique ID:    "); Serial.println(sensor.sensor_id);
Serial.print  ("Max Value:    "); Serial.print(sensor.max_value); Serial.println(" lux");
Serial.print  ("Min Value:    "); Serial.print(sensor.min_value); Serial.println(" lux");
Serial.print  ("Resolution:   "); Serial.print(sensor.resolution); Serial.println(" lux");
Serial.println("------------------------------------");
Serial.println("");
References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Adafruit VL53L0X Library

Build Status

This is a library for the Adafruit VL53L0X time-of-flight breakout:

Check out the links above for our tutorials and wiring diagrams. This chip uses I2C to communicate

Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!

Written by Limor Fried/Ladyada for Adafruit Industries. MIT license, all text above must be included in any redistribution

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

AnimatedGIF

Introduction

AnimatedGIF is an optimized library for playing animated GIFs on embedded devices.

Features
  • Supports any MCU with at least 24K of RAM (Cortex-M0+ is the simplest I’ve tested).

  • Optimized for speed; the main limitation will be how fast you can copy the pixels to the display. You can use SPI+DMA to help.

  • GIF image data can come from memory (FLASH/RAM), SDCard or any media you provide.

  • GIF files can be any length, (e.g. hundreds of megabytes)

  • Simple C++ class and callback design allows you to easily add GIF support to any application.

  • The C code doing the heavy lifting is completely portable and has no external dependencies.

  • Does not use dynamic memory (malloc/free/new/delete), so it’s easy to build it for a minimal bare metal system.

Using
  1. Add COMPONENT_DEPENDS += AnimatedGIF to your application componenent.mk file.

  2. Add these lines to your application:

    #include <AnimatedGifTask.h>
    
    namespace
    {
    AnimatedGifTask* task;
    
    // ...
    
    } // namespace
    
    void init()
    {
            // ...
    
            initDisplay();
            tft.setOrientation(Graphics::Orientation::deg270);
    
            auto surface = tft.createSurface();
            assert(surface != nullptr);
            task = new AnimatedGifTask(*surface, gifData);
            task->resume();
    }
    
References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: AnimatedGIF
AnimatedGIF

Copyright (c) 2020 BitBank Software, Inc.
Written by Larry Bank
larry@bitbanksoftware.com

I optimize other people’s code for a living. This library is a good example of the kind of work I do for my commercial clients; it contains many unique and clever optimizations that allows it to perform better than anything else available. I’m happy to contribute optimized libraries to the open source community in addition to working on commercial projects. Whatever platform you’re using, I can make significant improvements to your native code. Please contact me so that I can show you how.

AnimatedGIF

I started working with image and video files around 1989 and soon turned my interest into a Document Imaging product based on my own imaging library. Over the years I added support for more and more formats until I had supported all of the standard ones, including DICOM. I sold my Document Imaging business in 1997, but still found uses for my imaging code in other projects such as retro gaming. I recently went looking to see if there was a good GIF player for Arduino and only found Adafruit’s library. Unfortunately it only runs on their ATSAMD51 boards. I thought it would be possible to use my old code to create a universal GIF player that could run on any MCU/SoC with at least 24K of RAM, so I started modifying my old GIF code for this purpose. The focus of this project is speed and to use as little RAM as possible, so there are some limitations.

Limitations

To save memory, the code limits the maximum image width to 320 pixels. This is just a constant defined in AnimatedGIF.h, so you can set it larger if you like. Animated GIF images have a lot of options to save space when encoding the changes from frame to frame. Many of the tools which generate animated GIFs don’t make use of every option and that’s helpful because the frame disposal options are not supported in the library code. They can be implemented in your drawing callback if you want. The reason they’re not provided with this code is because one of the image disposal options requires you to keep an entire copy of the previous frame and this would prevent it from working on low memory MCUs. If would also require dynamic memory allocation which is another area I avoided with this code.

Designed for Speed

My work is always focused on code optimization, so this project is no different. I’ve profiled this code and optimized it for 32-bit CPUs. I discovered while testing it that seeking on micro SD cards (on Arduino) is very very slow. I had to use a little extra RAM to buffer incoming data to avoid seeking. The code does need to seek, but it’s done very rarely. I also wrote code to buffer and de-chunk some of the incoming LZW data to avoid checking for chunk boundaries in the inner decode loop. There are a number of clever optimizations that should allow this to run faster than any existing GIF solutions on Arduino boards. The speed gained from my decoder will be lost if it takes too long to display the pixels. For this reason, the GIFDRAW callback passes an RGB565 palette in the byte order of your choosing and the example sketches uses functions to write entire lines of pixels to the SPI TFT displays in a single shot. If you implement your own GIFDRAW callback and have to pass 1 pixel at a time to whatever display device you’re using, this will cause a major slowdown in the display of the frames.

A note about performance

The chart above shows the total time to decode an 8-frame sequence included in the test_images folder. The decoding speed of your particular image depends on the complexity (how much compressed data) and if/how transparent pixels are used. Small runs of transparent pixels will interfere with the performance of displaying the image on an SPI LCD. This particular image doesn’t use transparency, so the time is purely for decoding the data.

Features:
  • Supports any MCU with at least 24K of RAM (Cortex-M0+ is the simplest I’ve tested).

  • Optimized for speed; the main limitation will be how fast you can copy the pixels to the display. You can use SPI+DMA to help.

  • GIF image data can come from memory (FLASH/RAM), SDCard or any media you provide.

  • GIF files can be any length, (e.g. hundreds of megabytes)

  • Simple C++ class and callback design allows you to easily add GIF support to any application.

  • The C code doing the heavy lifting is completely portable and has no external dependencies.

  • Does not use dynamic memory (malloc/free/new/delete), so it’s easy to build it for a minimal bare metal system.

Acquiring GIF files to play:

You’ll notice that the images provided in the test_images folder have been turned into C code. Each byte is now in the form 0xAB so that it can be compiled into your program and stored in FLASH memory alongside your other code. You can use a command I wrote called image_to_c (https://github.com/bitbank2/image_to_c) to convert a binary file into this type of text. If you use another tool, make sure to add a const modifier in front of the GIF data array to ensure that it gets written to FLASH and not RAM by your build environment.

The Callback functions:

One of the reasons that this is apparently the first universal GIF library for Arduino is because the lack of available RAM and myriad display options would make it difficult to support all MCUs and displays properly. I decided that to solve this issue, I would isolate the GIF decoding from the display and file I/O with callback functions. This allows the core code to run on any system, but you need to help it a little. At a minimum, your code must provide a function to draw (or store) each scan line of image. If you’re playing a GIF file from memory, this is the only function you need to provide. In the examples folder there are multiple sketches to show how this is done on various display libraries. For reading from SD cards, 4 other functions must be provided: open, close, read, seek. There is an example for implementing these in the examples folder as well.

Note:

If you’re using the ESP32 or ESP8266 and playing GIF images stored in RAM, you’ll need to provide the 4 file callback functions or modify the existing ones because RAM and FLASH are in different adddress spaces (Harvard architecture). The code assumes the source of the GIF data is in FLASH and uses memcpy_P() instead of memcpy() to access it.

The API:

Please consult the Wiki for detailed info about each method exposed by the AnimatedGIF class.


If you find this code useful, please consider sending a donation or becoming a Github sponsor.

paypal

ArduCAM Library

Introduction

This is a opensource library for taking high resolution still images and short video clip on Arduino based platforms using ArduCAM’s camera modules.
The camera breakout boards should work with ArduCAM shield before connecting to the Arduino boards.
ArduCAM mini series camera modules like Mini-2MP, Mini-5MP(Plus) can be connected to Arduino boards directly.
In addition to Arduino, the library can be ported to any hardware platforms as long as they have I2C and SPI interface based on this ArduCAM library.

Now Supported Cameras
  • OV7660 0.3MP

  • OV7670 0.3MP

  • OV7675 0.3MP

  • OV7725 0.3MP

  • MT9V111 0.3MP

  • MT9M112 1.3MP

  • MT9M001 1.3MP

  • MT9D111 2MP

  • OV2640 2MP JPEG

  • MT9T112 3MP

  • OV3640 3MP

  • OV5642 5MP JPEG

  • OV5640 5MP JPEG

Supported MCU Platform

Note: ArduCAM library for ESP8266 is maintained in another repository ESP8266 using a json board manager script.

Libraries Structure

The basic libraries are composed by two sub-libraries one is ArduCAM and the other is UTFT4ArduCAM_SPI. These two libraries should be copied right under the libraries of Arduino directory in order to be recognized by the Arduino IDE.
The ArduCAM library is the core library for ArduCAM shields. It contains supported image sensor drivers and user land API functions which issue capture or image data read commands .There is also an example directory inside the ArduCAM library which illustrates most function of the ArduCAM shields. The existing examples are plug and play without need to write a single line of code.
The UTFT4ArduCAM_SPI library is modified version of UTFT which is written by Henning Karlsen. We ported it to support ArduCAM shield with LCD screen. So the UTFT4ArduCAM_SPI library is only needed when using the ArduCAM-LF model.

How to use

The libraries should be configured before running any examples, or else you will get a compilation error message.

1. Edit memorysaver.h file

Open the memorysaver.h file in the ArduCAM folder and enable the hardware platform and camera module which matches to your hardware by comment or uncomment the macro definition in the file. For example, if you got a ArduCAM-Mini-2MP you should uncomment the line #define OV2640_MINI_2MP and comment all the other lines. And if you got a ArduCAM-Shield-V2 and a OV5642 camera module, you should uncomment the line #define ARDUCAM_SHIELD_V2 and the line #define OV5642_CAM then comment other lines.

2. Choose correct CS pin for your camera

Open one of the examples, wiring SPI and I2C interface especially CS pins to ArduCAM shield according to the examples. Hardware and software should be consistent to run the examples correctly.

3. Upload the examples

In the example folder there are seven sub directories for different ArduCAM models and the host application. The Mini folder is for ArduCAM-Mini-2MP and ArduCAM-Mini-5MP modules.
The Mini_5MP_Plus folder is for ArduCAM-Mini-5MP-Plus (OV5640/OV5642) modules.
The RevC folder is for ArduCAM-Shield-RevC or ArduCAM-Shield-RevC+ shields.
The Shield_V2 folder is for ArduCAM-Shield-V2 shield.
The host_app folder is host capture and display application for all of ArduCAM modules.
The RaspberryPi folder is examples used for Raspberry Pi platform, see more instruction.
The ESP8266 folder is for ArduCAM-ESP8266-UNO board examples for library compatibility. Please try repository ESP8266 using josn board manager script instead.
Selecting correct COM port and Arduino boards then upload the sketches.

Arducam MINI Camera Demo Tutorial for Arduino
IMAGE ALT TEXT
Arducam Camera Shield V2 Demo Tutorial for Arduino
IMAGE ALT TEXT
4. How To Connect Bluetooth Module
  • Using this demo

https://github.com/ArduCAM/Arduino/blob/master/ArduCAM/examples/mini/ArduCAM_Mini_Video_Streaming_Bluetooth/ArduCAM_Mini_Video_Streaming_Bluetooth.ino
Alt text
5. How to download the Host V2 ?
References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

arduinoFFT

Fast Fourier Transform for Arduino

This is a fork from https://code.google.com/p/makefurt/ which has been abandoned since 2011.

This is a C++ library for Arduino for computing FFT. Now it works both on Arduino and C projects.

Tested on Arduino 1.6.11

Installation on Arduino

Use the Arduino Library Manager to install and keep it updated. Just look for arduinoFFT. Only for Arduino 1.5+

Manual installation on Arduino

To install this library, just place this entire folder as a subfolder in your Arduino installation

When installed, this library should look like:

ArduinolibrariesarduinoFTT (this library’s folder) ArduinolibrariesarduinoFTTarduinoFTT.cpp (the library implementation file, uses 32 bits floats vectors) ArduinolibrariesarduinoFTTarduinoFTT.h (the library header file, uses 32 bits floats vectors) ArduinolibrariesarduinoFTTkeywords.txt (the syntax coloring file) ArduinolibrariesarduinoFTTexamples (the examples in the “open” menu) ArduinolibrariesarduinoFTTreadme.md (this file)

Building on Arduino

After this library is installed, you just have to start the Arduino application. You may see a few warning messages as it’s built.

To use this library in a sketch, go to the Sketch | Import Library menu and select arduinoFTT. This will add a corresponding line to the top of your sketch:

#include <arduinoFTT.h>

TODO
  • Ratio table for windowing function.

  • Document windowing functions advantages and disadvantages.

  • Optimize usage and arguments.

  • Add new windowing functions. * Spectrum table?

API
  • arduinoFFT(void);

  • arduinoFFT(double *vReal, double *vImag, uint16_t samples, double samplingFrequency); Constructor

  • ~arduinoFFT(void); Destructor

  • ComplexToMagnitude(double *vReal, double *vImag, uint16_t samples);

  • ComplexToMagnitude();

  • Compute(double *vReal, double *vImag, uint16_t samples, uint8_t dir);

  • Compute(double *vReal, double *vImag, uint16_t samples, uint8_t power, uint8_t dir);

  • Compute(uint8_t dir); Calcuates the Fast Fourier Transform.

  • DCRemoval(double *vData, uint16_t samples);

  • DCRemoval(); Removes the DC component from the sample data.

  • MajorPeak(double *vD, uint16_t samples, double samplingFrequency);

  • MajorPeak();

  • MajorPeakParabola(); Looks for and returns the frequency of the biggest spike in the analyzed signal.

  • Revision(void); Returns the library revision.

  • Windowing(double *vData, uint16_t samples, uint8_t windowType, uint8_t dir);

  • Windowing(uint8_t windowType, uint8_t dir); Performs a windowing function on the values array. The possible windowing options are:

    • FFT_WIN_TYP_RECTANGLE

    • FFT_WIN_TYP_HAMMING

    • FFT_WIN_TYP_HANN

    • FFT_WIN_TYP_TRIANGLE

    • FFT_WIN_TYP_NUTTALL

    • FFT_WIN_TYP_BLACKMAN

    • FFT_WIN_TYP_BLACKMAN_NUTTALL

    • FFT_WIN_TYP_BLACKMAN_HARRIS

    • FFT_WIN_TYP_FLT_TOP

    • FFT_WIN_TYP_WELCH

  • Exponent(uint16_t value); Calculates and returns the base 2 logarithm of the given value.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

ArduinoJson Version 5

Provided to support existing applications. New projects should use ArduinoJson Version 6.

Attention

Issue with JSON keys (applies to version 5 only)

According to the ArduinoJson docs it should take an internal copy of char* strings, but it doesn’t! This can occur using the _F() macro:

root[_F("offset")] = something;

This won’t work. Instead, use the F() macro:

root[F("offset")] = something;
References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: ArduinoJson
ArduinoJson
arduino-library-badge Build Status Build Status Coverage Status Star this project

ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).

Features
  • JSON decoding (comments are supported)

  • JSON encoding (with optional indentation)

  • Elegant API, easy to use

  • Fixed memory allocation (zero malloc)

  • No data duplication (zero copy)

  • Portable (written in C++98, can be used in any C++ project)

  • Self-contained (no external dependency)

  • Small footprint

  • Input and output streams

  • 100% code coverage

  • Header-only library

  • MIT License

  • Comprehensive documentation

Compatibility

ArduinoJson works on the following hardware:

ArduinoJson compiles with zero warning on the following compilers, IDEs, and platforms:

Quickstart
Deserialization

Here is a program that parses a JSON document with ArduinoJson.

char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";

StaticJsonBuffer<200> jsonBuffer;

JsonObject& root = jsonBuffer.parseObject(json);

const char* sensor = root["sensor"];
long time          = root["time"];
double latitude    = root["data"][0];
double longitude   = root["data"][1];

See the tutorial on arduinojson.org

Serialization

Here is a program that generates a JSON document with ArduinoJson:

StaticJsonBuffer<200> jsonBuffer;

JsonObject& root = jsonBuffer.createObject();
root["sensor"] = "gps";
root["time"] = 1351824120;

JsonArray& data = root.createNestedArray("data");
data.add(48.756080);
data.add(2.302038);

root.printTo(Serial);
// This prints:
// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}

See the tutorial on arduinojson.org

Documentation

The documentation is available on arduinojson.org, here are some shortcuts:

  • The Examples show how to use the library in various situations.

  • The API Reference contains the description of each class and function.

  • The FAQ has the answer to virtually every question.

  • The ArduinoJson Assistant writes programs for you!


Do you like this library? Please star this project on GitHub!

What? You don’t like it but you love it? We don’t take donations anymore, but we sell a book, so you can help and learn at the same time!

ArduinoJson Version 6

Current version is 6.12.0.

If you’re upgrading from version 5, some changes will be required to your code. See the Version 6 Migration Guide for details.

Some methods of JsonVariant have been removed, replacements are:

asString() -> as<char*>() or as<const char*>. Note that as<String> produces a serialized version, so you’ll get “null” instead of an empty/invalid result String.

asArray() -> as<JsonArray>()

asObject() -> as<JsonObject>()

There are also some useful helper functions available in the Json namespace. See Libraries/ArduinoJson6/include/ArduinoJson.h.

Sming definitions

JsonObjectStream

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: ArduinoJson
ArduinoJson
arduino-library-badge Build Status Build Status Fuzzing Status Coverage Status GitHub stars

ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).

Features
Quickstart
Deserialization

Here is a program that parses a JSON document with ArduinoJson.

char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";

DynamicJsonDocument doc(1024);
deserializeJson(doc, json);

const char* sensor = doc["sensor"];
long time          = doc["time"];
double latitude    = doc["data"][0];
double longitude   = doc["data"][1];

See the tutorial on arduinojson.org

Serialization

Here is a program that generates a JSON document with ArduinoJson:

DynamicJsonDocument doc(1024);

doc["sensor"] = "gps";
doc["time"]   = 1351824120;
doc["data"][0] = 48.756080;
doc["data"][1] = 2.302038;

serializeJson(doc, Serial);
// This prints:
// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}

See the tutorial on arduinojson.org

Support the project

Do you like this library? Please star this project on GitHub!

What? You don’t like it but you love it?
We don’t take donations anymore, but we sell a book, so you can help and learn at the same time.

Arduino TensorFlow Lite

This library runs TensorFlow machine learning models on microcontrollers, allowing you to build AI/ML applications powered by deep learning and neural networks.

With the included examples, you can recognize speech, detect people using a camera, and recognise “magic wand” gestures using an accelerometer.

The examples work best with the Arduino Nano 33 BLE Sense board, which has a microphone and accelerometer.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

At Client

Introduction

AT commands were initially instructions used to control a modem. AT is the abbreviation of ATtention. Every command line starts with “AT” or “at”. If interested a good background article is Hayes Command Set.

Nowadays also other devices allow communication via AT commands as for example GSM/GPRS modems, GPS trackers, web cameras and more.

This library simplifies the communication with such devices.

Usage
  1. Add COMPONENT_DEPENDS += AtClient to your application componenent.mk file.

  2. Add these lines to your application:

    #include <AtClient.h>
    
    namespace
    {
            AtClient* atClient;
    
            // ...
    
    } // namespace
    
    void init()
    {
            Serial.begin(SERIAL_BAUD_RATE);
            Serial.systemDebugOutput(true);
    
            atClient = new AtClient(Serial);
            atClient->send("ATE0\r");
            // ...
    }
    
References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

BH1750FVI Light Sensor

https://github.com/Genotronex/BH1750FVI_Master.git

Digital Light Sensor BH1750

/*

This is a simple code to test BH1750FVI Light senosr

communicate using I2C Protocol

this library enable 2 slave device address

Main address 0x23

secondary address 0x5C

connect this sensor as following :

VCC >>> 3.3V

SDA >>> A4

SCL >>> A5

addr >> A3 “Optional and use address 0x23 “

Gnd >>>Gnd

please after download this library unzip and rename it to BH1750FVI and put it in the libraries folder in the arduino path , then restart Arduino IDE

Written By : Mohannad Rawashdeh

26/9/2013

for more information : http://www.instructables.com/id/BH1750-Digital-Light-Sensor/

contact me on my email

genotronex@gmail.com

*/

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

ESP32 BLE Gamepad

Introduction

This library allows you to make the ESP32 act as a Bluetooth gamepad and control what it does. The library uses ESP32 NimBLE for faster and lighter communication.

Features

Using this library you can do the following:

  • Button press (128 buttons)

  • Button release (128 buttons)

  • Axes movement (6 axes (16 bit) (x, y, z, rZ, rX, rY) –> (Left Thumb X, Left Thumb Y, Right Thumb X, Right Thumb Y, Left Trigger, Right Trigger))

  • 2 Sliders (16 bit) (Slider 1 and Slider 2)

  • 4 point of view hats (ie. d-pad plus 3 other hat switches)

  • Simulation controls (rudder, throttle, accelerator, brake, steering)

  • Configurable HID descriptor

  • Report optional battery level to host (basically works, but it doesn’t show up in Android’s status bar)

  • Customize Bluetooth device name/manufacturer

  • Uses efficient NimBLE bluetooth library

  • Compatible with Windows

  • Compatible with Android (Android OS maps default buttons / axes / hats slightly differently than Windows)

  • Compatible with Linux (limited testing)

  • Compatible with MacOS X (limited testing)

Using
  1. Add COMPONENT_DEPENDS += BLEGamepad to your application componenent.mk file.

  2. Add these lines to your application:

    #include <BleGamepad.h>
    
    namespace
    {
            BleGamepad bleGamepad;
    
            // ...
    
    } // namespace
    
    void init()
    {
            // ...
    
            bleGamepad.begin();
    }
    
Notes

By default, reports are sent on every button press/release or axis/slider/hat/simulation movement, however this can be disabled, and then you manually call sendReport on the gamepad instance as shown in the IndividualAxes.ino example.

There is also Bluetooth specific information that you can use (optional):

Instead of BleGamepad bleGamepad; you can do BleGamepad bleGamepad("Bluetooth Device Name", "Bluetooth Device Manufacturer", 100);. The third parameter is the initial battery level of your device. Adjusting the battery level later on doesn’t work. By default the battery level will be set to 100%, the device name will be ESP32 BLE Gamepad and the manufacturer will be Espressif.

References
Used by
SoC support
  • esp32

  • esp32c3

  • esp32s3

Submodule: ESP32-BLE-Gamepad
POSSIBLE BREAKING CHANGES - PLEASE READ

A large code rebase (configuration class) along with some extra features (start, select, menu, home, back, volume up, volume down and volume mute buttons) has been committed thanks to @dexterdy

Since version 5 of this library, the axes and simulation controls have configurable min and max values The decision was made to set defaults to 0 for minimum and 32767 for maximum (previously -32767 to 32767) This was due to the fact that non-Windows operating systems and some online web-based game controller testers didn’t play well with negative numbers. Existing sketches should take note, and see the DrivingControllerTest example for how to set back to -32767 if wanted

This version of the library has been tested against NimBLE-Arduino version 1.4.1; the latest released version –> https://github.com/h2zero/NimBLE-Arduino/releases/tag/1.4.1

Please see updated examples

NimBLE

Since version 3 of this library, the more efficient NimBLE library is used instead of the default BLE implementation Please use the library manager to install it, or get it from here: https://github.com/h2zero/NimBLE-Arduino Since version 3, this library also supports a configurable HID desciptor, which allows users to customise how the device presents itself to the OS (number of buttons, hats, axes, sliders, simulation controls etc). See the examples for guidance.

ESP32-BLE-Gamepad
License

Published under the MIT license. Please see license.txt.

It would be great however if any improvements are fed back into this version.

Features
  • [x] Button press (128 buttons)

  • [x] Button release (128 buttons)

  • [x] Axes movement (6 axes (configurable resolution up to 16 bit) (x, y, z, rZ, rX, rY) –> (Left Thumb X, Left Thumb Y, Right Thumb X, Right Thumb Y, Left Trigger, Right Trigger))

  • [x] 2 Sliders (configurable resolution up to 16 bit) (Slider 1 and Slider 2)

  • [x] 4 point of view hats (ie. d-pad plus 3 other hat switches)

  • [x] Simulation controls (rudder, throttle, accelerator, brake, steering)

  • [x] Special buttons (start, select, menu, home, back, volume up, volume down, volume mute) all disabled by default

  • [x] Configurable HID descriptor

  • [x] Configurable VID and PID values

  • [x] Configurable BLE characteristics (name, manufacturer, model number, software revision, serial number, firmware revision, hardware revision)

  • [x] Report optional battery level to host

  • [x] Uses efficient NimBLE bluetooth library

  • [x] Compatible with Windows

  • [x] Compatible with Android (Android OS maps default buttons / axes / hats slightly differently than Windows)

  • [x] Compatible with Linux (limited testing)

  • [x] Compatible with MacOS X (limited testing)

  • [ ] Compatible with iOS (No - not even for accessibility switch - This is not a “Made for iPhone” (MFI) compatible device)

Installation
  • (Make sure you can use the ESP32 with the Arduino IDE. Instructions can be found here.)

  • Download the latest release of this library from the release page.

  • In the Arduino IDE go to “Sketch” -> “Include Library” -> “Add .ZIP Library…” and select the file you just downloaded.

  • In the Arduino IDE go to “Tools” -> “Manage Libraries…” -> Filter for “NimBLE-Arduino” by h2zero and install.

  • You can now go to “File” -> “Examples” -> “ESP32 BLE Gamepad” and select an example to get started.

Example
/*
 * This example turns the ESP32 into a Bluetooth LE gamepad that presses buttons and moves axis
 *
 * At the moment we are using the default settings, but they can be canged using a BleGamepadConfig instance as parameter for the begin function.
 *
 * Possible buttons are:
 * BUTTON_1 through to BUTTON_16
 * (16 buttons by default. Library can be configured to use up to 128)
 *
 * Possible DPAD/HAT switch position values are:
 * DPAD_CENTERED, DPAD_UP, DPAD_UP_RIGHT, DPAD_RIGHT, DPAD_DOWN_RIGHT, DPAD_DOWN, DPAD_DOWN_LEFT, DPAD_LEFT, DPAD_UP_LEFT
 * (or HAT_CENTERED, HAT_UP etc)
 *
 * bleGamepad.setAxes sets all axes at once. There are a few:
 * (x axis, y axis, z axis, rx axis, ry axis, rz axis, slider 1, slider 2)
 *
 * Library can also be configured to support up to 5 simulation controls
 * (rudder, throttle, accelerator, brake, steering), but they are not enabled by default.
 *
 * Library can also be configured to support different function buttons
 * (start, select, menu, home, back, volume increase, volume decrease, volume mute)
 * start and select are enabled by default
 */

#include <Arduino.h>
#include <BleGamepad.h>

BleGamepad bleGamepad;

void setup()
{
    Serial.begin(115200);
    Serial.println("Starting BLE work!");
    bleGamepad.begin();
    // The default bleGamepad.begin() above enables 16 buttons, all axes, one hat, and no simulation controls or special buttons
}

void loop()
{
    if (bleGamepad.isConnected())
    {
        Serial.println("Press buttons 5, 16 and start. Move all enabled axes to max. Set DPAD (hat 1) to down right.");
        bleGamepad.press(BUTTON_5);
        bleGamepad.press(BUTTON_16);
        bleGamepad.pressStart();
        bleGamepad.setAxes(32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767);
        bleGamepad.setHat1(HAT_DOWN_RIGHT);
        // All axes, sliders, hats etc can also be set independently. See the IndividualAxes.ino example
        delay(500);

        Serial.println("Release button 5 and start. Move all axes to min. Set DPAD (hat 1) to centred.");
        bleGamepad.release(BUTTON_5);
        bleGamepad.releaseStart();
        bleGamepad.setHat1(HAT_CENTERED);
        bleGamepad.setAxes(0, 0, 0, 0, 0, 0, 0, 0);
        delay(500);
    }
}

By default, reports are sent on every button press/release or axis/slider/hat/simulation movement, however this can be disabled, and then you manually call sendReport on the gamepad instance as shown in the IndividualAxes.ino example.

VID and PID values can be set. See TestAll.ino for example.

There is also Bluetooth specific information that you can use (optional):

Instead of BleGamepad bleGamepad; you can do BleGamepad bleGamepad("Bluetooth Device Name", "Bluetooth Device Manufacturer", 100);. The third parameter is the initial battery level of your device. By default the battery level will be set to 100%, the device name will be ESP32 BLE Gamepad and the manufacturer will be Espressif.

Battery level can be set during operation by calling, for example, bleGamepad.setBatteryLevel(80); Update sent on next gamepad update if auto reporting is not enabled

Credits

Credits to T-vK as this library is based on his ESP32-BLE-Mouse library (https://github.com/T-vK/ESP32-BLE-Mouse) that he provided.

Credits to chegewara as the ESP32-BLE-Mouse library is based on this piece of code that he provided.

Credits to wakwak-koba for the NimBLE code that he provided.

Notes

This library allows you to make the ESP32 act as a Bluetooth Gamepad and control what it does.
Relies on NimBLE-Arduino

Use this Windows test app to test/see all of the buttons

You might also be interested in:

or the NimBLE versions at

ESP32 BLE Keyboard

Introduction

This library allows you to make the ESP32 act as a Bluetooth keyboard and control what it does. The library uses ESP32 NimBLE for faster and lighter communication.

Features

Using this library you can do the following:

  • Send key strokes

  • Send text

  • Press/release individual keys

  • Media keys are supported

  • Set battery level (basically works, but doesn’t show up in Android’s status bar)

  • Compatible with Android

  • Compatible with Windows

  • Compatible with Linux

  • Compatible with MacOS X (not stable, some people have issues, doesn’t work with old devices)

  • Compatible with iOS (not stable, some people have issues, doesn’t work with old devices)

Using
  1. Add COMPONENT_DEPENDS += BLEKeyboard to your application componenent.mk file.

  2. Add these lines to your application:

    #include <BleKeyboard.h>
    
    
    namespace
    {
            BleKeyboard bleKeyboard;
    
            // ...
    
    } // namespace
    
    void init()
    {
            // ...
    
            bleKeyboard.begin();
    }
    
API documentation

The BleKeyboard interface is almost identical to the Keyboard Interface, so you can use documentation right here: https://www.arduino.cc/reference/en/language/functions/usb/keyboard/

In addition to that you can send media keys (which is not possible with the USB keyboard library). Supported are the following:

  • KEY_MEDIA_NEXT_TRACK

  • KEY_MEDIA_PREVIOUS_TRACK

  • KEY_MEDIA_STOP

  • KEY_MEDIA_PLAY_PAUSE

  • KEY_MEDIA_MUTE

  • KEY_MEDIA_VOLUME_UP

  • KEY_MEDIA_VOLUME_DOWN

  • KEY_MEDIA_WWW_HOME

  • KEY_MEDIA_LOCAL_MACHINE_BROWSER // Opens “My Computer” on Windows

  • KEY_MEDIA_CALCULATOR

  • KEY_MEDIA_WWW_BOOKMARKS

  • KEY_MEDIA_WWW_SEARCH

  • KEY_MEDIA_WWW_STOP

  • KEY_MEDIA_WWW_BACK

  • KEY_MEDIA_CONSUMER_CONTROL_CONFIGURATION // Media Selection

  • KEY_MEDIA_EMAIL_READER

There is also Bluetooth specific information that you can set (optional): Instead of BleKeyboard bleKeyboard; you can do BleKeyboard bleKeyboard("Bluetooth Device Name", "Bluetooth Device Manufacturer", 100);. (Max length is 15 characters, anything beyond that will be truncated.) The third parameter is the initial battery level of your device. To adjust the battery level later on you can simply call e.g. bleKeyboard.setBatteryLevel(50) (set battery level to 50%). By default the battery level will be set to 100%, the device name will be ESP32 Bluetooth Keyboard and the manufacturer will be Espressif. There is also a setDelay method to set a delay between each key event. E.g. bleKeyboard.setDelay(10) (10 milliseconds). The default is 8. This feature is meant to compensate for some applications and devices that can’t handle fast input and will skip letters if too many keys are sent in a small time frame.

References
Used by
SoC support
  • esp32

  • esp32c3

  • esp32s3

Submodule: ESP32-BLE-Keyboard
ESP32 BLE Keyboard library

This library allows you to make the ESP32 act as a Bluetooth Keyboard and control what it does.
You might also be interested in:

Features
  • [x] Send key strokes

  • [x] Send text

  • [x] Press/release individual keys

  • [x] Media keys are supported

  • [ ] Read Numlock/Capslock/Scrolllock state

  • [x] Set battery level (basically works, but doesn’t show up in Android’s status bar)

  • [x] Compatible with Android

  • [x] Compatible with Windows

  • [x] Compatible with Linux

  • [x] Compatible with MacOS X (not stable, some people have issues, doesn’t work with old devices)

  • [x] Compatible with iOS (not stable, some people have issues, doesn’t work with old devices)

Installation
Example
/**
 * This example turns the ESP32 into a Bluetooth LE keyboard that writes the words, presses Enter, presses a media key and then Ctrl+Alt+Delete
 */
#include <BleKeyboard.h>

BleKeyboard bleKeyboard;

void setup() {
  Serial.begin(115200);
  Serial.println("Starting BLE work!");
  bleKeyboard.begin();
}

void loop() {
  if(bleKeyboard.isConnected()) {
    Serial.println("Sending 'Hello world'...");
    bleKeyboard.print("Hello world");

    delay(1000);

    Serial.println("Sending Enter key...");
    bleKeyboard.write(KEY_RETURN);

    delay(1000);

    Serial.println("Sending Play/Pause media key...");
    bleKeyboard.write(KEY_MEDIA_PLAY_PAUSE);

    delay(1000);

   //
   // Below is an example of pressing multiple keyboard modifiers
   // which by default is commented out.
   //
   /* Serial.println("Sending Ctrl+Alt+Delete...");
    bleKeyboard.press(KEY_LEFT_CTRL);
    bleKeyboard.press(KEY_LEFT_ALT);
    bleKeyboard.press(KEY_DELETE);
    delay(100);
    bleKeyboard.releaseAll();
    */

  }
  Serial.println("Waiting 5 seconds...");
  delay(5000);
}
API docs

The BleKeyboard interface is almost identical to the Keyboard Interface, so you can use documentation right here: https://www.arduino.cc/reference/en/language/functions/usb/keyboard/

Just remember that you have to use bleKeyboard instead of just Keyboard and you need these two lines at the top of your script:

#include <BleKeyboard.h>
BleKeyboard bleKeyboard;

In addition to that you can send media keys (which is not possible with the USB keyboard library). Supported are the following:

  • KEY_MEDIA_NEXT_TRACK

  • KEY_MEDIA_PREVIOUS_TRACK

  • KEY_MEDIA_STOP

  • KEY_MEDIA_PLAY_PAUSE

  • KEY_MEDIA_MUTE

  • KEY_MEDIA_VOLUME_UP

  • KEY_MEDIA_VOLUME_DOWN

  • KEY_MEDIA_WWW_HOME

  • KEY_MEDIA_LOCAL_MACHINE_BROWSER // Opens “My Computer” on Windows

  • KEY_MEDIA_CALCULATOR

  • KEY_MEDIA_WWW_BOOKMARKS

  • KEY_MEDIA_WWW_SEARCH

  • KEY_MEDIA_WWW_STOP

  • KEY_MEDIA_WWW_BACK

  • KEY_MEDIA_CONSUMER_CONTROL_CONFIGURATION // Media Selection

  • KEY_MEDIA_EMAIL_READER

There is also Bluetooth specific information that you can set (optional): Instead of BleKeyboard bleKeyboard; you can do BleKeyboard bleKeyboard("Bluetooth Device Name", "Bluetooth Device Manufacturer", 100);. (Max lenght is 15 characters, anything beyond that will be truncated.)
The third parameter is the initial battery level of your device. To adjust the battery level later on you can simply call e.g. bleKeyboard.setBatteryLevel(50) (set battery level to 50%).
By default the battery level will be set to 100%, the device name will be ESP32 Bluetooth Keyboard and the manufacturer will be Espressif.
There is also a setDelay method to set a delay between each key event. E.g. bleKeyboard.setDelay(10) (10 milliseconds). The default is 8.
This feature is meant to compensate for some applications and devices that can’t handle fast input and will skip letters if too many keys are sent in a small time frame.

NimBLE-Mode

The NimBLE mode enables a significant saving of RAM and FLASH memory.

Comparison (SendKeyStrokes.ino at compile-time)

Standard

RAM:   [=         ]   9.3% (used 30548 bytes from 327680 bytes)
Flash: [========  ]  75.8% (used 994120 bytes from 1310720 bytes)

NimBLE mode

RAM:   [=         ]   8.3% (used 27180 bytes from 327680 bytes)
Flash: [====      ]  44.2% (used 579158 bytes from 1310720 bytes)
Comparison (SendKeyStrokes.ino at run-time)

Standard

NimBLE mode

difference

ESP.getHeapSize()

296.804

321.252

+ 24.448

ESP.getFreeHeap()

143.572

260.764

+ 117.192

ESP.getSketchSize()

994.224

579.264

- 414.960

How to activate NimBLE mode?
ArduinoIDE:

Uncomment the first line in BleKeyboard.h

#define USE_NIMBLE
PlatformIO:

Change your platformio.ini to the following settings

lib_deps =
  NimBLE-Arduino

build_flags =
  -D USE_NIMBLE
Credits

Credits to chegewara and the authors of the USB keyboard library as this project is heavily based on their work!
Also, credits to duke2421 who helped a lot with testing, debugging and fixing the device descriptor! And credits to sivar2311 for adding NimBLE support, greatly reducing the memory footprint, fixing advertising issues and for adding the setDelay method.

BME280 Barometric Pressure Sensor

BME280 is Barometric Pressure Sensor.

Datasheet for BME280:

http://www.bosch-sensortec.com/content/language1/downloads/BST-BMP180-DS000-07.pdf

Copyright (C) 2012 Love Electronics Ltd (loveelectronics.com).

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

BMP180 Pressure/Temperature Sensor

Arduino library for BMP180 Bosch pressure/temperature sensor

Copyright (C) 2012 Love Electronics Ltd (loveelectronics.com)

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Bounce library for Arduino

Version 1.6

by Thomas Ouellet Fredericks with contributions from: Eric Lowry Jim Schimpf Tom Harkaway

contact: mrtoftrash@gmail.com

See the online documentation here: http://www.arduino.cc/playground/Code/Bounce

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

CS5460 energy meter IC

Product information: https://www.cirrus.com/products/cs5460a/

The CS5460(A) is a highly integrated power measurement solution which combines:

  • Two Analog-to-digital Converters (ADCs)

  • High-speed power calculation functions

  • A serial interface

on a single chip. It is designed to accurately measure and calculate

  • Real (True) Energy

  • Instantaneous Power

  • I RMS

  • V RMS

for single phase 2- or 3-wire power metering applications.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: CS5460
CS5460

Arduino library for CS5460

Using SPI interface to connect, tested on Arduino UNO.

Current functions include set config register arguments manually, read measurements in raw binary format and float number and calibrate gain and offset.

See detail information of CS5460: datasheet

Todo: Make full use of EOUT, EDIR and INT ports.

Capacitive Sensor Library

CapacitiveSensor lets you create sensors that can detect touch or proximity.

http://www.pjrc.com/teensy/td_libs_CapacitiveSensor.html

http://playground.arduino.cc/Main/CapacitiveSensor

http://www.youtube.com/watch?v=BHQPqQ_5ulc

CapacitiveSensor was originally written by Paul Badger and is now maintained by Paul Stoffregen.

CapacitiveSensor Demo
References
Used by
SoC support
  • esp8266

Command Processing

Introduction

Command handler provides a common command line interface (CLI). Command line must be text. Commands should be separated with a single character. CLI can be used with:

  • Serial

  • Network (Websockets, Telnet)

and all communication protocols that are exchanging text data.

Commands can be added to and removed from the command handler. Each command will trigger a defined Delegate.

A welcome message may be shown when a user connects and end-of-line (EOL) character may be defined. An automatic “help” display is available.

For more examples take a look at the CommandLine, TelnetServer and HttpServer Websockets samples.

Using
  1. Add these lines to your application componenent.mk file:

    COMPONENT_DEPENDS += CommandProcessing
    
  2. Add these lines to your application:

    #include <CommandProcessing/Utils.h>
    
  3. Basic example:

    #include <CommandProcessing/Utils.h>
    
    CommandProcessing::Handler commandHandler;
    
    void processShutdownCommand(String commandLine, ReadWriteStream& commandOutput)
    {
      // ...
    }
    
    void init()
    {
      commandHandler.registerSystemCommands();
      commandHandler.registerCommand({CMDP_STRINGS("shutdown", "Shutdown Server Command", "Application"), processShutdownCommand});
    }
    
CMDPROC_FLASHSTRINGS

default: undefined (RAM strings) 1: Store strings in flash memory

Command name, help and group strings default to RAM. This maintains backward compatibility with existing applications which may define commands like this:

commandHandler.registerCommand({"shutdown", "Shutdown Server Command", "Application", processShutdownCommand});

To save RAM, update your code to use the CMDP_STRINGS macro and build with CMDPROC_FLASHSTRINGS=1.

API Documentation
CMDP_STRINGS(name, help, group)
References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

DFPlayer - A Mini MP3 Player For Arduino

DFPlayer - A Mini MP3 Player For Arduino https://www.dfrobot.com/index.php?route=product/product&product_id=1121

This example shows the all the function of library for DFPlayer.

Created 2016-12-07 By Angelo qiao

GNU Lesser General Public License. See http://www.gnu.org/licenses/ for details. All above must be included in any redistribution

Notice and Trouble shooting

1.Connection and Diagram can be found here https://www.dfrobot.com/wiki/index.php/DFPlayer_Mini_SKU:DFR0299#Connection_Diagram 2.This code is tested on Arduino Uno, Leonardo, Mega boards.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

DHT ESP Temperature/Humidity Sensors

An Arduino library for reading the DHT family of temperature and humidity sensors.
Forked from arduino-DHT
Original written by Mark Ruys, mark@paracas.nl.

Why did I clone this library instead of forking the original repo and push the changes? When I searched through Github for DHT libraries, I found a lot of them, some of them offers additional functions, some of them only basic temperature and humidity values. I wanted to combine all interesting functions into one library. In addition, none of the DHT libraries I found were written to work without errors on the ESP32. For ESP32 (a multi core/ multi processing SOC) task switching must be disabled while reading data from the sensor.
Another problem I found is that many of the available libraries use the same naming (dht.h, dht.cpp), which easily leads to conflicts if different libraries are used for different platforms.

*According to users, the library works as well with DHT33 and DHT44 sensors. But as I do not own these sensors, I cannot test and confirm it. However, if you want to use this sensors, you can do so by using ``setup(pin, DHTesp::DHT22)`` and it should work. Please give me feedback in the issues if you successfull use these sensors. Thank you_

The library is tested as well on ESP8266 and should work on AVR boards as well.

Changes to the original library:
  • 2017-12-12: Renamed DHT class to DHTesp and filenames from dht.* to DHTesp.* to avoid conflicts with other libraries - beegee-tokyo, beegee@giesecke.tk.

  • 2017-12-12: Updated to work with ESP32 - beegee-tokyo, beegee@giesecke.tk.

  • 2017-12-12: Added function computeHeatIndex. Reference: Adafruit DHT library.

  • 2017-12-14: Added function computeDewPoint. Reference: idDHTLib.

  • 2017-12-14: Added function getComfortRatio. Reference: libDHT. (References about Human Comfort invalid)

  • 2017-12-15: Added function computePerception. Reference: WikiPedia Dew point==> Relationship to human comfort - beegee-tokyo, beegee@giesecke.tk.

  • 2018-01-02: Added example for multiple sensors usage.

  • 2018-01-03: Added function getTempAndHumidity which returns temperature and humidity in one call.

  • 2018-01-03: Added retry in case the reading from the sensor fails with a timeout.

  • 2018-01-08: Added ESP8266 (and probably AVR) compatibility.

  • 2018-03-11: Updated DHT example

  • 2018-06-19: Updated DHT example to distinguish between ESP8266 examples and ESP32 examples

  • 2018-07-06: Fixed bug in ESP32 example

  • 2018-07-17: Use correct field separator in keywords.txt

  • 2019-03-07: Added computeAbsoluteHumidity which returns the absolute humidity in g/m³. Reference: How to convert relative humidity to absolute humidity kudos to Wurstnase

  • 2019-03-22: Fixed auto detection problem

  • 2019-07-31: Make getPin() public, Updated ESP8266 example

  • 2019-10-01: Using noInterrupts() & interrupts() instead of cli and sei

  • 2019-10-05: Reduce CPU usage and add decimal part for DHT11 (thanks to Swiftyhu)

  • 2019-10-06: Back to working version by removing the last commit

Features
Functions

**``void setup(uint8_t pin, DHT_MODEL_t model=AUTO_DETECT);``*_

  • Call to initialize the interface, define the GPIO pin to which the sensor is connected and define the sensor type. Valid sensor types are:

    • AUTO_DETECT Try to detect which sensor is connected (default if 2nd parameter is not used)

    • DHT11

    • DHT22

    • AM2302 Packaged DHT22

    • RHT03 Equivalent to DHT22
      **``void resetTimer();``*_

  • Reset last time the sensor was read

**``float getTemperature();``*_

  • Get the temperature in degree Centigrade from the sensor
    Either one of *getTemperature()* or *getHumidity()* or *getTempAndHumidity()* initiates reading a value from the sensor if the last reading was older than the minimal refresh time of the sensor.
    See example _`DHT_ESP32.ino`_ or _`DHT_Test.ino`_

**``float getHumidity();``*_

  • Get the humidity from the sensor
    Either one of *getTemperature()* or *getHumidity()* or *getTempAndHumidity()* initiates reading a value from the sensor if the last reading was older than the minimal refresh time of the sensor.
    See example _`DHT_ESP32.ino`_ or _`DHT_Test.ino`_

**``TempAndHumidity getTempAndHumidity();``*_

  • Get the temperature and humidity from the sensor
    Either one of *getTemperature()* or *getHumidity()* or *getTempAndHumidity()* initiates reading a value from the sensor if the last reading was older than the minimal refresh time of the sensor.
    Return value is a struct of type *TempAndHumidity* with temperature and humidity as float values. See example _`DHT_Multi.ino`_

**``DHT_ERROR_t getStatus();``*_

  • Get last error if reading from the sensor failed. Possible values are:

    • ERROR_NONE no error occured

    • ERROR_TIMEOUT timeout reading from the sensor

    • ERROR_CHECKSUM checksum of received values doesn’t match

*`const char* getStatusString();`_

  • Get last error as a char *

**``DHT_MODEL_t getModel()``*_

  • Get detected (or defined) sensor type

**``int getMinimumSamplingPeriod();``*_

  • Get minimmum possible sampling period. For DHT11 this is 1000ms, for other sensors it is 2000ms

**``int8_t getNumberOfDecimalsTemperature();``*_

  • Get number of decimals in the temperature value. For DHT11 this is 0, for other sensors it is 1

**``int8_t getLowerBoundTemperature();``*_

  • Get lower temperature range of the sensor. For DHT11 this is 0 degree Centigrade, for other sensors this is -40 degree Centrigrade

**``int8_t getUpperBoundTemperature();``*_

  • Get upper temperature range of the sensor. For DHT11 this is 50 degree Centigrade, for other sensors this is 125 degree Centrigrade

**``int8_t getNumberOfDecimalsHumidity();``*_

  • Get number of decimals in the humidity value. This is always 0.

**``int8_t getLowerBoundHumidity();``*_

  • Get lower humidity range of the sensor. For DHT11 this is 20 percent, for other sensors this is 0 percent

**``int8_t getUpperBoundHumidity();``*_

  • Get upper temperature range of the sensor. For DHT11 this is 90 percent, for other sensors this is 100 percent

**``static float toFahrenheit(float fromCelcius);``*_

  • Convert Centrigrade value to Fahrenheit value

**``static float toCelsius(float fromFahrenheit) { return (fromFahrenheit - 32.0) / 1.8; };``*_

  • Convert Fahrenheit value to Centigrade value

**``float computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit=false);``*_

  • Compute the heat index. Default temperature is in Centrigrade.

**``float computeDewPoint(float temperature, float percentHumidity, bool isFahrenheit=false);``*_

  • Compute the dew point. Default temperature is in Centrigrade.

**``float computeAbsoluteHumidity(float temperature, float percentHumidity, bool isFahrenheit=false);``*_

  • Compute the absolute humidity in g/m³. Default temperature is in Centrigrade.

**``float getComfortRatio(ComfortState& destComfStatus, float temperature, float percentHumidity, bool isFahrenheit=false);``*_

  • Compute the comfort ratio. Default temperature is in Centrigrade. Return values:
    0 -> OK
    1 -> Too Hot
    2 -> Too cold
    4 -> Too dry
    8 -> Too humid
    9 -> Hot and humid
    5 -> Hot and dry
    10 -> Cold and humid
    6 -> Cold and dry

**``byte computePerception(float temperature, float percentHumidity, bool isFahrenheit=false);``*_

  • Compute the human perception. Default temperature is in Centrigrade. Return values:
    0 -> Dry
    1 -> Very comfortable
    2 -> Comfortable
    3 -> Ok
    4 -> Uncomfortable
    5 -> Quite uncomfortable
    6 -> Very uncomfortable
    7 -> Severe uncomfortable

**``uint8_t getPin(void);``*_

  • Returns the assigned GPIO for this instance. Usefull when connecting multiple sensors

Usage

See examples. For all the options, see dhtesp.h.

Installation

In Arduino IDE open Sketch->Include Library->Manage Libraries then search for *DHT ESP:raw-html-m2r:`<br>` In PlatformIO open PlatformIO Home, switch to libraries and search for ***DHT ESP32*. Or install the library in the terminal with **platformio lib install 2029**_

For manual installation download the archive, unzip it and place the DHTesp folder into the library directory.
In Arduino IDE this is usually **``<arduinosketchfolder>/libraries/``*:raw-html-m2r:`<br>` In PlatformIO this is usually **<user/.platformio/lib>**_

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

DIscovery And Launch (DIAL)

Introduction

DIAL—for DIscovery And Launch—is a simple protocol that second-screen devices can use to discover and launch apps on first-screen devices. For example, your can stream a video from your embedded device on your connected TV.

Using
  1. Add COMPONENT_DEPENDS += DIAL to your application componenent.mk file.

  2. Add these lines to your application:

    #include <Dial/Client.h>
    
    static UPnP::ControlPoint controlPoint;
    static Dial::Client* myClient;
    
    // Call when IP address has been obtained
    void onIp(IpAddress ip, IpAddress mask, IpAddress gateway)
    {
       // ...
    
       /* Use UPnP to auto-discover all DIAL-enabled servers */
       Dial::discover(controlPoint, [](Dial::Client& client) {
          // Are we looking for a specific device? Can match on friendlyName, UDN, etc.
          if(client.friendlyName() == F("FriendlyNameToFind")) {
             // Take a reference to the device
             myClient = &client;
    
             // Get an app and do something...
             auto& app = myClient->getApp("YouTube");
    
             // Keep this device
             return true;
          }
    
          // Don't want this device, destroy it
          return false;
       });
    
       // ...
    }
    

See the Control your DIAL-enabled smart monitor/TV using Sming sample application.

API Documentation
namespace Dial

Functions

bool discover(UPnP::ControlPoint &controlPoint, Client::Discovered callback)
class App
#include <App.h>
class Client : public UPnP::DeviceControl
#include <Client.h>

Public Functions

App &getApp(const String &applicationId)

Get application object by name.

Parameters:

applicationId – the unique application. A list of registered ids can be found here: http://www.dial-multiscreen.org/dial-registry/namespace-database#TOC-Registered-Names

Return values:

App& – Application object reference

virtual void onConnected(HttpConnection &connection) override

Inherited classes may override this to pull out any additional information from received response headers, etc. Invoked after description has been processed.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

DS18S20 Temperature Sensor

This library provides access to DS18S20 temperature sensors connected via 1-Wire bus to a single GPIO The DS18S20 can run in several modes, with varying degrees of resolution. The highest resolution is 12-bit which provides 0.0625C resolution. 12-bit measurement takes 750ms. With 4 sensors connected, measurement will take 3s.

Created on: 01-09-2015 Author: flexiti and Anakod

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Arduino DS3232RTC Library v1.0

https://github.com/JChristensen/DS3232RTC ReadMe file
Jack Christensen Mar 2013

CC BY-SA
Introduction

DS3232RTC is an Arduino library that supports the Maxim Integrated DS3232 and DS3231 Real-Time Clocks. This library is intended to be used with the Arduino Time library.

The DS3232RTC library is a drop-in replacement for the DS1307RTC.h library by Michael Margolis that is supplied with the Arduino Time library above. To change from using a DS1307 RTC to an DS323x RTC, it is only necessary to use #include <DS3232RTC.h> instead of #include <DS1307RTC.h>.

This library is **not** a drop-in replacement for PJRC’s newer version of the DS1307RTC library.

DS3232RTC also implements functions to support the additional features of the DS3232 and DS3231. The DS3231 has the same features as the DS3232 except: (1) Battery-backed SRAM, (2) Battery-backed 32kHz output (BB32kHz bit in Control/Status register 0x0F), and (3) Adjustable temperature sensor sample rate (CRATE1:0 bits in the Control/Status register).

“Arduino DS3232RTC Library” by Jack Christensen is licensed under CC BY-SA 4.0.

Installation

To use the DS3232RTC library:

  • Go to https://github.com/JChristensen/DS3232RTC, click the Download ZIP button and save the ZIP file to a convenient location on your PC.

  • Uncompress the downloaded file. This will result in a folder containing all the files for the library, that has a name that includes the branch name, usually DS3232RTC-master.

  • Rename the folder to just DS3232RTC.

  • Copy the renamed folder to the Arduino sketchbooklibraries folder.

Examples

The following example sketches are included with the DS3232RTC library:

  • SetSerial: Set the RTC’s date and time from the Arduino serial monitor. Displays date, time and temperature.

  • TimeRTC: Same as the example of the same name provided with the Time library, demonstrating the interchangeability of the DS3232RTC library with the DS1307RTC library.

  • tiny3232_KnockBang: Demonstrates interfacing an ATtiny45/85 to a DS3231 or DS3232 RTC.

Usage notes

When using the DS3232RTC library, the user is responsible for ensuring that reads and writes do not exceed the device’s address space (0x00-0x12 for DS3231, 0x00-0xFF for DS3232); no bounds checking is done by the library.

Similar to the DS1307RTC library, the DS3232RTC library instantiates an RTC object; the user does not need to do this.

To use the DS3232RTC library, the Time and Wire libraries must also be included. For brevity, these includes are not repeated in the examples below:

#include <DS3232RTC.h>    //http://github.com/JChristensen/DS3232RTC
#include <Time.h>         //http://www.arduino.cc/playground/Code/Time
#include <Wire.h>         //http://arduino.cc/en/Reference/Wire (included with Arduino IDE)
Enumerations
SQWAVE_FREQS_t
Description

Symbolic names used with the squareWave() method (described below).

Values
  • SQWAVE_NONE

  • SQWAVE_1_HZ

  • SQWAVE_1024_HZ

  • SQWAVE_4096_HZ

  • SQWAVE_8192_HZ

ALARM_TYPES_t
Description

Symbolic names used with the setAlarm() method (described below).

Values for Alarm 1
  • ALM1_EVERY_SECOND – causes an alarm once per second.

  • ALM1_MATCH_SECONDS – causes an alarm when the seconds match (i.e. once per minute).

  • ALM1_MATCH_MINUTES – causes an alarm when the minutes and seconds match.

  • ALM1_MATCH_HOURS – causes an alarm when the hours and minutes and seconds match.

  • ALM1_MATCH_DATE – causes an alarm when the date of the month and hours and minutes and seconds match.

  • ALM1_MATCH_DAY – causes an alarm when the day of the week and hours and minutes and seconds match.

Values for Alarm 2
  • ALM2_EVERY_MINUTE – causes an alarm once per minute.

  • ALM2_MATCH_MINUTES – causes an alarm when the minutes match (i.e. once per hour).

  • ALM2_MATCH_HOURS – causes an alarm when the hours and minutes match.

  • ALM2_MATCH_DATE – causes an alarm when the date of the month and hours and minutes match.

  • ALM2_MATCH_DAY – causes an alarm when the day of the week and hours and minutes match.

Methods for setting and reading the time
get(void)
Description

Reads the current date and time from the RTC and returns it as a time_t value. Returns zero if an I2C error occurs (RTC not present, etc.).

Syntax

RTC.get();

Parameters

None.

Returns

Current date and time (time_t)

Example
time_t myTime;
myTime = RTC.get();
set(time_t t)
Description

Sets the RTC date and time to the given time_t value. Clears the oscillator stop flag (OSF) bit in the control/status register. See the oscStopped() function and also the DS323x datasheet for more information on the OSF bit.

Syntax

RTC.set(t);

Parameters

t: The date and time to set the RTC to (time_t)

Returns

I2C status (byte). Returns zero if successful.

Example
//this example first sets the system time (maintained by the Time library) to
//a hard-coded date and time, and then sets the RTC from the system time.
//the setTime() function is part of the Time library.
setTime(23, 31, 30, 13, 2, 2009);   //set the system time to 23h31m30s on 13Feb2009
RTC.set(now());                     //set the RTC from the system time
read(tmElements_t &tm)
Description

Reads the current date and time from the RTC and returns it as a tmElements_t structure. See the Arduino Time library for details on the tmElements_t structure.

Syntax

RTC.read(tm);

Parameters

tm: Address of a tmElements_t structure to which the date and time are returned.

Returns

I2C status (byte). Returns zero if successful. The date and time read from the RTC are returned to the tm parameter.

Example
tmElements_t tm;
RTC.read(tm);
Serial.print(tm.Hour, DEC);
Serial.print(':');
Serial.print(tm.Minute,DEC);
Serial.print(':');
Serial.println(tm.Second,DEC);
write(tmElements_t &tm)
Description

Sets the RTC to the date and time given by a tmElements_t structure. Clears the oscillator stop flag (OSF) bit in the control/status register. See the oscStopped() function and also the DS323x datasheet for more information on the OSF bit.

Syntax

RTC.write(tm);

Parameters

tm: Address of a tmElements_t structure used to set the date and time.

Returns

I2C status (byte). Returns zero if successful.

Example
tmElements_t tm;
tm.Hour = 23;             //set the tm structure to 23h31m30s on 13Feb2009
tm.Minute = 31;
tm.Minute = 30;
tm.Day = 13;
tm.Month = 2;
tm.Year = 2009 - 1970;    //tmElements_t.Year is the offset from 1970
RTC.write(tm);            //set the RTC from the tm structure
Methods for reading and writing RTC registers or static RAM (SRAM) for the DS3232

The DS3232RTC.h file defines symbolic names for the timekeeping, alarm, status and control registers. These can be used for the addr argument in the functions below.

writeRTC(byte addr, byte *values, byte nBytes)
Description

Write one or more bytes to RTC memory.

Syntax

RTC.writeRTC(addr, values, nbytes);

Parameters

addr: First SRAM address to write (byte). The valid address range is 0x00-0x12 for DS3231, 0x00-0xFF for DS3232. The general-purpose SRAM for the DS3232 begins at address 0x14. Address is not checked for validity by the library. values: An array of values to write (*byte)
nBytes: Number of bytes to write (byte). Must be between 1 and 31 (Wire library limitation) but is not checked by the library.

Returns

I2C status (byte). Returns zero if successful.

Example
//write 1, 2, ..., 8 to the first eight DS3232 SRAM locations
byte buf[8] = {1, 2, 3, 4, 5, 6, 7, 8};
RTC.sramWrite(0x14, buf, 8);
writeRTC(byte addr, byte value)
Description

Write a single byte to RTC memory.

Syntax

RTC.writeRTC(addr, value);

Parameters

addr: SRAM address to write (byte). The valid address range is 0x00-0x12 for DS3231, 0x00-0xFF for DS3232. The general-purpose SRAM for the DS3232 begins at address 0x14. Address is not checked for validity by the library. value: Value to write (byte)

Returns

I2C status (byte). Returns zero if successful.

Example
RTC.writeRTC(3, 14);   //write the value 14 to SRAM address 3
readRTC(byte addr, byte *values, byte nBytes)
Description

Read one or more bytes from RTC RAM.

Syntax

RTC.readRTC(addr, values, nbytes);

Parameters

addr: First SRAM address to read (byte). The valid address range is 0x00-0x12 for DS3231, 0x00-0xFF for DS3232. The general-purpose SRAM for the DS3232 begins at address 0x14. Address is not checked for validity by the library. values: An array to receive the values read (*byte)
nBytes: Number of bytes to read (byte). Must be between 1 and 32 (Wire library limitation) but is not checked by the library.

Returns

I2C status (byte). Returns zero if successful.

Example
//read the last eight locations of SRAM into buf
byte buf[8];
RTC.sramRead(248, buf, 8);
readRTC(byte addr)
Description

Reads a single byte from RTC RAM.

Syntax

RTC.readRTC(addr);

Parameters

addr: SRAM address to read (byte). The valid address range is 0x00-0x12 for DS3231, 0x00-0xFF for DS3232. The general-purpose SRAM for the DS3232 begins at address 0x14. Address is not checked for validity by the library.

Returns

Value read from the RTC (byte)

Example
byte val;
val = RTC.readRTC(3);  //read the value from SRAM location 3
Alarm methods

The DS3232 and DS3231 have two alarms. Alarm1 can be set to seconds precision; Alarm2 can only be set to minutes precision.

setAlarm(ALARM_TYPES_t alarmType, byte seconds, byte minutes, byte hours, byte daydate)
Description

Set an alarm time. Sets the alarm registers only. To cause the INT pin to be asserted on alarm match, use alarmInterrupt(). This method can set either Alarm 1 or Alarm 2, depending on the value of alarmType (use the ALARM_TYPES_t enumeration above). When setting Alarm 2, the seconds value must be supplied but is ignored, recommend using zero. (Alarm 2 has no seconds register.)

Syntax

RTC.setAlarm(alarmType, seconds, minutes, hours, dayOrDate);

Parameters

alarmType: A value from the ALARM_TYPES_t enumeration, above. (ALARM_TYPES_t)
seconds: The seconds value to set the alarm to. (byte)
minutes: The minutes value to set the alarm to. (byte)
hours: The hours value to set the alarm to. (byte)
dayOrDate: The day of the week or the date of the month. For day of the week, use a value from the Time library timeDayOfWeek_t enumeration, i.e. dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday. (byte)

Returns

None.

Example
//Set Alarm1 for 12:34:56 on Sunday
RTC.setAlarm(ALM1_MATCH_DAY, 56, 34, 12, dowSunday);
setAlarm(ALARM_TYPES_t alarmType, byte minutes, byte hours, byte daydate)
Description

Set an alarm time. Sets the alarm registers only. To cause the INT pin to be asserted on alarm match, use alarmInterrupt(). This method can set either Alarm 1 or Alarm 2, depending on the value of alarmType (use the ALARM_TYPES_t enumeration above). However, when using this method to set Alarm 1, the seconds value is set to zero. (Alarm 2 has no seconds register.)

Syntax

RTC.setAlarm(alarmType, minutes, hours, dayOrDate);

Parameters

alarmType: A value from the ALARM_TYPES_t enumeration, above. (ALARM_TYPES_t)
minutes: The minutes value to set the alarm to. (byte)
hours: The hours value to set the alarm to. (byte)
dayOrDate: The day of the week or the date of the month. For day of the week, use a value from the Time library timeDayOfWeek_t enumeration, i.e. dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday. (byte)

Returns

None.

Example
//Set Alarm2 for 12:34 on the 4th day of the month
RTC.setAlarm(ALM1_MATCH_DATE, 34, 12, 4);
alarmInterrupt(byte alarmNumber, boolean alarmEnabled)
Description

Enable or disable an alarm “interrupt”. Note that this “interrupt” causes the RTC’s INT pin to be asserted. To use this signal as an actual interrupt to a microcontroller, it will need to be connected properly and programmed in the application firmware. on the RTC.

Syntax

RTC.alarmInterrupt(alarmNumber, enable);

Parameters

alarmNumber: The number of the alarm to enable or disable, ALARM_1 or ALARM_2 (byte)
alarmEnabled: true or false (boolean)

Returns

None.

Example
RTC.alarmInterrupt(ALARM_1, true);      //assert the INT pin when Alarm1 occurs.
RTC.alarmInterrupt(ALARM_2, false);     //disable Alarm2
alarm(byte alarmNumber)
Description

Tests whether an alarm has been triggered. If the alarm was triggered, returns true and resets the alarm flag in the RTC, else returns false.

Syntax

RTC.alarm(alarmNumber);

Parameters

alarmNumber: The number of the alarm to test, ALARM_1 or ALARM_2 (byte)

Returns

Description (type)

Example
if ( RTC.alarm(ALARM_1) ) {     //has Alarm1 triggered?
    //yes, act on the alarm
}
else {
    //no alarm
}
Other methods
temperature(void)
Description

Returns the RTC temperature.

Syntax

RTC.temperature();

Parameters

None.

Returns

RTC temperature as degrees Celsius times four. (int)

Example
int t = RTC.temperature();
float celsius = t / 4.0;
float fahrenheit = celsius * 9.0 / 5.0 + 32.0;
squareWave(SQWAVE_FREQS_t freq)
Description

Enables or disables the square wave output.

Syntax

RTC.squareWave(freq);

Parameters

freq: a value from the SQWAVE_FREQS_t enumeration above. (SQWAVE_FREQS_t)

Returns

None.

Example
RTC.squareWave(SQWAVE_1_HZ);    //1 Hz square wave
RTC.squareWave(SQWAVE_NONE);    //no square wave
oscStopped(bool clearOSF)
Description

Returns the value of the oscillator stop flag (OSF) bit in the control/status register which indicates that the oscillator is or was stopped, and that the timekeeping data may be invalid. Optionally clears the OSF bit depending on the argument passed. If the clearOSF argument is omitted, the OSF bit is cleared by default. Calls to set() and write() also clear the OSF bit.

Syntax

RTC.oscStopped(clearOSF);

Parameters

clearOSF: an optional true or false value to indicate whether the OSF bit should be cleared (reset). If not supplied, a default value of true is used, resetting the OSF bit. (bool)

Returns

True or false (bool)

Example
if ( RTC.oscStopped(false) ) {      //check the oscillator
    //may be trouble
}
else {
    //all is well
}
References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Disk Storage

Provides support for using disk storage devices.

Block Devices

Internal devices such as SPI flash may be read and written in bytes, however disk devices must be accessed in blocks. Historically these blocks are referred to as ‘sectors’, which are 512 bytes for SD cards.

Sector-addressable (block) devices should be based on Disk::BlockDevice. An example is the Storage::SD::Card provided by the SD Storage library. Note that devices larger than 4GB will require the ENABLE_STORAGE_SIZE64 project setting.

Partitioning

Partitions can be enumerated and created using this library. Both MBR and GPT partitioning schemes are supported.

Call Storage::Disk::scanPartitions() to populate the device partition table. For each partition, additional information can be obtained via Storage::Partition::diskpart() call.

Partition information is written using Storage::Disk::formatDisk().

Buffering

Block devices are typically used with a sector-based filing system such as FAT (see FatIFS). Note that these filing systems are generally unsuitable for use with SPI flash without some kind of intermediate wear-levelling management layer as provided by SD card controllers.

By default, block devices must be strictly accessed only by sector, i.e. for SD cards aligned chunks of 512 bytes. This is rather inflexible so BlockDevice supports byte-level access using internal buffering, which applications may enable using the allocateBuffers method.

This allows other filing systems to be used. LittleFS seems to work OK, although SPIFFS IFS Library does not. Partitions may also be used directly without any filing system.

Important

The Device::sync() method must be called at appropriate times to ensure data is actually written to disk. Filing systems should take care of this internally when files are closed, for example.

Testing

Linux provides excellent support for testing generated image files.

Windows users may find this tool useful: https://www.diskinternals.com/linux-reader/.

Configuration
DISK_MAX_SECTOR_SIZE

default: 512

Determines the minimum supported read/write block size for storage devices.

For example, AF disks use 4096-byte sectors so internal reads and writes must be a multiple of this value. This will increase the internal buffer sizes and so consume more RAM.

Acknowledgements

Code in this library is based on http://elm-chan.org/fsw/ff/00index_e.html. It has been heavily reworked using structures (from the Linux kernel) and separated from the actual FAT filing system implementation (FatIFS).

API
namespace Disk

Typedefs

using SysTypes = BitSet<uint8_t, SysType>

Enums

enum class Error

Values:

enumerator XX
enum class SysType : uint8_t

Identifies exact disk volume type.

Values:

enumerator unknown

Partition type not recognised.

enumerator fat12
enumerator fat16
enumerator fat32
enumerator exfat
enum SysIndicator

MBR partition system type indicator values.

Values:

enumerator SI_FAT12
enumerator SI_FAT16

FAT16 with fewer than 65536 sectors.

enumerator SI_FAT16B

FAT16B with 65536 or more sectors.

enumerator SI_IFS
enumerator SI_EXFAT
enumerator SI_FAT32X

FAT32 with LBA.

Functions

template<typename T>
T align_up(T value, uint32_t align)
template<typename T>
auto getBlockCount(T byteCount, uint32_t blockSize)
uint32_t crc32_byte(uint32_t crc, uint8_t d)
uint32_t crc32(uint32_t bcc, const void *data, size_t length)
inline uint32_t crc32(const void *data, size_t length)
inline bool operator!(Error err)
Error formatDisk(BlockDevice &device, GPT::PartitionTable &table, const Uuid &diskGuid = {})

Partition a device using the GPT scheme.

Parameters:
  • device

  • table – Partitions to create

Return values:

Error

Error formatDisk(BlockDevice &device, MBR::PartitionTable &table)

Partition a device using the MBR scheme.

Parameters:
  • device

  • table – Partitions to create

Return values:

Error

inline SysType getSysTypeFromIndicator(SysIndicator si)
Error validate(BasePartitionTable &table, storage_size_t firstAvailableBlock, storage_size_t totalAvailableBlocks, uint32_t blockSize)

Validate partition table entries.

  • If size <= 100 then the actual size is calculated as a percentage and updated

  • If offset = 0 then a suitable location is found and the offset updated

On success, partition entries are ordered by position.

Parameters:
  • firstAvailableBlock – First block number which may be allocated to a partition

  • totalAvailableBlocks – Number of blocks available for partition allocation

  • blockSize – Size of a block

Return values:

Error – For each partition:

bool scanPartitions(Device &device)

Variables

constexpr uint32_t PARTITION_ALIGN = {0x100000U}
static constexpr SysTypes fatTypes = SysType::fat12 | SysType::fat16 | SysType::fat32 | SysType::exfat
class BlockDevice : public Storage::Device
#include <BlockDevice.h>

Base class for sector-addressable (block) devices.

Inherited classes must set the sectorCount value, and implement the four raw_xxx methods.

This class supports byte-level access using internal buffering, which if required must be enabled by the application via the allocateBuffers method. Without buffering, read/writes must always be sector-aligned. Rrase must always be sector-aligned.

For power-loss resiliency it is important to call sync() at appropriate times. Filing system implementations should do this after closing a file, for example. Applications should consider this if leaving files open for extended periods, and explicitly call ‘flush’ on the filing system or ‘sync’ on the partition or device if necessary.

Subclassed by Storage::Disk::HostFileDevice, Storage::SD::Card, USB::MSC::LogicalUnit

Public Functions

virtual bool read(storage_size_t address, void *dst, size_t size) override

Read data from the storage device.

Parameters:
  • address – Where to start reading

  • dstBuffer to store data

  • size – Size of data to be read, in bytes.

Return values:

bool – true on success, false on error

virtual bool write(storage_size_t address, const void *src, size_t size) override

Write data to the storage device.

Parameters:
  • address – Where to start writing

  • src – Data to write

  • size – Size of data to be written, in bytes.

Return values:

bool – true on success, false on error

virtual bool erase_range(storage_size_t address, storage_size_t size) override

Erase a region of storage in preparation for writing.

Parameters:
  • address – Where to start erasing

  • size – Size of region to erase, in bytes

Return values:

bool – true on success, false on error

inline virtual size_t getBlockSize() const override

Obtain smallest allocation unit for erase operations.

inline virtual storage_size_t getSize() const override

Obtain addressable size of this device.

Return values:

storage_size_t – Must be at least as large as the value declared in the hardware configuration

inline virtual storage_size_t getSectorCount() const override

Obtain total number of sectors on this device.

virtual bool sync() override

Flush any pending writes to the physical media.

Devices with intermediate buffering should implement this method.

Return values:

bool – Return false if sync operation failed.

bool allocateBuffers(unsigned numBuffers)

Set number of sector buffers to use.

Required to support byte-level read/write operations on block devices. Buffering can improve performance, with diminishing returns above around 4 sectors.

Parameters:

numBuffers – Number of buffers to allocate 1,2,4,8,etc. Pass 0 to deallocate/disable buffering.

Return values:

bool – false on memory allocation error, or if failed to flush existing buffers to disk

struct Stat
#include <BlockDevice.h>

Public Members

Func func[3]

Read, Write, Erase.

std::map<uint32_t, Func> sectors

By sector.

struct Func
#include <BlockDevice.h>
struct Buffer : public std::unique_ptr<uint8_t[]>
#include <Buffer.h>
class BufferList
#include <Buffer.h>
class HostFileDevice : public Storage::Disk::BlockDevice
#include <HostFileDevice.h>

Create custom storage device using backing file.

Public Functions

HostFileDevice(const String &name, const String &filename, storage_size_t size)

Construct a file device with custom size.

Parameters:
  • name – Name of device

  • filename – Path to file

  • size – Size of device in bytes

HostFileDevice(const String &name, const String &filename)

Construct a device using existing file.

Device will match size of existing file

Parameters:
  • name – Name of device

  • filename – Path to file

inline virtual String getName() const override

Obtain unique device name.

inline virtual Type getType() const override

Obtain device type.

struct DiskPart
#include <PartInfo.h>

Adds information specific to MBR/GPT disk partitions.

Subclassed by Storage::Disk::PartInfo

Public Functions

size_t printTo(Print &p) const

Print full contents of this structure.

Public Members

Uuid typeGuid

GPT type GUID.

Uuid uniqueGuid

GPT partition unique GUID.

SysType systype = {}

Identifies volume filing system type.

SysIndicator sysind = {}

Partition sys value.

struct PartInfo : public Storage::Partition::Info, public Storage::Disk::DiskPart
#include <PartInfo.h>

In-memory partition information.

A disk Storage::Partition refers to this instance.

Public Functions

inline virtual const Disk::DiskPart *diskpart() const override

Obtain additional disk information.

Accessed via Partition::diskpart() method

virtual size_t printTo(Print &p) const override

Print important fields only.

class BasePartitionTable : public OwnedLinkedObjectListTemplate<PartInfo>
#include <PartInfo.h>

Common type for MBR/GPT partition table.

Subclassed by Storage::Disk::GPT::PartitionTable, Storage::Disk::MBR::PartitionTable

class Scanner
#include <Scanner.h>

Class to iterate through disk partition tables.

Supports MBR and GPT partitioning schemes.

Public Functions

std::unique_ptr<PartInfo> next()

Obtains the next partition entry (if any)

class SectorBuffer : public std::unique_ptr<uint8_t[]>
#include <SectorBuffer.h>

Buffer for working with disk sectors.

namespace EXFAT
namespace FAT
namespace GPT

Functions

String getTypeName(const Uuid &typeGuid)

Get string for known GPT type GUIDs.

struct SmingTypeGuid : public Uuid
#include <GPT.h>
class PartitionTable : public Storage::Disk::BasePartitionTable
#include <GPT.h>

Public Functions

inline bool add(const String &name, SysType sysType, storage_size_t offset, storage_size_t size, const Uuid &uniqueGuid = {}, const Uuid &typeGuid = {}, Partition::Flags flags = 0)

Add a new standard GPT partition definition.

Parameters:
  • namePartition name

  • sysType – Intended content for this partition

  • offset – Start offset, or 0 to have position calculated

  • size – Size of partition (in bytes), or percentage (0-100) of total partitionable disk space

  • uniqueGuid – Unique partition identifier (optional: will be generated if not provided)

  • typeGuidPartition type GUID (default is BASIC_DATA)

  • flags

Return values:

bool – true on success

inline bool add(const String &name, Partition::FullType type, storage_size_t offset, storage_size_t size, const Uuid &uniqueGuid = {}, Partition::Flags flags = 0)

Add a new GPT partition for a regular Sming filing system.

Note

These partitions use a custom type GUID and won’t be recognised by external software.

Parameters:
  • namePartition name

  • type – Sming partition type/subtype

  • offset – Start offset, or 0 to have position calculated

  • size – Size of partition (in bytes), or percentage (0-100) of total partitionable disk space

  • uniqueGuid – Unique partition identifier (optional: will be generated if not provided)

  • flags

Return values:

bool – true on success

namespace MBR
class PartitionTable : public Storage::Disk::BasePartitionTable
#include <MBR.h>

Public Functions

inline bool add(SysType sysType, SysIndicator sysIndicator, storage_size_t offset, storage_size_t size, Partition::Flags flags = 0)

Add a new MBR partition definition.

Note

MBR does not have partition name field; this will appear as ‘mbr1’, ‘mbr2’, etc.

Parameters:
  • sysType – Intended content for this partition (or ‘unknown’)

  • SysIndicator – Appropriate system code SI_xxx

  • offset – Start offset, or 0 to have position calculated

  • size – Size of partition (in bytes), or percentage (0-100) of total partitionable disk space

  • flags

Return values:

bool – true on success

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

FatIFS

IFS library for Sming supporting FAT filesystems.

Formatting

Use IFS::FAT::formatVolume() to format a partition:

part = ... // Storage::Partition
int err = IFS::FAT::formatVolume(part);
Serial << "formatVolume: " << IFS::Error::toString(err) << endl;

An optional IFS::FAT::FormatOptions parameter can be used to provide additional settings:

IFS::FAT::FormatOptions opt{
   .volumeLabel = "My FAT volume", // Volume label (distinct from partition name)
   .types = Storage::Disk::SysType::fat32, // Only use FAT32 for this volume
};
int err = IFS::FAT::formatVolume(part, opt);

For more detailed low-control over the formatting:

IFS::FAT::FatParam param;
int err = IFS::FAT::calculateFatParam(part, opt, param);
if(err == FS_OK) {
   // .. adjust settings in `param` before formatting
   int err = IFS::FAT::formatVolume(part, param);
}
Mounting FAT volumes

Unlike SPIFFS or LittleFS, failure to mount a filing system does not auto-format the volume. Instead, this is left to the application to handle. For example:

auto fs = IFS::createFatFilesystem(part);
if(fs != nullptr) {
   int err = fs->mount();
   if(err == Error::BadFileSystem) {
      // Filesystem layout is invalid...
      fs->format();
      err = fs->mount();
   }
   if(err != FS_OK) {
      // Handle error
   }
}
Configuration options
ENABLE_EXFAT

default: 0 (disabled)

Set to 1 to enable support for EXFAT volumes. Requires ENABLE_STORAGE_SIZE64 option.

ENABLE_FAT_TRIM

default: disabled

When using devices with TRIM support this setting should be enabled.

See https://en.wikipedia.org/wiki/Trim_(computing).

FAT_CODEPAGE

default: 437 (US English)

Acknowledgements
API Documentation
namespace FAT

Functions

int translateFatfsResult(uint8_t result, bool diskio_write)
String fatfsErrorToStr(uint8_t err)
ErrorCode calculateFatParam(Partition partition, const FormatOptions &opt, FatParam &param)

Deduce FAT volume parameters for given space.

Parameters:
  • partition – The partition to format

  • opt – Formatting options

  • param – On success, contains calculated parameters for FAT volume

Return values:

ErrorCode – When partitioning using MBR format, this method can be used to determine the Sys indicator value setting.

ErrorCode formatVolume(Partition partition, const FatParam &param)

Format partition using pre-calculated FAT parameters.

Parameters:
  • partition – The partition to format

  • param – Detailed FAT parameters (returned from calculateFatParam)

Return values:

ErrorCode – This function allows fine control over exactly how a FAT partition is constructed. Generally the calculateFatParam function should be used to populate the param structure, then any modifications can be made as required before actually formatting the volume.

inline ErrorCode formatVolume(Partition partition, const FormatOptions &opt = {})

Format partition with a blank FAT volume.

Parameters:
  • partition – The partition to format

  • opt – Formatting options

Return values:

ErrorCode

class FileSystem : public IFS::IFileSystem
#include <FileSystem.h>

Wraps fatfs

Public Functions

virtual int mount() override

Mount file system, performing any required initialisation.

Return values:

error – code

virtual int getinfo(Info &info) override

get filing system information

Parameters:

info – structure to read information into

Return values:

int – error code

virtual int setProfiler(IProfiler *profiler) override

Set profiler instance to enable debugging and performance assessment.

Parameters:

profiler

Return values:

int – error code - profiling may not be supported on all filesystems

virtual String getErrorString(int err) override

get the text for a returned error code

Return values:

String

virtual int opendir(const char *path, DirHandle &dir) override

open a directory for reading

Parameters:
  • path – path to directory. nullptr is interpreted as root directory

  • dir – returns a pointer to the directory object

Return values:

int – error code

virtual int readdir(DirHandle dir, Stat &stat) override

read a directory entry

Note

File system allocates entries structure as it usually needs to track other information. It releases memory when closedir() is called.

Parameters:
  • dir

  • stat

Return values:

int – error code

virtual int rewinddir(DirHandle dir) override

Reset directory read position to start.

Parameters:

dir

Return values:

int – error code

virtual int closedir(DirHandle dir) override

close a directory object

Parameters:

dir – directory to close

Return values:

int – error code

virtual int mkdir(const char *path) override

Create a directory.

Only the final directory in the path is guaranteed to be created. Usually, this call will fail if intermediate directories are not present. Use IFS::FileSystem::makedirs() for this purpose.

Parameters:

path – Path to directory

Return values:

int – error code

virtual int stat(const char *path, Stat *stat) override

get file information

Returned stat will indicate whether the path is a mountpoint or directory. For a mount point, stats for the root directory of the mounted filesystem must be obtained by opening a handle then using fstat:

int handle = fs.open("path-to-mountpoint");
Stat stat;
fs.fstat(handle, &stat);
fs.close(handle);
Parameters:
  • path – name or path of file/directory/mountpoint

  • s – structure to return information in, may be null to do a simple file existence check

Return values:

int – error code

virtual int fstat(FileHandle file, Stat *stat) override

get file information

Parameters:
  • file – handle to open file

  • stat – structure to return information in, may be null

Return values:

int – error code

virtual int fcontrol(FileHandle file, ControlCode code, void *buffer, size_t bufSize) override

Low-level and non-standard file control operations.

To simplify usage the same buffer is used for both input and output. Only the size of the buffer is provided. If a specific FCNTL code requires more information then it will be contained within the provided data.

Parameters:
  • file

  • code – FCNTL_XXX code

  • buffer – Input/Output buffer

  • bufSize – Size of buffer

Return values:

int – error code or, on success, data size

virtual int fsetxattr(FileHandle file, AttributeTag tag, const void *data, size_t size) override

Set an extended attribute on an open file.

Note

Attributes may not be written to disk until flush() or close() are called

Parameters:
  • file – handle to open file

  • tag – The attribute to write

  • data – Content of the attribute. Pass nullptr to remove the attribute (if possible).

  • size – Size of the attribute in bytes

Return values:

int – error code

virtual int fgetxattr(FileHandle file, AttributeTag tag, void *buffer, size_t size) override

Get an extended attribute from an open file.

Parameters:
  • file – handle to open file

  • tag – The attribute to read

  • buffer – Buffer to receive attribute content

  • size – Size of the buffer

Return values:

int – error code, on success returns size of attribute (which may be larger than size)

virtual int fenumxattr(FileHandle file, AttributeEnumCallback callback, void *buffer, size_t bufsize) override

Enumerate attributes.

Parameters:
  • file – handle to open file

  • callback – Callback function to invoke for each attribute found

  • buffer – Buffer to use for reading attribute data. Use nullptr if only tags are required

  • bufsize – Size of buffer

Return values:

int – error code, on success returns number of attributes read

virtual int setxattr(const char *path, AttributeTag tag, const void *data, size_t size) override

Set an extended attribute for a file given its path.

Parameters:
  • path – Full path to file (or directory)

  • tag – The attribute to write

  • data – Content of the attribute. Pass nullptr to remove the attribute (if possible).

  • size – Size of the attribute in bytes

Return values:

int – error code

virtual int getxattr(const char *path, AttributeTag tag, void *buffer, size_t size) override

Get an attribute from a file given its path.

Parameters:
  • file – Full path to file (or directory)

  • tag – The attribute to read

  • buffer – Buffer to receive attribute content

  • size – Size of the buffer

Return values:

int – error code, on success returns size of attribute (which may be larger than size)

virtual FileHandle open(const char *path, OpenFlags flags) override

open a file (or directory) by path

This function may also be used to obtain a directory handle to perform various operations such as enumerating attributes. Calls to read or write on such handles will typically fail.

Parameters:
  • path – full path to file

  • flags – Desired access and other options

Return values:

FileHandle – file handle or error code

virtual int close(FileHandle file) override

close an open file

Parameters:

file – handle to open file

Return values:

int – error code

virtual int read(FileHandle file, void *data, size_t size) override

read content from a file and advance cursor

Parameters:
  • file – handle to open file

  • data – buffer to write into

  • size – size of file buffer, maximum number of bytes to read

Return values:

int – number of bytes read or error code

virtual int write(FileHandle file, const void *data, size_t size) override

write content to a file at current position and advance cursor

Parameters:
  • file – handle to open file

  • data – buffer to read from

  • size – number of bytes to write

Return values:

int – number of bytes written or error code

virtual file_offset_t lseek(FileHandle file, file_offset_t offset, SeekOrigin origin) override

change file read/write position

Parameters:
  • file – handle to open file

  • offset – position relative to origin

  • origin – where to seek from (start/end or current position)

Return values:

file_offset_t – current position or error code

virtual int eof(FileHandle file) override

determine if current file position is at end of file

Parameters:

file – handle to open file

Return values:

int – 0 - not EOF, > 0 - at EOF, < 0 - error

virtual file_offset_t tell(FileHandle file) override

get current file position

Parameters:

file – handle to open file

Return values:

file_offset_t – current position relative to start of file, or error code

virtual int ftruncate(FileHandle file, file_size_t new_size) override

Truncate (reduce) the size of an open file.

Note

In POSIX ftruncate() can also make the file bigger, however SPIFFS can only reduce the file size and will return an error if newSize > fileSize

Parameters:
  • file – Open file handle, must have Write access

  • newSize

Return values:

intError code

virtual int flush(FileHandle file) override

flush any buffered data to physical media

Parameters:

file – handle to open file

Return values:

int – error code

virtual int rename(const char *oldpath, const char *newpath) override

rename a file

Parameters:
  • oldpath

  • newpath

Return values:

int – error code

virtual int remove(const char *path) override

remove (delete) a file by path

Parameters:

path

Return values:

int – error code

virtual int fremove(FileHandle file) override

remove (delete) a file by handle

Parameters:

file – handle to open file

Return values:

int – error code

virtual int format() override

format the filing system

Note

this does a default format, returning file system to a fresh state The filing system implementation may define more specialised methods which can be called directly.

Return values:

int – error code

virtual int check() override

Perform a file system consistency check.

Note

if possible, issues should be resolved. Returns 0 if file system checked out OK. Otherwise there were issues: < 0 for unrecoverable errors,

0 for recoverable errors.

Return values:

int – error code

struct FormatOptions
#include <Format.h>

Formatting options.

Public Members

SysTypes types = {0}

Valid partition format types.

uint8_t numFats = {1}

Number of FATs (1 or 2)

unsigned align = {0}

Data area alignment (sector)

unsigned numRootEntries = {512}

Number of root directory entries.

uint32_t clusterSize = {4096}

Cluster size (byte)

struct FatParam
#include <Format.h>

Public Members

uint32_t sectorsPerBlock

Flash erase block size.

uint16_t sectorsPerCluster

Set to 0 for auto-calculation.

References
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Flash In-Place

This library is intended to support in-place application updates where there is insufficient available flash storage for more than one application partition.

This is aimed at the (Rp2040) Pico (W) which has only 2MBytes of flash.

This library takes a firmware image stored elsewhere in flash, then runs a simple piece of code running from RAM. This copies the new firmware into the application partition then reboots.

This is similar to the Arduino Pico approach, but it doesn’t require any bootloader changes and the critical copier code (in RAM) is much simpler as it doesn’t need to know anything about filing systems; all it does is copy blocks from one region of flash to another.

Operation
  1. Verify the source image

    The application handles this e.g. CRC32 checksum.

  2. Build a block list

    Most file systems will store larger files (especially of 2-300KBytes or more) in multiple, non-contiguous regions of flash storage. These regions are known as ‘extents’, and this information is obtained via the IFS::IFilelSystem::fgetextents() call.

    The FlashIP class uses this to build a list, which the copier code then executes from RAM. This keeps the copier code very simple.

    Example:

    // Get the target partition
    auto part = *Storage::findPartition(Storage::Partition::Type::app);
    
    // Build the block list
    FlashIP fip;
    if(fip.addFile(part, 0, "firmware.bin")) {
        // Copy the image and reboot
        fip.execute();
    }
    
  3. Write the block list to flash

    This code runs exclusively in RAM. All interrupts and DMA are disabled first. The block list is then iterated with a read/erase/program procedure.

  4. Reboot

Issues
Recovery

There is no recovery available if the update operation is interrupted part-way. If physical device access is available then firmware can be re-flashed via USB.

Writing a second-stage bootloader and storing the block list in FLASH would mitigate this issue. For example, we’d reserve the first two flash sectors for this, with the application itself relocated. Each block-list entry will be invalidated once complete so the bootloader need only write incomplete blocks.

Compression

Using a compressed firmware image would require e.g. uzlib to be located in RAM. We can add a build variable to make that happen.

A quick test on the release HttpServer_ConfigNetwork image using gzip reduces the size from 341136 to 280084 bytes (82% of the original size).

Encryption

Note that where updating via local network, encryption may not be necessary.

If encryption is used, the application would need to decrypt that first. Typically this is done as the file is received, before writing the data to the local file.

References
Used by
SoC support
  • host

  • rp2040

GoogleCast

This component allows you to communicate with Chrome Cast dongles or smart TVs that support the Google Cast Protocol.

Using
  1. Add these lines to your application componenent.mk file:

    COMPONENT_DEPENDS += GoogleCast
    ENABLE_SSL := Bearssl
    
  2. Add these lines to your application:

    #include <Network/GoogleCast/Client.h>
    
  3. Basic example:

    #include <Network/GoogleCast/Client.h>
    
    GoogleCast::Client castClient;
    
    void gotIp(IpAddress ip, IpAddress mask, IpAddress gateway)
    {
       // connect directly to the IP of the devise
       castClient.connect(IpAddress("192.168.10.15"));
    
       castClient.onConnect([](bool success) {
          Serial.print(F("Client connect: "));
          Serial.println(success ? "OK" : "FAIL");
          if(success) {
            // Starting YouTube on the device
            castClient.receiver.launch(APPID_YOUTUBE);
          }
       });
    }
    
Re-Generating C files from proto file

You can re-generate the C files from the proto file. This can be useful if you want to use a newer version of the Google Cast proto file. Make sure to compile it using nanopb. The re-generation can be done with the commands below:

cd $SMING_HOME/Libraries/GoogleCast/samples/basic
make python-requirements # should be executed once to download and install the required python packages
make rebuild-cast-proto
Protocol

There are multiple documents in internet that describe the Google Cast protocol and its inner workings. As a start one can take a look at the presentation and documents below.

App-Ids

List of current APP-IDS https://clients3.google.com/cast/chromecast/device/baseconfig

Individual app configurations are obtained from this url: https://clients3.google.com/cast/chromecast/device/app?a={appid}

Where {appid} is the id of the app, and the output of this is JSON in the format of a receiver app definition.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Sming Graphics Library

An asynchronous graphics management library intended for SPI-based displays. See the Basic Graphics sample application for demonstration of how to use it.

For a discussion about multitasking and how Sming works, see Multitasking.

Getting started

On first use several required python modules must be installed. Try building a sample application:

cd $SMING_HOME
make fetch Graphics
cd Libraries/Graphics/samples/Basic_Graphics
make python-requirements
make
Virtual Screen

This is a display device for use by the Host Emulator. The ‘display’ is a python application which communicates with the Sming Graphics::Display::Virtual display driver using a TCP socket. Graphics processing is handled using SDL2.

To start the virtual screen server type make virtual-screen from your project directory. The default TCP port is 7780. If you need to change this, use:

make virtual-screen VSPORT=7780

The screen server’s IP address is shown in the caption bar. Build and run your project in host mode as follows:

make SMING_ARCH=Host
make run VSADDR=192.1.2.3
Some definitions
Graphics::Object

Defines a specific graphical item, such as a line, circle or image.

Graphics::SceneObject

List of objects describing the components of a displayed image (line, rectangle, circle, image, text, etc.) Also contains a list of assets which allows objects to be re-used within a scene.

Applications use methods such as drawCircle to construct a scene.

Graphics::TextBuilder

Helper object to simplify text layout, handling wrapping, alignment etc.

Graphics::Surface

Rectangular array of pixels representing a display or offscreen bitmap

Graphics::Device

A display device. This provides a single Surface for access to the screen.

Graphics::Renderer

Class which performs background rendering tasks such as drawing a sequence of lines, filling a circle, etc. Each renderer performs a single, specific-task. The renderers are created by the Surface which allows display devices to provide customised implementations.

Graphics::PixelFormat

Describes the format of pixel data required by a Surface.

For example, the ILI9341 display uses RGB565 colours (2 bytes) with the MSB first. It can use other pixel formats (RGB 6:6:6), but this is how the driver sets the display up during initialisation.

Applications do not need to be aware of this, however, and simply express colours in a 32-bit Graphics::Color type. This includes an alpha channel, where 255 is fully opaque and 0 is fully transparent. Conversion is handled in a single step (via Graphics::pack()) so further manipulation (even byte-swapping) is not required by the display driver.

Note that the ILI9341 display has another quirk in that display data is always read back in 24-bit format (18 significant bits). This detail is managed by the display driver which does the conversion during read operations. See Graphics::Surface::readDataBuffer().

Graphics::Blend

This is a virtual base class used to implement custom colour blending operations. Currently only supported for memory images using Graphics::ImageSurface.

Resources

Projects describe which fonts and image resources they require in a JSON resource script.

This is compiled to produce two files:
$(OUT_BASE)/resource.h

A header file describing the resources and containing indices and other lookup tables. Include this ONCE from a source file.

$(OUT_BASE)/resource.bin

Contains image data and font glyph bitmaps. This file can be linked into the application or stored in a file. A better approach is to store it in a dedicated partition as this avoids any filing system overhead. The library accesses this as a stream - applications must call Graphics::Resource::init().

The goal is to enable an optimal set of resources to be produced for the target display from original high-quality assets.

Usage
  1. Create a directory to contain project resources, e.g. resource.

  2. Place any custom images, fonts, etc. into this directory

  3. Create a resource script file, e.g. resource/graphics.rc. See below for details on editing this file.

  4. Add an entry to the project component.mk file:

    RESOURCE_SCRIPT := resource/graphics.rc
    
  5. Optionally, change the directory where compiled resources are written:

    RESOURCE_OUTPUT := out
    

The general structure of a resource script is:

{
    "resources": {
        "<type>": {
            "<name>": {
                ...
            }
        }
    }
}
Fonts

A Graphics::Font contains up to four typefaces which correspond to the selected style (normal, italic, bold or bold+italic).

The default font is Graphics::LcdFont, a simple Arduino fixed Adafruit GFX font. Only the ‘normal’ typeface is defined. This is linked into the program image.

All other fonts are loaded from resources. These are parsed from their original format and converted to an efficient internal format based on the Arduino GFX font library.

Resource script entries look like this:

Styles are optional but a font must have at least one typeface.

By default, all ASCII characters from 0x20 (space) to 0x7e (~). The codepoints parameter is a comma-separated list of ranges:

a-z,A-Z,0-9,0x4000-0x4050

This overrides the default and includes only characters and digits, plus unicode characters in the given range. The chars parameter is a simple list of characters, e.g. “Include these chars”. Both lists are combined, de-duplicated and sorted in ascending order.

The following font classes are currently supported:

GFX

Adafruit GFX library font files. These are identified using a “gfx/” prefix for the font files. See resource/fonts/GFX.

Linux

Linux console bitmap fonts. These are in .c format, identified by “linux/” path prefix. See resource/fonts/Linux.

PFI

The “.pfi” file extension is a Portable Font Index. Font glyph bitmap information is contained in an associated .pbm file. See resource/fonts/PFI.

VLW

Smoothed fonts produced by the https://processing.org/ library with a “.vlw” file extension. These are from the TFT_eSPI library. See resource/fonts/VLW.

Note that TTF/OTF scalable vector fonts are supported directly by this library so is the preferred format for new fonts.

freetype
Font type is identified from the file extension:
.ttf

TrueType

.otf

OpenType

.pcf, .pcf.gz

X11 bitmap font

The freetype library supports other types so if required these are easily added.

These fonts have some additional parameters:
“mono”: <True/False>

Whether to produce monochrome (1-bit) or grayscale (8-bit alpha) glyphs. If not specified, defaults to grayscale.

“size”: <Point size of font>

e.g. 16, 14.5

Images

Resource script entries look like this:

"image": {
    "<name>": {
        "format": "<target format>", // RGB565, RGB24 or BMP
        "source": "filename" | "url",
        // Optional list of transformations to apply
        "transform": {
            "width": target width,      // Height will be auto-scaled
            "height": target height,    // Width will be auto-scaled
            "crop": "width,height",     // Crop image to given size from (0, 0)
            "crop": "x,y,width,height", // Crop to selected area of image
            "resize": "width,height",   // Resize image
            "flip": "left_right" | "top_bottom", // Flip the image horizontally or vertically
            "rotate": angle in degrees, // Rotate the image
            "color": { // Convert image to grayscale then colorise it
                "black-color": blackpoint, // Color at blackpoint, e.g. "darkblue": 0
                "mid-color": midpoint,     // OPTIONAL
                "white-color": whitepoint  // Color at whitepoint, e.g. "lightred": 255
            }
        }
    }
}

The Python pillow library is used to read and process images.

The format parameter is intended to produce a raw, un-compressed image compatible with the target display device. Alternatively, specify “BMP” to output a standared .bmp file or omit to store the original image contents un-processed.

Scene construction

Instead of writing directly to the display, applications create a Graphics::SceneObject which will contain a description of the scene to be drawn.

The scene can then be constructed using methods such as drawLine, drawRect, etc. These create an object describing the operation and adds it to the scene.

When the scene is fully described, the application calls the display device’s render method and the rendering begins in the background. A callback function may be provided which will be invoked when the scene has been drawn. The application is free to start creating further scenes, etc.

Note: Scenes are never modified during rendering so can be drawn multiple times if required.

Rendering

This is the process of converting the scene objects into pixels which are then drawn into a Graphics::Surface.

Computation and primitive rendering is done in small, manageable chunks via the task queue. Data is sent to the screen using the appropriate display driver. The ILI9341 driver uses interrupts and hardware (SPI) transfers to do its work.

If the primitive object is simple (e.g. block fill, horizontal/vertical line) then it can be written to the display buffer immediately.

More complex shapes (images, text, diagonal lines, rectangles, triangles, circles, etc.) create a specific Graphics::Renderer instance to do the work.

Transparency (alpha-blending) involves a read-modify-write cycle. The ILI9341 driver can also handle small transparent rectangles (including individual pixels) to assist with line drawing. Larger areas are handled by the appropriate renderer.

Shapes such as rectangles, triangles, circles, etc. are described as a set of connected lines and points which is then iterated through and rendered using fill operations.

The standard Graphics::Surface implementation uses a standard set of renderers to do this work, however these can be overridden by display devices to make use of available hardware features.

Transparency

With sufficient RAM this is a trivial exercise as we just render everything to a memory surface then copy it to the display. For updating small areas of the screen this may be the best approach. However, even a small 240x320 pixel display would require 150kBytes with RGB565.

The more general approach adopted by this library is to read a small block of pixels from the display, combine them with new data as required then write the block back to the display. The Graphics::TextRenderer does this by default. The algorithm also ensures that text can be rendered correctly with filled backgrounds (solid, transparent or custom brushes).

Note

It may not be possible to read from some displays. For example, a small 1-inch display is commonly available using the Graphics::Display::ST7789V controller connected in 4-wire mode, but with the SDO line unconnected internally.

In such cases transparency must be handled by the application using memory surfaces.

Display driver

The ILI9341 display driver buffers requests into a set of hardware-specific commands with associated data. When the request buffer is full, it is sent it to the hardware using the HardwareSPI library. This particular display is typically configured in 4-wire SPI mode which requires an additional GPIO to select between command and data transfers. The driver manages this within its interrupt service routines.

Two request buffers are used so that when one buffer has completed transfer the next can begin immediately. This switch is handled within the interrupt service routine, which also schedules a task callback to the renderer so it may re-fill the first request buffer.

Configuration variables
RESOURCE_SCRIPT

Location of .rc resource script for your project, relative to project directory. Define this in your project’s component.mk file.

RESOURCE_OUTPUT

Path to directory where compiled resources will be written, relative to project directory. Default is project build directory, e.g. out/Rp2040/debug/build.

VSADDR

e.g. 192.168.1.105:7780

TCP address of the virtual screen server application, as shown in the title bar.

VSPORT

default: 7780

Port number to use for virtual screen.

ENABLE_GRAPHICS_DEBUG

default 0 (off)

Set to ‘1’ to enable additional debug output from renderers

ENABLE_GRAPHICS_RAM_TRACKING

default 0 (off)

Set to ‘1’ to enable additional diagnistics to assist with tracking RAM usage

Further work

The list of possible updates to the library is endless. Some thoughts:

Text wrapping

Currently wraps at common punctuation characters. Add options to specify how to break text (character boundaries, word boundaries, break characters, etc.)

Rich Text

Add support for HTML, markdown and other suitable formats. This could be handled by the resource compiler by converting to Graphics::Drawing resources.

Transparent bitmaps

Simple transparency can be handled using Blend Images with per-pixel alpha are not currently implemented. The resource compiler would be extended to support creation of images in ARGB format, with appropriate renderer updates.

Text glyph alignment

Text is drawn using the upper-left corner as reference. It may be desirable to change this, using the baseline or bottom corner instead. This can be added as an option.

Metafiles

Rather than describing scenes in code, we should be able to draw them using other tools and export the layouts as metafiles. Similarly, we might like to export our scenes as metafiles for later re-use.

An example use case is for a ‘scribble’ application, recording pen strokes a touch-enabled screen.

The Drawing API is intended for this use, providing a simple Graphics Description Language (GDL). The provided GDRAW_xxx macros allow embedding of these scripts within programs. This feature is restricted so appropriate only for very simple constructions.

Drawings could be described in textual resource files and compiled during build.

A simple utility program can be written to convert between text and binary GDL formats. Other conversion programs then only need to generate the text equivalent. This will allow the binary format to change as required.

It would be highly desirable to give subroutines textual identifiers. These would be resolved into numeric identifiers for the binary format.

Interactive objects

Drawing buttons, for example, might be done by defining the basic button in GDL.

The button routine could use the following:

pen #0: Inactive surround
pen #1: Active surround
brush #0: Inactive fill
brush #1: Active fill

These slots can be set depending on the desired state by program code. It would be more efficient if GDL can do this based on other parameters, or perhaps by registering callbacks or control objects to make these decisions.

For example, a ButtonObject could use GDL to draw itself and provide callbacks to handle state control, etc. This would also be required for control purposes.

To support use of touch screens we need the ability to query scenes and identify objects at a specific position. Those objects may then be adjusted and redrawn to reflect ‘push’ operations, etc.

For example, we might create a button control by defining a scene describing how the button looks, then create an InteractiveObject containing a reference to that scene plus some kind of user identifier and location of the control ‘hot spot’.

Brushes, Gradient fills, etc.

Rectangular gradient fills can be done by creating a custom ImageObject and drawing it. The object just generates the pixel data as requested according to configured parameters.

This is the basis for Brush objects which would be used to render circles, rounded rectangles and glyphs. That would allow for all sorts of useful effects, including clipping images to other shapes.

Tiling an image can be done by filling a rectangle with an ImageBrush, which deals with coordinate wrapping and returns the corresponding region from a source ImageObject.

Cursors, sprites and priority drawing

Response to user input should be prioritised, such as movement of a cursor or mouse pointer.

Cursors are drawn over everything else. Although some hardware may support cursors directly, for now we’ll assume not. Similarly text carets (e.g. ‘_’, ‘>’ prompts) can be drawn with priority so that changes in their position are updated ahead of other items.

A read-xor-write pattern is simple enough to implement within a Command List, so that the XOR operation is done in interrupt context. This should provide sufficient priority.

Sprites can be managed using a Surface filter layer which tracks Address Window operations. If data is written to an area occupied by a sprite, then that data can be modified (using XOR) before passing through to provide flicker-free drawing.

Repeat fills make this a little tricky though. An alternative is to identify when a sprite will be affected and, if so, remove it before drawing the new information then re-draw it afterwards. This could be implemented using Regions so that only the affect portion is updated.

Each sprite must store both its own contents (foreground), plus the original display contents (background) excluding any other sprites. Multiple overlapping are combined at point of rendering.

References
File formats
Fonts
Graphics libraries
Metafiles
Papers
API
namespace Graphics

Typedefs

using AssetType = Asset::Type
using GlyphOptions = TextOptions
using BlendMode = Blend::Mode
using DrawingTarget = Drawing::Target
using CustomObject = ObjectTemplate<Object::Kind::Custom>

Base class for a custom object.

using AssetID = uint16_t

Numeric identifiers for re-useable objects.

using Point = TPoint<int16_t>
using IntPoint = TPoint<int>
using PointF = TPoint<float>
using Range = TRange<uint16_t>
using FontStyles = BitSet<uint16_t, FontStyle, 10>

Enums

enum class BrushStyle

Values:

enumerator FullScreen
enumerator SourceLocal
enum class Color : uint32_t

Standard colour definitions.

Values:

enumerator None
enumerator XX
enumerator XX
enum ColorOrder

Order refers to colour order within bitstream.

Values:

enumerator orderRGB
enumerator orderBGR
enum class PixelFormat : uint8_t

Values:

enumerator None
enumerator XX
enum class InputEvent

Values:

enumerator move
enumerator down
enumerator up
enum class ControlEvent

Values:

enumerator activate
enumerator deactivate
enum class Orientation

Defines orientation of display.

Values:

enumerator normal
enumerator deg0
enumerator deg90
enumerator deg180
enumerator deg270
enum class Align

Values:

enumerator Near
enumerator Centre
enumerator Far
enumerator Left
enumerator Top
enumerator Center
enumerator Right
enumerator Bottom
enum class Origin

Points on a compass.

These are ordered by angles in increments of 45 degrees.

Values:

enumerator E
enumerator NE
enumerator N
enumerator NW
enumerator W
enumerator SW
enumerator S
enumerator SE
enumerator Centre
enumerator TopLeft
enumerator Top
enumerator TopRight
enumerator Left
enumerator Center
enumerator Right
enumerator BottomLeft
enumerator Bottom
enumerator BottomRight
enum class FontStyle

Values:

enumerator XX

Functions

inline constexpr uint32_t getColorValue(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255)

Obtain 24-bit colour value from red, green and blue components.

inline constexpr Color makeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255)

Function to create a custom colour.

inline constexpr Color makeColor(uint32_t color, uint8_t alpha = 255)
inline constexpr Color makeColor(Color color, uint8_t alpha)
inline constexpr uint8_t getAlpha(Color color)
inline constexpr uint8_t getRed(Color color)
inline constexpr uint8_t getGreen(Color color)
inline constexpr uint8_t getBlue(Color color)
bool fromString(const char *s, Color &color)
inline bool fromString(const String &s, Color &color)
inline uint8_t getBytesPerPixel(PixelFormat format)

Get number of bytes required to store a pixel in the given format.

PixelBuffer pack(PixelBuffer src, PixelFormat format)

Convert RGB colour into packed format.

Parameters:
  • color – The RGB colour to convert

  • format – The desired pixel format

Return values:

PackedColor

inline PackedColor pack(Color color, PixelFormat format)
PixelBuffer unpack(PixelBuffer src, PixelFormat format)

Convert packed colour into RGB.

Parameters:
  • srcPixelBuffer loaded with packed colour

  • format – The exact format of the source colour

Return values:

Color – The corresponding RGB colour

inline Color unpack(PackedColor packed, PixelFormat format)
inline Color unpack(uint32_t packed, PixelFormat format)
size_t writeColor(void *buffer, PackedColor color, PixelFormat format)

Store a packed colour value into memory.

Parameters:
  • buffer – Where to write the colour value

  • color – The value to write

Return values:

size_t – The number of bytes written

inline size_t writeColor(void *buffer, Color color, PixelFormat format)
size_t writeColor(void *buffer, PackedColor color, PixelFormat format, size_t count)

Store a block of packed colours into memory.

Parameters:
  • buffer – Where to write the colour value

  • color – The value to write

  • count – The number of times to repeat the colour

Return values:

size_t – The number of bytes written

inline size_t writeColor(void *buffer, Color color, PixelFormat format, size_t count)
size_t convert(const void *srcData, PixelFormat srcFormat, void *dstBuffer, PixelFormat dstFormat, size_t numPixels)

Convert block of data from one pixel format to another.

void highlightText(SceneObject &scene)

Highlight text segments and print details.

void highlightText(TextObject &object)
uint16_t swapBytes(uint16_t w)
uint32_t makeWord(uint16_t w1, uint16_t w2)
inline Origin opposite(Origin o)

Get the origin for the opposite side of the rectangle.

e.g. E -> W, NE -> SW

inline constexpr Size rotate(Size size, Orientation orientation)
template<typename T, typename Q>
constexpr TPoint<T> operator+(TPoint<T> pt, const Q &other)
template<typename T, typename Q>
constexpr TPoint<T> operator-(TPoint<T> pt, const Q &other)
template<typename T, typename Q>
constexpr TPoint<T> operator*(TPoint<T> pt, const Q &other)
template<typename T>
constexpr TPoint<T> operator*(TPoint<T> pt, const Size &other)
template<typename T, typename Q>
constexpr TPoint<T> operator/(TPoint<T> pt, const Q &other)
template<typename T>
constexpr TPoint<T> operator/(TPoint<T> pt, const Size &other)
template<typename T, typename Q>
constexpr TPoint<T> operator%(TPoint<T> pt, const Q &other)
template<typename T>
Rect operator+(const Rect &rect, const T &other)
inline Rect operator-(const Rect &rect, const Point &offset)
inline Rect intersect(Rect r1, const Rect &r2)
inline Region operator-(const Region &rgn, const Rect &r)
inline uint16_t originToDegrees(Origin origin)

Get corresponding angle for given origin.

Origin degreesToOrigin(uint16_t angle)

Get origin closest to given angle (expressed in degrees)

uint16_t normaliseAngle(int angle)

Make 0 <= angle < 360.

Variables

LcdFont lcdFont
static constexpr uint8_t PIN_NONE = {255}

Undefined I/O pin value.

class VirtualTouch : public Graphics::Touch
#include <VirtualTouch.h>

Public Functions

virtual Size getNativeSize() const override

Get physical size of display.

Return values:

Size – Dimensions for NORMAL orientation

inline virtual State getState() const override

Get current state.

class AbstractDisplay : public Graphics::Device, public Graphics::RenderTarget
#include <AbstractDisplay.h>

Use this class as a base for all types of display drivers.

Subclassed by Graphics::Display::NullDevice, Graphics::Display::Virtual, Graphics::SpiDisplay

struct AddressWindow
#include <AddressWindow.h>

Manages a rectangular area of display memory with position information.

Accesses to display memory is controlled by first setting an active Address Window. This is a rectangular area into which following writes (or reads) will store data.

Although the display hardware usually manages this some operations require tracking the position within the driver.

Public Functions

inline size_t getPixelCount() const

Get remaining pixels in window from current position.

Public Members

Rect bounds = {}

y and h are updated by seek()

uint16_t column = {0}

Relative x position within window.

class Asset : public LinkedObjectTemplate<Asset>, public Graphics::Meta
#include <Asset.h>

An asset is used to render an Object, but is not itself drawable.

Subclassed by Graphics::AssetTemplate< AssetType::Object >, Graphics::AssetTemplate< AssetType::Text >, Graphics::AssetTemplate< AssetType::Font >, Graphics::AssetTemplate< AssetType::Surface >, Graphics::AssetTemplate< AssetType::TextureBrush >, Graphics::AssetTemplate< AssetType::Pen >, Graphics::AssetTemplate< AssetType::Typeface >, Graphics::AssetTemplate< AssetType::SolidBrush >, Graphics::AssetTemplate< AssetType::Blend >, Graphics::AssetTemplate< asset_type >

template<Asset::Type asset_type>
class AssetTemplate : public Graphics::Asset
#include <Asset.h>
class SolidBrush : public Graphics::AssetTemplate<AssetType::SolidBrush>
#include <Asset.h>
class TextureBrush : public Graphics::AssetTemplate<AssetType::TextureBrush>
#include <Asset.h>

Subclassed by Graphics::GradientBrush, Graphics::ImageBrush

class GradientBrush : public Graphics::TextureBrush
#include <Asset.h>
class ImageBrush : public Graphics::TextureBrush
#include <Asset.h>

Brush using pixels from image.

class Brush : public Graphics::Meta
#include <Asset.h>

The source of colour for drawing.

Subclassed by Graphics::Pen

class Pen : public Graphics::Brush
#include <Asset.h>

Subclassed by Graphics::PenAsset

class PenAsset : public Graphics::AssetTemplate<AssetType::Pen>, public Graphics::Pen
#include <Asset.h>
class TextOptions : public Graphics::Meta
#include <Asset.h>
class TypeFace : public Graphics::AssetTemplate<AssetType::Typeface>
#include <Asset.h>

Base class for a loaded typeface, e.g. Sans 16pt bold.

Subclassed by Graphics::LcdTypeFace, Graphics::ResourceTypeface

Public Functions

virtual FontStyles getStyle() const = 0

Style of this typeface (bold, italic, etc.)

virtual uint8_t height() const = 0

Get height of typeface, same for all characters.

virtual uint8_t descent() const = 0

How many pixels from bottom of em-square to baseline

virtual GlyphMetrics getMetrics(char ch) const = 0

Get metrics for a character.

virtual std::unique_ptr<GlyphObject> getGlyph(char ch, const GlyphOptions &options) const = 0

Get the glyph for a character.

Caller is responsible for destroying the glyph when no longer required.

Parameters:
  • ch

  • options – Options to control how the glyph is drawn (colour, shading, etc)

Return values:

GlyphObject* – The glyph, nullptr if no glyph exists in the typeface for this character

inline uint8_t baseline() const

Get baseline relative to top of mbox.

uint16_t getTextWidth(const char *text, uint16_t length) const

Compute displayed width for a text string.

class Font : public Graphics::AssetTemplate<AssetType::Font>
#include <Asset.h>

Base class for a loaded font.

As there are various formats for defining fonts we need a consistent interface for accessing and rendering them, which this class defines.

Fonts are specified by family and size, and can be registered with various typefaces.

Subclassed by Graphics::LcdFont, Graphics::ResourceFont

class ResourceTypeface : public Graphics::TypeFace
#include <Asset.h>

Public Functions

inline virtual FontStyles getStyle() const override

Style of this typeface (bold, italic, etc.)

inline virtual uint8_t height() const override

Get height of typeface, same for all characters.

inline virtual uint8_t descent() const override

How many pixels from bottom of em-square to baseline

virtual GlyphMetrics getMetrics(char ch) const override

Get metrics for a character.

virtual std::unique_ptr<GlyphObject> getGlyph(char ch, const GlyphOptions &options) const override

Get the glyph for a character.

Caller is responsible for destroying the glyph when no longer required.

Parameters:
  • ch

  • options – Options to control how the glyph is drawn (colour, shading, etc)

Return values:

GlyphObject* – The glyph, nullptr if no glyph exists in the typeface for this character

class ResourceFont : public Graphics::Font
#include <Asset.h>
class TextAsset : public Graphics::AssetTemplate<AssetType::Text>
#include <Asset.h>
class ObjectAsset : public Graphics::AssetTemplate<AssetType::Object>
#include <Asset.h>
class AssetList : public OwnedLinkedObjectListTemplate<Asset>
#include <Asset.h>
class Blend : public Graphics::AssetTemplate<AssetType::Blend>
#include <Blend.h>

Blend operations.

See MemoryImageSurface::write

Subclassed by Graphics::BlendTemplate< BlendXor, BlendMode::Xor >, Graphics::BlendTemplate< BlendWrite, BlendMode::Write >, Graphics::BlendTemplate< BlendMask, BlendMode::Mask >, Graphics::BlendTemplate< BlendXNor, BlendMode::XNor >, Graphics::BlendTemplate< BlendAlpha, BlendMode::Alpha >, Graphics::BlendTemplate< BlendTransparent, BlendMode::Transparent >, Graphics::BlendTemplate< Class, blendMode >

template<class Class, BlendMode blendMode>
class BlendTemplate : public Graphics::Blend
#include <Blend.h>
class BlendWrite : public Graphics::BlendTemplate<BlendWrite, BlendMode::Write>
#include <Blend.h>
class BlendXor : public Graphics::BlendTemplate<BlendXor, BlendMode::Xor>
#include <Blend.h>
class BlendXNor : public Graphics::BlendTemplate<BlendXNor, BlendMode::XNor>
#include <Blend.h>
class BlendMask : public Graphics::BlendTemplate<BlendMask, BlendMode::Mask>
#include <Blend.h>
class BlendTransparent : public Graphics::BlendTemplate<BlendTransparent, BlendMode::Transparent>
#include <Blend.h>
class BlendAlpha : public Graphics::BlendTemplate<BlendAlpha, BlendMode::Alpha>
#include <Blend.h>
class SharedBuffer
#include <Buffer.h>

Shared heap-allocated data buffer.

Used for write operations with data outside Command List.

class Control
#include <Buffer.h>
struct ReadBuffer
#include <Buffer.h>

Buffer used for reading pixel data from device.

Subclassed by Graphics::ReadStatusBuffer

Public Members

SharedBuffer data

Buffer to read pixel data.

uint16 offset = {0}

Offset from start of buffer to start writing.

PixelFormat format = {}

Input: Requested pixel format, specify ‘None’ to get native format.

struct ReadStatus
#include <Buffer.h>

Stores result of read operation.

Public Members

size_t bytesRead = {0}

On completion, set to actual length of data read.

PixelFormat format = {}

Format of data.

struct ReadStatusBuffer : public Graphics::ReadBuffer
#include <Buffer.h>

Composite ReadBuffer with status.

union PixelFormatStruct
#include <Colors.h>

Public Functions

inline uint8_t byteCount() const
inline uint8_t bitsPerPixel() const
inline ColorOrder colorOrder() const

Public Members

PixelFormat format
uint8_t mByteCount
uint8_t mBitsPerPixel
uint8_t mColorOrder
struct Graphics::PixelFormatStruct::[anonymous] [anonymous]
struct PackedColor
#include <Colors.h>

Colour in device pixel format.

union PixelBuffer
#include <Colors.h>

Structure used to perform pixel format conversions.

Public Members

Color color
PackedColor packed
uint8_t u8[4]
BGRA32 bgra32
RGB24 rgb24
BGR24 bgr24
RGB565 rgb565
struct BGR24
#include <Colors.h>
struct BGRA32
#include <Colors.h>
struct RGB24
#include <Colors.h>
struct RGB565
#include <Colors.h>
class Console : public Print
#include <Console.h>

Public Functions

inline Console(AbstractDisplay &display, RenderQueue &renderQueue)

Console constructor.

Parameters:
  • display – Output device

  • renderQueue – Allows shared access to display

void systemDebugOutput(bool enable)

Use console for debug output.

Parameters:

enable – true to divert m_puts to console, false to disable debug output

void pause(bool state)

Suspend/resume output to display.

While paused, content will be buffered in RAM.

Parameters:

state – true to stop output, false to resume

inline bool isPaused() const

Determine if output is paused.

virtual size_t write(const uint8_t *data, size_t size) override

Writes characters from a buffer to output stream.

Parameters:
  • buffer – Pointer to character buffer

  • size – Quantity of characters to write

Return values:

size_t – Quantity of characters written to stream

inline virtual size_t write(uint8_t c) override

Writes a single character to output stream.

Parameters:

c – Character to write to output stream

Return values:

size_t – Quantity of characters written to output stream

class Button : public Graphics::Control
#include <Button.h>

Basic interactive button on screen.

Responsible for drawing area within designated rectangle

class Control : public Graphics::ObjectTemplate<object_kind>
#include <Control.h>

Basic interactive button on screen.

Responsible for drawing area within designated rectangle

Subclassed by Graphics::Button, Graphics::Label, Graphics::TimeClock

Public Functions

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

class List : public LinkedObjectListTemplate<Control>
#include <Control.h>
class Label : public Graphics::Control
#include <Label.h>

Non-interactive text label.

class Screen
#include <Screen.h>

Public Types

using DrawMethod = Delegate<bool(SceneObject &scene)>

Invoked when screen is drawn.

Param scene:

Where to compose screen

Retval bool:

Return true to continue default processing

using ControlMethod = Delegate<bool(ControlEvent event, Control &control)>

Invoked in response to user input.

Param event:

Param control:

Retval bool:

Return true to continue default processing

class TimeClock : public Graphics::Control
#include <TimeClock.h>
struct HMS
#include <TimeClock.h>
class Device
#include <Device.h>

A physical display device.

Subclassed by Graphics::AbstractDisplay

Public Functions

virtual String getName() const = 0

Get name of display.

virtual bool setOrientation(Orientation orientation) = 0

Set display orientation.

virtual Size getNativeSize() const = 0

Get physical size of display.

Return values:

Size – Dimensions for NORMAL orientation

inline Orientation getOrientation()

Get current display orientation.

virtual bool setScrollMargins(uint16_t top, uint16_t bottom) = 0

Set margins for hardware scrolling.

Area between top/bottom can be scrolled using scroll() method.

Parameters:
  • top – Number of fixed pixels at top of screen

  • bottom – Number of fixed pixels at bottom of screen

virtual bool scroll(int16_t y) = 0

Scroll region of display up or down using hardware scrolling.

Parameters:

y – Number of lines to scroll. Positive values scroll content down, negative values scroll up.

struct FillInfo
#include <DisplayList.h>

Supports DisplayList blend operations.

See also

See DisplayList::fill

class DisplayList
#include <DisplayList.h>

Stores list of low-level display commands.

Used by hardware surfaces to efficiently buffer commands which are then executed in interrupt context.

The ILI9341 4-wire SPI mode is awkard to use so to allow more efficient access the command and data information is buffered as various types of ‘chunk’ using this class. A single HSPI request packet is used for all requests and is re-filled in interrupt context from this list.

Count values (data length) are stored as either 1 or 2 bytes. Values less than 0x80 bytes are stored in 1 byte, values from 0x0080 to 0x7fff are stored MSB first with the top bit set, followed by the LSB. For example, 0x1234 would be stored as 0x92 0x34.

Commands are stored in 1 byte. To allow use of this class with other displays may require adjusting this, perhaps converting this into a class template to accommodate these differences more efficiently.

  • Standard chunk. Contains a display-specific command byte followed by optional data.

    • command (1 byte)

    • data length (1 or 2 bytes)

    • data (variable length)

  • Data chunk (no command).

    • COMMAND_DATA (1 byte)

    • data length (1 or 2 bytes)

    • data (variable length)

  • Repeated data chunk (no command). Used to perform colour fills.

    • COMMAND_DATA_REPEAT (1 byte)

    • data length (1 or 2 bytes)

    • repeat count (1 or 2 bytes)

    • data (variable length)

  • Read command. Defines a single read command packet. Reading is particularly awkward on the ILI9341 as it ‘forgets’ the read position after each read packet. Reading a block of display memory therefore requires the address window to be set immediately before the RAMRD instruction. The final block in the sequence is a CALLBACK command.

    • COMMAND_READ (1 byte)

    • data length (1 or 2 bytes)

    • command (1 byte) The display-specific command to issue

    • buffer address (4 bytes)

  • Callback. Invokes a callback function (see Callback type). Note that the single parameter points to a copy of the original data which is stored in the list.

    • COMMAND_CALLBACK (1 byte)

    • parameter data length (1 or 2 bytes)

    • callback function pointer (4 bytes)

    • callback parameters (variable)

Subclassed by Graphics::SpiDisplayList

Public Types

enum CodeArgLengths

Obtain maximum size for command, not including variable data which may be added.

Used to check space before starting a command sequence. Actual space used may be less than this.

Values:

enumerator XX
using Callback = void (*)(void *parameterData)

Queued callback.

The parameter data is copied so the caller does not need to allocate memory dynamically. The data must start on a 32-bit word boundary.

Param parameterData:

A copy of the original parameter data

Public Functions

inline DisplayList(AddressWindow &addrWindow, size_t bufferSize)
Parameters:
  • commands – Codes corresponding to specific display commands

  • addrWindow – Reference to current device address window, synced between lists

  • bufferSize

inline DisplayList(AddressWindow &addrWindow, const FSTR::ObjectBase &data)

Create pre-defined display list from flash data.

Used for initialisation data

inline DisplayList(AddressWindow &addrWindow, const void *data, size_t length)

Create initialised display list from RAM data.

Used for initialisation data

void reset()

Reset the display list ready for re-use List MUST NOT be in use!

inline bool isEmpty() const

Determine if any commands have been stored for execution.

inline uint16_t freeSpace() const

Get number of bytes remaining in buffer.

inline uint16_t readOffset() const

Get current read position.

inline uint16_t used() const

Get number of bytes stored in buffer.

inline const uint8_t *getContent() const

Get read-only pointer to start of buffer.

uint8_t *getBuffer(uint16_t &available)

Get some space in the list to write pixel data.

Call commit after the data has been written

Parameters:

available – (OUT) How much space is available

Return values:

uint8_t* – Where to write data

inline uint8_t *getBuffer(uint16_t minBytes, uint16_t &available)

Get some space in the list to write pixel data.

Call commit after the data has been written

Parameters:
  • minBytes – Minimum bytes required

  • available – (OUT) How much space is available

Return values:

uint8_t* – Where to write data, nullptr if required space not available

void commit(uint16_t length)

Commit block of data to the list.

Note

MUST NOT call with more data than returned from available in getBuffer call.

Parameters:

length – How many bytes have been written

inline bool writeCommand(uint8_t command, uint32_t data, uint8_t length)

Write command with 1-4 bytes of parameter data.

bool writeCommand(uint8_t command, const void *data, uint16_t length)

Write command with variable amount of parameter data.

bool writeData(const void *data, uint16_t length)

Add WRITE command plus data.

bool writeDataBuffer(SharedBuffer &data, size_t offset, uint16_t length)

Add WRITE command plus external data.

Parameters:

data – Will be locked until display list has been executed

bool blockFill(const void *data, uint16_t length, uint32_t repeat)

Perform a block fill operation with repeat, e.g. multiple pixel fill or repeated pattern.

bool setAddrWindow(const Rect &rect)

Set window for read/write operations.

bool setPixel(PackedColor color, uint8_t bytesPerPixel, Point pt)

Set a single pixel.

bool readMem(void *buffer, uint16_t length)

Read a block of display memory.

bool writeCallback(Callback callback, void *params, uint16_t paramLength)

Request a callback.

The callback will be invoked (in interrupt context) when read from the display list

Parameters:
  • callback – Callback function to invoke

  • params – Parameter data, will be stored in list

  • paramLengthSize of parameter data

bool fill(const Rect &rect, PackedColor color, uint8_t bytesPerPixel, FillInfo::Callback callback)

Perform a block fill operation with blending.

Performs a read/blend/write operation. Use to perform transparent fills or other blend operations on small regions of display memory.

Parameters:
  • rect – Area to fill

  • color

  • bytesPerPixel

  • callback – Invoked in interrupt context to perform blend operation

inline bool canLockBuffer()

Enforce maximum number of locked buffers to conserve memory.

Return values:

bool – true if call to lockBuffer() will succeed

bool lockBuffer(SharedBuffer &buffer)

Lock a shared buffer by storing a reference to it. This will be released when reset() is called.

inline bool require(uint16_t length)

Check if list has space for the given number of bytes.

Return values:

bool – true if there’s room

bool readEntry(Entry &info)

Read next command list entry.

Used by virtual display and for debugging. SpiDisplayList uses a different mechanism for reading.

Parameters:

info

Return values:

bool – false if there are no more commands

inline void prepare(Callback callback, void *param)

Prepare for playback.

Parameters:
  • callback – Invoked when playback has completed, in task context

  • param – Parameter passed to callback

struct Entry
#include <DisplayList.h>

Values returned from readEntry

union Header
#include <DisplayList.h>

Each list entry starts with a header.

This information is interpreted by the display driver but should translate to a single display command.

Each command has fixed content with optional variable data. Length can be packed with command (0-31), or an additional uint8_t or uint16_t.

setPixel(uint8_t x, uint8_t y, void* data, uint8_t len) COL(uint8_t x, uint8_t len) ROW(uint8_t y, 1) DAT data, len

setPixel(uint16_t x, uint8_t y, void* data, uint16_t len) COL(uint16_t x, uint16_t len) ROW(uint8_t y, 1) DAT data, len

fillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, data len) COL16 x, w ROW16 y, h

Public Members

Code code
uint8_t len
struct Graphics::DisplayList::Header::[anonymous] [anonymous]
uint8_t u8

Public Static Attributes

static constexpr uint8_t lenMax = {15}
class ImageSurface : public Graphics::Surface
#include <ImageSurface.h>

Virtual class to access an image as a Surface.

Use to create off-screen bitmaps by drawing or copying areas from display memory.

Subclassed by Graphics::FileImageSurface, Graphics::MemoryImageSurface

Public Functions

virtual int readDataBuffer(ReadBuffer &buffer, ReadStatus *status, ReadCallback callback, void *param) override

Read some pixels.

Call setAddrWindow to set up region to be read. Returns true when all pixels have been queued for reading.

Parameters:
  • buffer – Details requested format and buffer to read

  • status – Optional. Stores result of read operation.

  • callback – Optional. Invoked when read has completed

  • param – Parameters passed to callback

Return values:

int – Number of pixels queued for reading (or read); 0 if no further pixels to read, < 0 to try again later

inline virtual void reset() override

Reset surface ready for more commands.

virtual bool present(PresentCallback callback, void *param) override

Present surface to display device.

Hardware devices will queue buffered commands to the display device then return. The surface will be marked as BUSY. Attempting to call present() on a BUSY surface must return true. If surface is EMPTY (no buffered commands), must return false.

Parameters:
  • callback – Invoked when surface is available for more commands

  • param – Passed to callback

Return values:

bool – true If callback has been queued, false if surface is empty

class MemoryImageSurface : public Graphics::ImageSurface
#include <ImageSurface.h>

Image surface using RAM as backing store.

Useful for sprites, etc.

class FileImageSurface : public Graphics::ImageSurface
#include <ImageSurface.h>

Image surface using filing system as backing store.

Slower than RAM but size is unrestricted. Use for constructing complex scenes for faster drawing.

class LcdGlyph : public Graphics::GlyphObject
#include <LcdFont.h>

Public Functions

inline virtual bool init() override

Initialise the object, e.g. parse header content and obtain dimensions.

virtual void readAlpha(void *buffer, Point origin, size_t stride) const override

Obtain glyph information as block of 8-bit alpha values.

This method is called with a positive origin to accommodate negative x/y glyph offsets. Italic and script typefaces do this a lot!

Parameters:
  • buffer

  • originLocation of cursor within buffer

  • stride – Number of bytes per row in buffer

class LcdTypeFace : public Graphics::TypeFace
#include <LcdFont.h>

Public Functions

inline virtual FontStyles getStyle() const override

Style of this typeface (bold, italic, etc.)

inline virtual uint8_t height() const override

Get height of typeface, same for all characters.

inline virtual uint8_t descent() const override

How many pixels from bottom of em-square to baseline

inline virtual GlyphObject::Metrics getMetrics(char ch) const override

Get metrics for a character.

virtual std::unique_ptr<GlyphObject> getGlyph(char ch, const GlyphObject::Options &options) const override

Get the glyph for a character.

Caller is responsible for destroying the glyph when no longer required.

Parameters:
  • ch

  • options – Options to control how the glyph is drawn (colour, shading, etc)

Return values:

GlyphObject* – The glyph, nullptr if no glyph exists in the typeface for this character

class LcdFont : public Graphics::Font
#include <LcdFont.h>
class Meta
#include <Meta.h>

Empty base class to support object enumeration Non-virtual to avoid bloat.

Subclassed by Graphics::Asset, Graphics::Brush, Graphics::Object, Graphics::TextObject::Element, Graphics::TextOptions

class MetaWriter
#include <Meta.h>

Writes object content in readable format for debugging.

class MipiDisplay : public Graphics::SpiDisplay
#include <MipiDisplay.h>

Subclassed by Graphics::Display::ILI9341, Graphics::Display::ST7789V

Public Functions

inline void setNativeSize(Size screenSize)

Sets the screen size. Must be called before calling begin()

inline virtual Size getNativeSize() const override

Get physical size of display.

Return values:

Size – Dimensions for NORMAL orientation

virtual bool setScrollMargins(uint16_t top, uint16_t bottom) override

Set margins for hardware scrolling.

Area between top/bottom can be scrolled using scroll() method.

Parameters:
  • top – Number of fixed pixels at top of screen

  • bottom – Number of fixed pixels at bottom of screen

virtual bool scroll(int16_t y) override

Scroll region of display up or down using hardware scrolling.

Parameters:

y – Number of lines to scroll. Positive values scroll content down, negative values scroll up.

virtual bool setOrientation(Orientation orientation) override

Set display orientation.

inline virtual Size getSize() const override

Get target dimensions.

inline virtual PixelFormat getPixelFormat() const override

All surfaces support the same pixel format.

virtual Surface *createSurface(size_t bufferSize = 0) override

Create a surface for use with this render target.

Caller is responsible for destroying the surface when no longer required.

Parameters:

bufferSizeSize of internal command/data buffer

Return values:

Surface* – The surface to use

class MipiSurface : public Graphics::Surface
#include <MipiDisplay.h>

Public Functions

inline virtual void reset() override

Reset surface ready for more commands.

virtual int readDataBuffer(ReadBuffer &buffer, ReadStatus *status, ReadCallback callback, void *param) override

Read some pixels.

Call setAddrWindow to set up region to be read. Returns true when all pixels have been queued for reading.

Parameters:
  • buffer – Details requested format and buffer to read

  • status – Optional. Stores result of read operation.

  • callback – Optional. Invoked when read has completed

  • param – Parameters passed to callback

Return values:

int – Number of pixels queued for reading (or read); 0 if no further pixels to read, < 0 to try again later

virtual bool render(const Object &object, const Rect &location, std::unique_ptr<Renderer> &renderer) override

Start rendering an object.

Surfaces may override this method to implement alternative rendering using specific hardware features of the display device.

Software renderers should be run by calling surface::execute.

Parameters:
  • object – What to render

  • location – Placement information

  • renderer – If operation cannot be completed in hardware, create a renderer instance to manage the process

Return values:

Return – true on success, false to retry later

virtual bool present(PresentCallback callback, void *param) override

Present surface to display device.

Hardware devices will queue buffered commands to the display device then return. The surface will be marked as BUSY. Attempting to call present() on a BUSY surface must return true. If surface is EMPTY (no buffered commands), must return false.

Parameters:
  • callback – Invoked when surface is available for more commands

  • param – Passed to callback

Return values:

bool – true If callback has been queued, false if surface is empty

class Renderer : public LinkedObjectTemplate<Renderer>
#include <Object.h>

Virtual base class to manage rendering of various types of information to a surface.

Subclassed by Graphics::BlendRenderer, Graphics::CircleRenderer, Graphics::CopyRenderer, Graphics::EllipseRenderer, Graphics::FilledCircleRenderer, Graphics::FilledEllipseRenderer, Graphics::FilledRectRenderer, Graphics::FilledRoundedRectRenderer, Graphics::GfxLineRenderer, Graphics::ImageRenderer, Graphics::LineRenderer, Graphics::MultiRenderer, Graphics::PolylineRenderer, Graphics::RectRenderer, Graphics::RoundedRectRenderer, Graphics::ScrollRenderer, Graphics::SurfaceRenderer, Graphics::TextRenderer

Public Functions

inline Renderer(const Location &location)

Constructor.

Parameters:

locationLocation information

virtual bool execute(Surface &surface) = 0

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class Object : public LinkedObjectTemplate<Object>, public Graphics::Meta
#include <Object.h>

A drawable object inherits from this virtual base class.

Subclassed by Graphics::ObjectTemplate< Object::Kind::FilledCircle >, Graphics::ObjectTemplate< Object::Kind::Reference >, Graphics::ObjectTemplate< Object::Kind::Line >, Graphics::ObjectTemplate< Object::Kind::Rect >, Graphics::ObjectTemplate< Object::Kind::FilledEllipse >, Graphics::ObjectTemplate< Object::Kind::Ellipse >, Graphics::ObjectTemplate< Object::Kind::FilledArc >, Graphics::ObjectTemplate< Object::Kind::Arc >, Graphics::ObjectTemplate< Object::Kind::Surface >, Graphics::ObjectTemplate< Object::Kind::FilledRect >, Graphics::ObjectTemplate< Object::Kind::Drawing >, Graphics::ObjectTemplate< Object::Kind::Polyline >, Graphics::ObjectTemplate< Object::Kind::Point >, Graphics::ObjectTemplate< Object::Kind::Copy >, Graphics::ObjectTemplate< Object::Kind::Circle >, Graphics::ObjectTemplate< Object::Kind::Text >, Graphics::ObjectTemplate< Object::Kind::Image >, Graphics::ObjectTemplate< Object::Kind::Scroll >, Graphics::ObjectTemplate< object_kind >, Graphics::SceneObject

Public Functions

virtual Renderer *createRenderer(const Location &location) const = 0

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

template<Object::Kind object_kind>
class ObjectTemplate : public Graphics::Object
#include <Object.h>

Subclassed by Graphics::Control

class ReferenceObject : public Graphics::ObjectTemplate<Object::Kind::Reference>
#include <Object.h>

Reference to another object.

Objects are owned by a Scene, so this allows objects to be re-used in multiple scenes, or to other classes which expose an Object interface.

Public Functions

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

class PointObject : public Graphics::ObjectTemplate<Object::Kind::Point>
#include <Object.h>

A single pixel == 1x1 rectangle.

Public Functions

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

class RectObject : public Graphics::ObjectTemplate<Object::Kind::Rect>
#include <Object.h>

A rectangular outline.

Public Functions

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

class FilledRectObject : public Graphics::ObjectTemplate<Object::Kind::FilledRect>
#include <Object.h>

A filled rectangle.

Public Functions

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

class LineObject : public Graphics::ObjectTemplate<Object::Kind::Line>
#include <Object.h>

A drawn line.

Public Functions

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

class PolylineObject : public Graphics::ObjectTemplate<Object::Kind::Polyline>
#include <Object.h>

A sequence of lines.

By default, lines are connected. This is used to draw rectangles, triangles or any other type of polygon. A line is drawn between points 0-1, 1-2, 2-3, etc.

Setting the connected property to false allows the lines to be discontinuous, so a line is drawn between points 0-1, 2-3, 3-4, etc.

Public Functions

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

class CircleObject : public Graphics::ObjectTemplate<Object::Kind::Circle>
#include <Object.h>

A circle outline.

Public Functions

inline Rect getRect() const

Get bounding retangle for this circle.

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

class FilledCircleObject : public Graphics::ObjectTemplate<Object::Kind::FilledCircle>
#include <Object.h>

A filled circle.

Public Functions

inline Rect getRect() const

Get bounding retangle for this circle.

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

class EllipseObject : public Graphics::ObjectTemplate<Object::Kind::Ellipse>
#include <Object.h>

An ellipse outline.

Public Functions

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

class FilledEllipseObject : public Graphics::ObjectTemplate<Object::Kind::FilledEllipse>
#include <Object.h>

A filled ellipse.

Public Functions

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

class ArcObject : public Graphics::ObjectTemplate<Object::Kind::Arc>
#include <Object.h>

An arc outline.

Public Functions

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

class FilledArcObject : public Graphics::ObjectTemplate<Object::Kind::FilledArc>
#include <Object.h>

A filled arc.

Public Functions

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

class ImageObject : public Graphics::ObjectTemplate<Object::Kind::Image>
#include <Object.h>

Virtual base class for an image.

Subclassed by Graphics::GlyphObject, Graphics::StreamImageObject

Public Functions

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

virtual bool init() = 0

Initialise the object, e.g. parse header content and obtain dimensions.

virtual PixelFormat getPixelFormat() const = 0

Get native pixel format.

Return values:

PixelFormat – Return None if ambivalent about format (e.g. calculated pixel data)

virtual size_t readPixels(const Location &loc, PixelFormat format, void *buffer, uint16_t width) const = 0

Read pixels in requested format.

Parameters:
  • loc – Start position

  • format – Required pixel format

  • buffer – Buffer for pixels

  • width – Number of pixels to read

Return values:

size_t – Number of bytes written

class StreamImageObject : public Graphics::ImageObject
#include <Object.h>

Image whose contents are stored in a stream, typically in a file or flash memory.

Subclassed by Graphics::BitmapObject, Graphics::RawImageObject

class BitmapObject : public Graphics::StreamImageObject
#include <Object.h>

A BMP format image.

Code based on https://github.com/adafruit/Adafruit-GFX-Library

Public Functions

virtual bool init() override

Initialise the object, e.g. parse header content and obtain dimensions.

inline virtual PixelFormat getPixelFormat() const override

Get native pixel format.

Return values:

PixelFormat – Return None if ambivalent about format (e.g. calculated pixel data)

virtual size_t readPixels(const Location &loc, PixelFormat format, void *buffer, uint16_t width) const override

Read pixels in requested format.

Parameters:
  • loc – Start position

  • format – Required pixel format

  • buffer – Buffer for pixels

  • width – Number of pixels to read

Return values:

size_t – Number of bytes written

class RawImageObject : public Graphics::StreamImageObject
#include <Object.h>

Image stored as raw pixels in a specific format.

Use images stored in native display format for best performance as no conversion is required.

Subclassed by Graphics::FileImageObject, Graphics::MemoryImageObject

Public Functions

inline virtual bool init() override

Initialise the object, e.g. parse header content and obtain dimensions.

inline virtual PixelFormat getPixelFormat() const override

Get native pixel format.

Return values:

PixelFormat – Return None if ambivalent about format (e.g. calculated pixel data)

virtual size_t readPixels(const Location &loc, PixelFormat format, void *buffer, uint16_t width) const override

Read pixels in requested format.

Parameters:
  • loc – Start position

  • format – Required pixel format

  • buffer – Buffer for pixels

  • width – Number of pixels to read

Return values:

size_t – Number of bytes written

class RenderTarget
#include <Object.h>

Interface for objects which support writing via surfaces.

Subclassed by Graphics::AbstractDisplay, Graphics::FileImageObject, Graphics::MemoryImageObject

Public Functions

virtual Size getSize() const = 0

Get target dimensions.

virtual PixelFormat getPixelFormat() const = 0

All surfaces support the same pixel format.

virtual Surface *createSurface(size_t bufferSize = 0) = 0

Create a surface for use with this render target.

Caller is responsible for destroying the surface when no longer required.

Parameters:

bufferSizeSize of internal command/data buffer

Return values:

Surface* – The surface to use

class MemoryImageObject : public Graphics::RawImageObject, public Graphics::RenderTarget
#include <Object.h>

Public Functions

inline virtual Size getSize() const override

Get target dimensions.

inline virtual PixelFormat getPixelFormat() const override

Get native pixel format.

Return values:

PixelFormat – Return None if ambivalent about format (e.g. calculated pixel data)

inline virtual Surface *createSurface(size_t bufferSize = 0) override

Create a surface for use with this render target.

Caller is responsible for destroying the surface when no longer required.

Parameters:

bufferSizeSize of internal command/data buffer

Return values:

Surface* – The surface to use

class FileImageObject : public Graphics::RawImageObject, public Graphics::RenderTarget
#include <Object.h>

Public Functions

inline virtual Size getSize() const override

Get target dimensions.

inline virtual PixelFormat getPixelFormat() const override

Get native pixel format.

Return values:

PixelFormat – Return None if ambivalent about format (e.g. calculated pixel data)

virtual Surface *createSurface(size_t bufferSize = 0) override

Create a surface for use with this render target.

Caller is responsible for destroying the surface when no longer required.

Parameters:

bufferSizeSize of internal command/data buffer

Return values:

Surface* – The surface to use

class GlyphObject : public Graphics::ImageObject
#include <Object.h>

A character glyph image.

Characters are accessed like regular images but there are some specialisations which may be necessary which this class exposes:

  • To support devices with custom renderers we require access to the raw monochrome bits

  • Images currently don’t support transparency, although this could be enabled using a flag bits in the PackedColor format. The display driver or renderer would then need to interpret this flag and render accordingly. An alternative method is to nominate a transparent colour.

Subclassed by Graphics::LcdGlyph

Public Functions

inline virtual PixelFormat getPixelFormat() const override

Get native pixel format.

Return values:

PixelFormat – Return None if ambivalent about format (e.g. calculated pixel data)

virtual size_t readPixels(const Location &loc, PixelFormat format, void *buffer, uint16_t width) const override

Read pixels in requested format.

Parameters:
  • loc – Start position

  • format – Required pixel format

  • buffer – Buffer for pixels

  • width – Number of pixels to read

Return values:

size_t – Number of bytes written

virtual void readAlpha(void *buffer, Point origin, size_t stride) const = 0

Obtain glyph information as block of 8-bit alpha values.

This method is called with a positive origin to accommodate negative x/y glyph offsets. Italic and script typefaces do this a lot!

Parameters:
  • buffer

  • originLocation of cursor within buffer

  • stride – Number of bytes per row in buffer

class TextObject : public Graphics::ObjectTemplate<Object::Kind::Text>
#include <Object.h>

A block of text consisting of zero or more segments.

A segment is a straight run of text using a specific typeface and style.

Public Functions

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

class ColorElement : public Graphics::TextObject::Element
#include <Object.h>
class Element : public LinkedObjectTemplate<Element>, public Graphics::Meta
#include <Object.h>

Subclassed by Graphics::TextObject::ColorElement, Graphics::TextObject::FontElement, Graphics::TextObject::RunElement, Graphics::TextObject::TextElement

class FontElement : public Graphics::TextObject::Element
#include <Object.h>
class RunElement : public Graphics::TextObject::Element
#include <Object.h>
class TextElement : public Graphics::TextObject::Element
#include <Object.h>
class SurfaceObject : public Graphics::ObjectTemplate<Object::Kind::Surface>
#include <Object.h>

Describes a target surface and corresponding source location.

Used in surface copy operations to read display memory contents.

Public Functions

inline SurfaceObject(Surface &surface, const Rect &dest, Point source)

Constructor.

Parameters:
  • surface

  • dest – Where on the surface to write

  • source – Start position of source

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

class CopyObject : public Graphics::ObjectTemplate<Object::Kind::Copy>
#include <Object.h>

Describes a copy operation within the same surface.

Public Functions

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

class ScrollObject : public Graphics::ObjectTemplate<Object::Kind::Scroll>
#include <Object.h>

Describes a scrolling operation.

Public Functions

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

class DrawingObject : public Graphics::ObjectTemplate<Object::Kind::Drawing>
#include <Object.h>

A collection of line and curve drawing operations.

Stored in a compact format which can be streamed.

Public Functions

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

template<typename T>
class ItemList
#include <Renderer.h>

Fixed list of types.

Rendering algorithms create small sets of points, lines or rectangles. Buffering these in a small list simplifies logic considerably.

class PointList : public Graphics::ItemList<Point>
#include <Renderer.h>

Small list of points for drawing.

Algorithms generate multiple points within a loop so buffering them in a list simplifies logic considerably.

Public Functions

bool render(Surface &surface)

Render each point.

Parameters:

surface

Return values:

bool – true if all points have been rendered, false if surface is full

class RectList : public Graphics::ItemList<Rect>
#include <Renderer.h>

Small list of rectangles, similar to PointList.

Subclassed by Graphics::ArcRectList

class MultiRenderer : public Graphics::Renderer
#include <Renderer.h>

Base class to render multiple objects.

Subclassed by Graphics::Drawing::Renderer, Graphics::RenderQueue, Graphics::SceneRenderer

Public Functions

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

inline Renderer(const Location &location)

Constructor.

Parameters:

locationLocation information

class SceneRenderer : public Graphics::MultiRenderer
#include <Renderer.h>

A scene is a list of other objects, so we just iterate through the list and draw each in turn.

Rendering is performed by calling Surface::render(). Surfaces are provided by devices so may be able to provide optimised renderers for their hardware.

class GfxLineRenderer : public Graphics::Renderer
#include <Renderer.h>

Draws 1-pixel lines.

Code based on https://github.com/adafruit/Adafruit-GFX-Library

Public Functions

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class LineRenderer : public Graphics::Renderer
#include <Renderer.h>

Draws lines.

See http://enchantia.com/graphapp/

Public Functions

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class PolylineRenderer : public Graphics::Renderer
#include <Renderer.h>

Draws series of lines defined by a PolylineObject

Public Functions

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class RectRenderer : public Graphics::Renderer
#include <Renderer.h>

Draws a rectangle as a polyline.

Public Functions

inline virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class FilledRectRenderer : public Graphics::Renderer
#include <Renderer.h>

Draws a filled rectangle.

To accommodate transparency, etc.

Public Functions

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class RoundedRectRenderer : public Graphics::Renderer
#include <Renderer.h>

Draws a rectangle outline with rounded corners.

Code based on https://github.com/adafruit/Adafruit-GFX-Library

Public Functions

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class FilledRoundedRectRenderer : public Graphics::Renderer
#include <Renderer.h>

Draws a filled rectangle with rounded corners.

Code based on https://github.com/adafruit/Adafruit-GFX-Library

Public Functions

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class CircleRenderer : public Graphics::Renderer
#include <Renderer.h>

Draws a circle outline.

Code based on https://github.com/adafruit/Adafruit-GFX-Library

Public Functions

inline CircleRenderer(const Location &location, const Pen &pen, Point centre, uint16_t radius, uint16_t delta, uint8_t corners)

Used to draw corners only.

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class FilledCircleRenderer : public Graphics::Renderer
#include <Renderer.h>

Draws a filled circle.

Code based on https://github.com/adafruit/Adafruit-GFX-Library

Public Functions

inline FilledCircleRenderer(const Location &location, const Brush &brush, Point centre, uint16_t radius, uint16_t delta, uint8_t quadrants)

Used to draw rounded parts of a rounded rectangle These are handled by drawing lines between the left/right corners.

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

struct Ellipse
#include <Renderer.h>

State information for tracing an ellipse outline.

class ArcRectList : public Graphics::RectList
#include <Renderer.h>
class EllipseRenderer : public Graphics::Renderer
#include <Renderer.h>

Draws an ellipse outline.

Uses McIlroy’s Ellipse Algorithm

See http://enchantia.com/graphapp/doc/tech/ellipses.html

Subclassed by Graphics::ArcRenderer

Public Functions

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class FilledEllipseRenderer : public Graphics::Renderer
#include <Renderer.h>

Draws a filled ellipse.

See http://enchantia.com/graphapp/doc/tech/ellipses.html

Subclassed by Graphics::FilledArcRenderer

Public Functions

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class ArcRenderer : public Graphics::EllipseRenderer
#include <Renderer.h>

Render arc outline with adjustable line width.

Public Functions

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class FilledArcRenderer : public Graphics::FilledEllipseRenderer
#include <Renderer.h>

Render arc outline with adjustable line width.

Public Functions

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class ImageRenderer : public Graphics::Renderer
#include <Renderer.h>

Render an image object.

Public Functions

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class SurfaceRenderer : public Graphics::Renderer
#include <Renderer.h>

Copy an area to another surface.

Typically used to copy display memory into RAM

Public Functions

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class CopyRenderer : public Graphics::Renderer
#include <Renderer.h>

Copy an area within the same surface.

Subclassed by Graphics::ImageCopyRenderer

Public Functions

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

inline Renderer(const Location &location)

Constructor.

Parameters:

locationLocation information

class ImageCopyRenderer : public Graphics::CopyRenderer
#include <Renderer.h>
class ScrollRenderer : public Graphics::Renderer
#include <Renderer.h>

Scroll an area.

Public Functions

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class BlendRenderer : public Graphics::Renderer
#include <Renderer.h>

Perform blending with draw.

Public Functions

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class TextRenderer : public Graphics::Renderer
#include <Renderer.h>

Draw a line of text.

If foreground and background colours are the same then the text is renderered transparently.

Public Functions

virtual bool execute(Surface &surface) override

Called to do some writing to the surface.

Return values:

bool – true when rendering is complete, false if more work to be done

class RenderQueue : private Graphics::MultiRenderer
#include <RenderQueue.h>

Top-level manager to queue objects for rendering to a specific target.

Use this to render single objects, typically Scenes or Drawings

Public Functions

inline RenderQueue(RenderTarget &target, uint8_t surfaceCount = 2, size_t bufferSize = 0)

Constructor.

Surfaces are created by the target display device.

For minimum RAM usage use a single surface.

For best performance use two, so one can be prepared whilst the other is being written to the screen.

The RenderQueue owns these surfaces.

Parameters:
  • target – Where to render scenes

  • bufferSizeSize of each allocated surface buffer. Specify 0 to use default.

  • surfaceCount – Number of surfaces to allocate

template<typename T>
inline void render(T *object, const Location &location, typename T::Callback callback = nullptr, uint16_t delayMs = 0)

Add object to the render queue and start rendering if it isn’t already.

Parameters:
  • object – Scene, Drawing, etc. to render

  • location – Where to draw the object

  • callback – Optional callback to invoke when render is complete

  • delayMs – Delay between render completion and callback

class SceneObject : public Graphics::Object
#include <Scene.h>

A Scene containing multiple objects.

Public Functions

virtual Renderer *createRenderer(const Location &location) const override

Create a software renderer for this object.

Return nullptr if object cannot/should not be rendered

Parameters:

location

Return values:

renderer – Returned renderer object

template<typename T>
inline T *addObject(T *obj)

Add a new object to the scene.

Use this method to add custom objects. To draw an object multiple times use drawObject which will add a reference instead.

Parameters:

obj – This will be owned by the scene

inline void reset(Size size)

Reset the scene with a new size.

inline void clear(const Brush &brush = Color::Black)

Clear the scene and fill with a chosen colour.

inline CopyObject *copy(const Rect &source, Point dest)

Copy region of display to another.

Parameters:
  • source – Area to copy

  • dest – Top-left corner to copy to

inline ScrollObject *scroll(const Rect &area, int16_t cx, int16_t cy, bool wrapx = false, bool wrapy = false, Color fill = Color::None)

Scroll display memory.

Parameters:
  • areaRegion to scroll

  • cx – Distance to scroll horizontally

  • cy – Distance to scroll vertically

  • wrapx – true to scroll, false to clip in X direction

  • wrapx – Y scroll/clip

  • fill – Optional color to fill in clip mode

class SpiDisplay : protected HSPI::Device, public Graphics::AbstractDisplay
#include <SpiDisplay.h>

Subclassed by Graphics::MipiDisplay

Public Functions

inline virtual HSPI::IoModes getSupportedIoModes() const override

Return set of IO modes supported by a device implementation.

class SpiDisplayList : public Graphics::DisplayList
#include <SpiDisplayList.h>

Display list for hardware SPI devices.

A single HSPI request packet is used for all requests and is re-filled in interrupt context from this list.

Public Functions

bool fillRequest()

Called from interrupt context to re-fill the SPI request packet.

Return values:

bool – true on success, false if there are no further requests

Public Members

HSPI::Request request

The HSPI request packet used by this list.

struct Commands
#include <SpiDisplayList.h>

Commonly-used display-specific command codes.

Short codes are used to represent these commands. Other commands are stored directly in the list.

class WriteStream
#include <Stream.h>
class ReadStream
#include <Stream.h>
class SubStream : public IDataSourceStream
#include <Stream.h>

Public Functions

inline virtual int available() override

Return the total length of the stream.

Return values:

int – -1 is returned when the size cannot be determined

inline virtual uint16_t readMemoryBlock(char *data, int bufSize) override

Read a block of memory.

Todo:

Should IDataSourceStream::readMemoryBlock return same data type as its bufSize param?

Parameters:
  • data – Pointer to the data to be read

  • bufSize – Quantity of chars to read

Return values:

uint16_t – Quantity of chars read

inline virtual int seekFrom(int offset, SeekOrigin origin) override

Change position in stream.

Note

This method is implemented by streams which support random seeking, such as files and memory streams.

Parameters:
  • offset

  • origin

Return values:

New – position, < 0 on error

inline virtual bool isFinished() override

Check if all data has been read.

Return values:

bool – True on success.

class Surface : public Graphics::AssetTemplate<AssetType::Surface>
#include <Surface.h>

Interface for a drawing surface.

Represents a rectangular area of pixels which can be read or written.

A display device has at least one of these, representing the primary display area. More complex devices with large amounts of display memory may allow additional surfaces to be used to perform screen updates by ‘flipping’ (switching active surface) or fast copies using display hardware.

Subclassed by Graphics::ImageSurface, Graphics::MipiSurface

Public Types

using ReadCallback = void (*)(ReadBuffer &data, size_t length, void *param)

Callback for readPixel() operations.

Param buffer:

Buffer passed to readPixel() call

Public Functions

virtual int readDataBuffer(ReadBuffer &buffer, ReadStatus *status = nullptr, ReadCallback callback = nullptr, void *param = nullptr) = 0

Read some pixels.

Call setAddrWindow to set up region to be read. Returns true when all pixels have been queued for reading.

Parameters:
  • buffer – Details requested format and buffer to read

  • status – Optional. Stores result of read operation.

  • callback – Optional. Invoked when read has completed

  • param – Parameters passed to callback

Return values:

int – Number of pixels queued for reading (or read); 0 if no further pixels to read, < 0 to try again later

virtual bool render(const Object &object, const Rect &location, std::unique_ptr<Renderer> &renderer)

Start rendering an object.

Surfaces may override this method to implement alternative rendering using specific hardware features of the display device.

Software renderers should be run by calling surface::execute.

Parameters:
  • object – What to render

  • location – Placement information

  • renderer – If operation cannot be completed in hardware, create a renderer instance to manage the process

Return values:

Return – true on success, false to retry later

bool render(const Object &object, const Rect &location)

Render an object in one cycle.

Use this method for simple renders which should complete in one cycle. Typically used for memory surface renders or where an object is expected to render within a single surface.

Parameters:
  • surface – Where to render the object to

  • location

Return values:

bool – true on success

inline bool execute(std::unique_ptr<Renderer> &renderer)

Execute a renderer.

Parameters:

renderer – Will be released when render has completed

Return values:

bool – true if render is complete

virtual void reset() = 0

Reset surface ready for more commands.

virtual bool present(PresentCallback callback = nullptr, void *param = nullptr) = 0

Present surface to display device.

Hardware devices will queue buffered commands to the display device then return. The surface will be marked as BUSY. Attempting to call present() on a BUSY surface must return true. If surface is EMPTY (no buffered commands), must return false.

Parameters:
  • callback – Invoked when surface is available for more commands

  • param – Passed to callback

Return values:

bool – true If callback has been queued, false if surface is empty

bool fillSmallRect(const Brush &brush, const Rect &location, const Rect &rect)

Fill a small rectangle using a non-transparent brush.

bool drawHLine(PackedColor color, uint16_t x0, uint16_t x1, uint16_t y, uint16_t w)

Draw a simple horizontal line using a filled rectangle.

bool drawVLine(PackedColor color, uint16_t x, uint16_t y0, uint16_t y1, uint16_t w)

Draw a simple vertical line using a filled rectangle.

struct Stat
#include <Surface.h>
class TextParser
#include <TextBuilder.h>

Simplifies construction of TextObject instances.

Subclassed by Graphics::TextBuilder

Public Functions

inline void setCursor(Point pt)

Set location to start new text segment.

class TextBuilder : public Graphics::TextParser, public Print
#include <TextBuilder.h>

Simplifies construction of TextObject instances.

Public Functions

inline virtual size_t write(uint8_t c) override

Writes a single character to output stream.

Parameters:

c – Character to write to output stream

Return values:

size_t – Quantity of characters written to output stream

inline virtual size_t write(const uint8_t *buffer, size_t size) override

Writes characters from a buffer to output stream.

Parameters:
  • buffer – Pointer to character buffer

  • size – Quantity of characters to write

Return values:

size_t – Quantity of characters written to stream

size_t write(uint8_t c) = 0

Writes a single character to output stream.

Parameters:

c – Character to write to output stream

Return values:

size_t – Quantity of characters written to output stream

inline size_t write(const char *str)

Writes a c-string to output stream.

Parameters:

str – Pointer to c-string

Return values:

size_t – Quantity of characters written to stream

size_t write(const uint8_t *buffer, size_t size)

Writes characters from a buffer to output stream.

Parameters:
  • buffer – Pointer to character buffer

  • size – Quantity of characters to write

Return values:

size_t – Quantity of characters written to stream

inline size_t write(const char *buffer, size_t size)

Writes characters from a buffer to output stream.

Parameters:
  • buffer – Pointer to character buffer

  • size – Quantity of characters to write

Return values:

size_t – Quantity of characters written to stream

class XPT2046 : private HSPI::Device, public Graphics::Touch
#include <XPT2046.h>

Public Functions

inline virtual Size getNativeSize() const override

Get physical size of display.

Return values:

Size – Dimensions for NORMAL orientation

inline virtual State getState() const override

Get current state.

class Touch
#include <Touch.h>

Subclassed by Graphics::VirtualTouch, Graphics::XPT2046

Public Types

using Callback = Delegate<void()>

Callback function.

Public Functions

virtual bool setOrientation(Orientation orientation)

Set display orientation.

virtual Size getNativeSize() const = 0

Get physical size of display.

Return values:

Size – Dimensions for NORMAL orientation

virtual State getState() const = 0

Get current state.

inline virtual void setCallback(Callback callback)

Register callback to be invoked when state changes.

inline Size getSize() const

Get native dimensions for current orientation.

inline Orientation getOrientation()

Get current display orientation.

inline Point translate(Point rawPos)

Translate position into screen co-ordinates.

struct Calibration
#include <Touch.h>
struct State
#include <Touch.h>
struct Size
#include <Types.h>

Size of rectangular area (width x height)

template<typename T>
struct TPoint
#include <Types.h>

An (x, y) display coordinate.

Public Functions

template<typename Q>
inline explicit constexpr TPoint(TPoint<Q> pt)

Conversion constructor.

struct Rect
#include <Types.h>

Location and size of rectangular area (x, y, w, h)

Public Functions

inline Rect &clip(const Rect &r)

Obtain intersection with another rectangle.

inline Rect &operator+=(const Rect &r)

Obtain smallest rectangle enclosing this rectangle and another.

class Region
#include <Types.h>

Represents the intersection of two rectangles.

This produces up to 4 separate, non-overlapping rectangles.

Public Functions

inline Region &operator+=(const Rect &r)

Add rectangle to this region.

Produces a single enclosing rectangle.

inline Region &operator-=(const Rect &r)

Remove rectangle from this region.

Operation is currently performed on bounding box ONLY. TODO: Implement region updates using existing information.

struct Location
#include <Types.h>

Identifies position within bounding rectangle.

Public Members

Rect dest

Where to write pixels on surface.

Rect source

Reference source area.

This is generally used by brushes to locate colour information. For example, with an ImageBrush objects can be filled to either re-use the same portion of the reference image, or to reveal a particular part of it.

Point pos

Position relative to dest/source top-left corner.

class ColorRange
#include <Types.h>
class Scale
#include <Types.h>
struct GlyphMetrics
#include <Types.h>

Glyph metrics.

Public Members

uint8_t width

Width of glyph.

uint8_t height

Height of glyph.

int8_t xOffset

Glyph position relative to cursor.

int8_t yOffset

Distance from upper-left corner to baseline.

uint8_t advance

Distance to next character.

namespace Display
class Virtual : public Graphics::AbstractDisplay
#include <Virtual.h>

Virtual display device for Host.

Talks to python virtual screen application via TCP

Public Functions

inline virtual String getName() const override

Get name of display.

inline virtual Size getNativeSize() const override

Get physical size of display.

Return values:

Size – Dimensions for NORMAL orientation

virtual bool setOrientation(Orientation orientation) override

Set display orientation.

inline virtual Size getSize() const override

Get target dimensions.

inline virtual PixelFormat getPixelFormat() const override

All surfaces support the same pixel format.

virtual bool setScrollMargins(uint16_t top, uint16_t bottom) override

Set margins for hardware scrolling.

Area between top/bottom can be scrolled using scroll() method.

Parameters:
  • top – Number of fixed pixels at top of screen

  • bottom – Number of fixed pixels at bottom of screen

virtual bool scroll(int16_t y) override

Scroll region of display up or down using hardware scrolling.

Parameters:

y – Number of lines to scroll. Positive values scroll content down, negative values scroll up.

virtual Surface *createSurface(size_t bufferSize = 0) override

Create a surface for use with this render target.

Caller is responsible for destroying the surface when no longer required.

Parameters:

bufferSizeSize of internal command/data buffer

Return values:

Surface* – The surface to use

class ILI9341 : public Graphics::MipiDisplay
#include <ILI9341.h>

Public Functions

inline virtual String getName() const override

Get name of display.

class NullDevice : public Graphics::AbstractDisplay
#include <Null.h>

Null display device, discards data.

Used for testing performance and algorithms.

Public Functions

inline virtual String getName() const override

Get name of display.

inline virtual Size getNativeSize() const override

Get physical size of display.

Return values:

Size – Dimensions for NORMAL orientation

inline virtual bool setOrientation(Orientation orientation) override

Set display orientation.

inline virtual bool setScrollMargins(uint16_t top, uint16_t bottom) override

Set margins for hardware scrolling.

Area between top/bottom can be scrolled using scroll() method.

Parameters:
  • top – Number of fixed pixels at top of screen

  • bottom – Number of fixed pixels at bottom of screen

inline virtual bool scroll(int16_t y) override

Scroll region of display up or down using hardware scrolling.

Parameters:

y – Number of lines to scroll. Positive values scroll content down, negative values scroll up.

inline virtual Size getSize() const override

Get target dimensions.

inline virtual PixelFormat getPixelFormat() const override

All surfaces support the same pixel format.

virtual Surface *createSurface(size_t bufferSize = 0) override

Create a surface for use with this render target.

Caller is responsible for destroying the surface when no longer required.

Parameters:

bufferSizeSize of internal command/data buffer

Return values:

Surface* – The surface to use

class ST7789V : public Graphics::MipiDisplay
#include <ST7789V.h>

Public Functions

inline virtual String getName() const override

Get name of display.

namespace Drawing

Enums

enum class Command : uint8_t

Values:

enumerator XX
enum class OpCode : uint8_t

Values:

enumerator store
enumerator add
enumerator sub
enumerator execute

Functions

String toString(OpCode opcode)
union Header
#include <Header.h>

Command header structure.

Public Types

enum class Type : uint8_t

Values:

enumerator uint8
enumerator uint16
enumerator uint32
enumerator resource
enum class DataType : uint8_t

Values:

enumerator charArray
enum class ResourceKind : uint8_t

Values:

enumerator text
enumerator image
enum class LengthSize : uint8_t

Values:

enumerator uint8
enumerator uint16

Public Members

uint8_t index

Register index.

Type type
OpCode opcode

Operation to perform.

= OpCode::store

uint32_t param
struct Graphics::Drawing::Header::[anonymous] [anonymous]

Size of resource length field.

ResourceKind kind
LengthSize lengthSize
DataType dataType
struct Graphics::Drawing::Header::[anonymous] resource
Command cmd
class Reader
#include <Reader.h>
struct Registers
#include <Registers.h>
class Renderer : public Graphics::MultiRenderer
#include <Renderer.h>

A drawing contains a compact list of drawing commands, like a virtual plotter.

class Target
#include <Target.h>
class Writer
#include <Writer.h>
namespace Mipi

Enums

enum SerialInterfaceCommand

Values:

enumerator DSI_V_SYNC_START
enumerator DSI_V_SYNC_END
enumerator DSI_H_SYNC_START
enumerator DSI_H_SYNC_END
enumerator DSI_COMPRESSION_MODE
enumerator DSI_END_OF_TRANSMISSION
enumerator DSI_COLOR_MODE_OFF
enumerator DSI_COLOR_MODE_ON
enumerator DSI_SHUTDOWN_PERIPHERAL
enumerator DSI_TURN_ON_PERIPHERAL
enumerator DSI_GENERIC_SHORT_WRITE_0_PARAM
enumerator DSI_GENERIC_SHORT_WRITE_1_PARAM
enumerator DSI_GENERIC_SHORT_WRITE_2_PARAM
enumerator DSI_GENERIC_READ_REQUEST_0_PARAM
enumerator DSI_GENERIC_READ_REQUEST_1_PARAM
enumerator DSI_GENERIC_READ_REQUEST_2_PARAM
enumerator DSI_DCS_SHORT_WRITE
enumerator DSI_DCS_SHORT_WRITE_PARAM
enumerator DSI_DCS_READ
enumerator DSI_EXECUTE_QUEUE
enumerator DSI_SET_MAXIMUM_RETURN_PACKET_SIZE
enumerator DSI_NULL_PACKET
enumerator DSI_BLANKING_PACKET
enumerator DSI_GENERIC_LONG_WRITE
enumerator DSI_DCS_LONG_WRITE
enumerator DSI_PICTURE_PARAMETER_SET
enumerator DSI_COMPRESSED_PIXEL_STREAM
enumerator DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20
enumerator DSI_PACKED_PIXEL_STREAM_YCBCR24
enumerator DSI_PACKED_PIXEL_STREAM_YCBCR16
enumerator DSI_PACKED_PIXEL_STREAM_30
enumerator DSI_PACKED_PIXEL_STREAM_36
enumerator DSI_PACKED_PIXEL_STREAM_YCBCR12
enumerator DSI_PACKED_PIXEL_STREAM_16
enumerator DSI_PACKED_PIXEL_STREAM_18
enumerator DSI_PIXEL_STREAM_3BYTE_18
enumerator DSI_PACKED_PIXEL_STREAM_24
enum SerialTransactionType

Values:

enumerator DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT
enumerator DSI_RX_END_OF_TRANSMISSION
enumerator DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE
enumerator DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE
enumerator DSI_RX_GENERIC_LONG_READ_RESPONSE
enumerator DSI_RX_DCS_LONG_READ_RESPONSE
enumerator DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE
enumerator DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE
enum DisplayCommandSet

Values:

enumerator DCS_NOP
enumerator DCS_SOFT_RESET
enumerator DCS_GET_COMPRESSION_MODE
enumerator DCS_GET_DISPLAY_ID
enumerator DCS_GET_ERROR_COUNT_ON_DSI
enumerator DCS_GET_RED_CHANNEL
enumerator DCS_GET_GREEN_CHANNEL
enumerator DCS_GET_BLUE_CHANNEL
enumerator DCS_GET_DISPLAY_STATUS
enumerator DCS_GET_POWER_MODE
enumerator DCS_GET_ADDRESS_MODE
enumerator DCS_GET_PIXEL_FORMAT
enumerator DCS_GET_DISPLAY_MODE
enumerator DCS_GET_SIGNAL_MODE
enumerator DCS_GET_DIAGNOSTIC_RESULT
enumerator DCS_ENTER_SLEEP_MODE
enumerator DCS_EXIT_SLEEP_MODE
enumerator DCS_ENTER_PARTIAL_MODE
enumerator DCS_ENTER_NORMAL_MODE
enumerator DCS_GET_IMAGE_CHECKSUM_RGB
enumerator DCS_GET_IMAGE_CHECKSUM_CT
enumerator DCS_EXIT_INVERT_MODE
enumerator DCS_ENTER_INVERT_MODE
enumerator DCS_SET_GAMMA_CURVE
enumerator DCS_SET_DISPLAY_OFF
enumerator DCS_SET_DISPLAY_ON
enumerator DCS_SET_COLUMN_ADDRESS
enumerator DCS_SET_PAGE_ADDRESS
enumerator DCS_WRITE_MEMORY_START
enumerator DCS_WRITE_LUT
enumerator DCS_READ_MEMORY_START
enumerator DCS_SET_PARTIAL_ROWS
enumerator DCS_SET_PARTIAL_COLUMNS
enumerator DCS_SET_SCROLL_AREA
enumerator DCS_SET_TEAR_OFF
enumerator DCS_SET_TEAR_ON
enumerator DCS_SET_ADDRESS_MODE
enumerator DCS_SET_SCROLL_START
enumerator DCS_EXIT_IDLE_MODE
enumerator DCS_ENTER_IDLE_MODE
enumerator DCS_SET_PIXEL_FORMAT
enumerator DCS_WRITE_MEMORY_CONTINUE
enumerator DCS_SET_3D_CONTROL
enumerator DCS_READ_MEMORY_CONTINUE
enumerator DCS_GET_3D_CONTROL
enumerator DCS_SET_VSYNC_TIMING
enumerator DCS_SET_TEAR_SCANLINE
enumerator DCS_GET_SCANLINE
enumerator DCS_SET_DISPLAY_BRIGHTNESS
enumerator DCS_GET_DISPLAY_BRIGHTNESS
enumerator DCS_WRITE_CONTROL_DISPLAY
enumerator DCS_GET_CONTROL_DISPLAY
enumerator DCS_WRITE_POWER_SAVE
enumerator DCS_GET_POWER_SAVE
enumerator DCS_SET_CABC_MIN_BRIGHTNESS
enumerator DCS_GET_CABC_MIN_BRIGHTNESS
enumerator DCS_READ_DDB_START
enumerator DCS_READ_PPS_START
enumerator DCS_READ_DDB_CONTINUE
enumerator DCS_READ_PPS_CONTINUE
enum DcsAddressMode

Values:

enumerator DCS_ADDRESS_MODE_MIRROR_Y
enumerator DCS_ADDRESS_MODE_MIRROR_X
enumerator DCS_ADDRESS_MODE_SWAP_XY
enumerator DCS_ADDRESS_MODE_REFRESH_BT
enumerator DCS_ADDRESS_MODE_BGR
enumerator DCS_ADDRESS_MODE_RGB
enumerator DCS_ADDRESS_MODE_LATCH_RL
enumerator DCS_ADDRESS_MODE_FLIP_X
enumerator DCS_ADDRESS_MODE_FLIP_Y
enum DcsPixelFormat

Values:

enumerator DCS_PIXEL_FMT_24BIT
enumerator DCS_PIXEL_FMT_18BIT
enumerator DCS_PIXEL_FMT_16BIT
enumerator DCS_PIXEL_FMT_12BIT
enumerator DCS_PIXEL_FMT_8BIT
enumerator DCS_PIXEL_FMT_3BIT
namespace Resource

Functions

void init(IDataSourceStream *stream)

Application calls this method to set source for graphics resourcess.

Graphics resource data such is compiled into a single binary file which the application must mount in some way (typically as a dedicated partition), then create a stream so the graphics library can access it.

Parameters:

stream – Where to obtain resource data from

IDataSourceStream *createSubStream(uint32_t offset, size_t size)

Graphics objects call this method to obtain access to resource data.

The resource compiler generates a header file containing resource descriptions. The application passes this to the appropriate object constructor, which in turn calls this function to access the related binary data (e.g. font or image bitmaps).

Parameters:
  • offsetLocation within resource stream

  • sizeSize of data BLOB

struct GlyphResource
#include <resource.h>

Describes glyph bitmap and position.

Public Members

uint16_t bmOffset

Offset relative to TypefaceResource::bmpOffset.

uint8_t width

Bitmap dimensions in pixels.

uint8_t height

Bitmap dimensions in pixels.

int8_t xOffset

X dist from cursor pos to UL corner.

int8_t yOffset

Y dist from cursor pos to UL corner.

uint8_t xAdvance

Distance to advance cursor (x axis)

struct GlyphBlock
#include <resource.h>

Identifies a run of unicode characters.

Public Members

uint16_t codePoint

First character code.

uint16_t length

Number of consecutive characters.

struct TypefaceResource
#include <resource.h>

Public Members

uint32_t bmOffset

Start of bitmap data in resource stream.

struct FontResource
#include <resource.h>
struct ImageResource
#include <resource.h>
References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

HMC5883L Compass

https://github.com/jrowberg/i2cdevlib.git

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

HardwareSPI

Asynchronous SPI device stack for Sming. Initially written for ESP8266 but supports other architectures.

Problem statement

The ESP8266 has limited I/O and the most useful way to expand it is using serial devices. There are several types available:

I2C

Can be fast-ish but more limited than SPI, however slave devices generally cheaper, more widely available and simpler to interface as they only require 2 pins. The ESP8266 does have hardware support however, so requires a bit-banging solution. Inefficient.

I2S

Designed for streaming audio devices but has other uses. See Esp8266 Drivers.

RS232

Tied in with RS485, Modbus, DMX512, etc. Well-supported with asynchronous driver.

SPI

Speed generally limited by slave device capability, can be multiplexed (‘overlapped’) onto SPI0 (flash memory) pins. The two SPI modules are identical, but the additional pins for quad/dual modes are only brought out for SPI0. In addition, three user chip selects are available in this mode; there is only one in normal mode although this can be handled using a regular GPIO.

The purpose of this driver is to provide a consistent interface which makes better use of the hardware capabilities.

  • Classes may inherit from HSPI::Device to provide support for specific devices such as Flash memory, SPI RAM, LCD controllers, etc.

  • Device objects are attached to the stack via specified PinSet (overlapped or normal) and chip select.

  • Multiple concurrent devices are supported, limited only by available chip selects and physical constraints such as wire length, bus speeds.

  • 2 and 4-bit modes are supported via overlap pins.

  • A HSPI::Request object supports transfers of up to 64K. The controller splits these into smaller transactions as required.

  • Asynchronous execution so application does not block during SPI transfer. Application may provide a per-request callback to be notified when request has completed.

  • Blocking requests are also supported.

  • Support for moving data between Sming Streams and SPI memory devices using the HSPI::StreamAdapter.

SPI system expansion

A primary use-case for this driver is to provide additional resources for the ESP8266 using SPI devices such as:

  • MC23S017 SPI bus expander. Operates at 10MHz (regular SPI) and provides 16 additional I/O with interrupt capability.

  • High-speed shift registers. These can be wired directly to SPI buses to expand GPIO capability.

  • Epson S1D13781 display controller. See TFT_S1D13781. Evaluation boards are inexpensive and is a useful way to evaluate display modules with TFT interfaces. The Newhaven NHD-5.0-800480TF-ATXL#-CTP was used during development of this driver.

  • Bridgetek FT813 EVE TFT display controller. This supports dual/quad modes and clocks up to 30MHz.

  • NRF24L01 RF transceiver. Rated bus speed is 8MHz but it seems to work fine at 40MHz.

  • Serial memory devices. The library contains a driver for the IS62/65WVS2568GALL fast serial RAM, which clocks up to 45MHz and supports SDI/SQI modes.

Software operation
Overview

We have:

  • Controller: SPI hardware

  • Device: Slave device on the SPI bus

  • Request: Details of a transaction for a specific device

The Controller maintains an active list of requests (as a linked-list), but does not own the request objects. These will be allocated by the application, configured then submitted to the Controller for execution.

The Device specifies how a slave is connected to the bus, and that may change dynamically. For example, at reset an SPI RAM may be in SPI mode but can be switched into SDI/SQI modes. This is device-specific so would be implemented by superclassing HSPI::Device.

Requests

Each HSPI::Request is split into transactions. A transaction has four phases: command - address - MOSI - dummy - MISO. All phases are optional. The dummy bits are typically used in read modes and specified by the device datasheet. No data is transferred during this phase.

The ESP8266 hardware FIFO is used for MOSI/MISO phases and is limited to 64 bytes, so larger transfers must be broken into chunks. The driver handles this automatically.

Requests may be executed asynchronously so the call will not block and the CPU can continue with normal operations. An optional callback is invoked when the request has completed. As an example, consider moving a 128KByte file from flash storage into FT813 display memory:

  1. Read the first file chunk into a RAM buffer, submit an SPI request1 to transfer it asynchronously

  2. Read the second file chunk into another RAM buffer, and prepare request2 for that (but do not submit it yet)

  3. When request1 has completed, submit request2 (from the interrupt callback). Schedule a task to read the next chunk and prepare request1.

  4. When request2 has completed, continue from step (2) to submit request1, etc.

Timing

A 64-byte data transfer (full hardware FIFO with 1 command byte and 3-byte address) at 26MHz would take 21us (5.25us in QIO mode) or 1680 (420) CPU cycles. To transfer 128Kbytes would take 2048 such transactions, 43ms (11ms for QIO), not including memory copy overheads.

In practice request sizes will be much smaller due to RAM constraints. Nevertheless, at high clock speeds the interrupt rate increases to the point where it consumes more CPU cycles than the actual transfer. The driver therefore disables interrupts in these situations and executes the request in task mode.

Bear in mind that issuing a blocking request will also require all queued requests to complete.

The driver does not currently support out-of-order execution, which might prioritise faster devices.

Pin Set

To avoid confusion, we’ll refer to the flash memory SPI bus as SPI0, and the user bus as SPI1. This driver doesn’t support direct use of SPI0 as on most devices it is reserved for flash memory. However, an overlap mode is supported which makes use of hardware arbitration to perform SPI1 transactions using SPI0 pins. This has several advantages:

  • Liberates three GPIO which would normally be required for MOSI, MISO and SCLK.

  • Only one additional pin is required for chip select.

  • Additional 2/4 bits-per-clock modes are available for supported devices.

For the ESP8266, these are the HSPI::PinSet assignments:

PinSet::normal
MISO=GPIO12, MOSI=GPIO13, SCLK=GPIO14. One chip select:
  1. GPIO15 (HSPI CS)

PinSet::overlap
MISO=SD0, MOSI=SD1, IO2=SD3, IO3=SD2, SCLK = CLK. Three chip selects:
  1. GPIO15 (HSPI_CS)

  2. GPIO1 (SPI_CS1 / UART0_TXD). This conflicts with the normal serial TX pin which should be swapped to GPIO2 if required.

  3. GPIO0 (SPI_CS2)

PinSet::manual

Typically a GPIO will be assigned to perform chip select (CS). The application should register a callback function via HSPI::onSelectDevice() which performs the actual switching. This MUST be in IRAM.

Note

The connections for IO2/3 look wrong above, but on two different models of SPI RAM chip these have been verified as correct by writing in SPIHD mode and reading in quad mode.

Multiplexed CS

Multiple devices can be supported on a single CS using, for example using a HC138 3:8 decoder. The CS line is connected to an enable input, with three GPIO outputs setting A0-2.

A custom controller should be created like this:

class CustomController: public HSPI::Controller
{
public:
   bool startDevice(Device& dev, PinSet pinSet, uint8_t chipSelect) override
   {
      /*
       * You should perform any custom validation here and return false on failure.
       * For example, if we're only using 3 of the 8 available outputs.
       */
      auto addr = chipSelect & 0x07;
      if(addr > 3) {
         debug_e("Invalid CS addr: %u", addr);
         return false;
      }

      /*
       * Provide a callback to route chip select signal as required.
       * Note this only needs to be done once, so could be called externally without
       * subclassing HSPI::Controller if the other mechanisms aren't required.
       */
      onSelectDevice(selectDevice);

      /*
       * Initialise hardware Controller
       */
      auto cs = chipSelect >> 3;
      return HSPI::Controller::startDevice(dev, pinSet, cs);
   }

private:
   static void IRAM_ATTR selectDevice(uint8_t chipSelect, bool active);

   uint8_t activeChipSelect{0};
};

Now in the .cpp file:

CustomController spi;

void IRAM_ATTR CustomController::selectDevice(uint8_t chipSelect, bool active)
{
   // Only perform GPIO if CS changes as GPIO is expensive
   if(active && chipSelect != activeChipSelect) {
      auto addr = chipSelect & 0x07;
      digitalWrite(PIN_MUXADDR0, addr & 0x01);
      digitalWrite(PIN_MUXADDR1, addr & 0x02);
      // As we only need 2 address lines, can leave this one
      // digitalWrite(PIN_MUXADDR2, addr & 0x03);

      activeChipSelect = chipSelect;
   }

   // If using a hardware CS output then we're done.

   if(spi.getActivePinSet() == HSPI::PinSet::manual) {
      // For manual chip select operation, set the state directly here
   }
}
IO Modes

Not to be confused with HSPI::ClockMode, the HSPI::IoMode determines how the command, address and data phases are transferred:

.

Bits per clock

.

IO Mode

Command

Address

Data

Duplex

SPI

1

1

1

Full

SPIHD

1

1

1

Half

SPI3WIRE

1

1

1

Half

DUAL

1

1

2

Half

DIO

1

2

2

Half

SDI

2

2

2

Half

QUAD

1

1

4

Half

QIO

1

4

4

Half

SQI

4

4

4

Half

Note

SDI and SQI are not supported directly by hardware, but is implemented within the driver using the address phase. In these modes, commands are limited to 8 bits.

This seems to be consistent with the ESP32 IDF driver, as in spi_ll.h:

/** IO modes supported by the master. */
typedef enum {
    SPI_LL_IO_MODE_NORMAL = 0,  ///< 1-bit mode for all phases
    SPI_LL_IO_MODE_DIO,         ///< 2-bit mode for address and data phases, 1-bit mode for command phase
    SPI_LL_IO_MODE_DUAL,        ///< 2-bit mode for data phases only, 1-bit mode for command and address phases
    SPI_LL_IO_MODE_QIO,         ///< 4-bit mode for address and data phases, 1-bit mode for command phase
    SPI_LL_IO_MODE_QUAD,        ///< 4-bit mode for data phases only, 1-bit mode for command and address phases
} spi_ll_io_mode_t;

Somne devices (e.g. W25Q32 flash) have specific commands to support these modes, but others (e.g. IS62/65WVS2568GALL fast serial RAM) do not, and the SDI/SQI mode setting applies to all phases. This needs to be implemented in the driver as otherwise the user code is more complex than necesssary and performance suffers considerably.

Streaming

The HSPI::StreamAdapter provides support for streaming of data to/from memory devices.

This would be used, for example, to transfer content to or from a FileStream or FlashMemoryStream to SPI RAM asynchronously.

Supported devices must inherit from HSPI::MemoryDevice.

API
enum class HSPI::ClockMode : uint8_t

SPI clock polarity (CPOL) and phase (CPHA)

Values:

enumerator mode0

CPOL: 0 CPHA: 0.

enumerator mode1

CPOL: 0 CPHA: 1.

enumerator mode2

CPOL: 1 CPHA: 0.

enumerator mode3

CPOL: 1 CPHA: 1.

enum class HSPI::IoMode : uint8_t

Mode of data transfer.

Values:

enumerator SPI

One bit per clock, MISO stage concurrent with MISO (full-duplex)

enumerator SPIHD

One bit per clock, MISO stage follows MOSI (half-duplex)

enumerator SPI3WIRE

Half-duplex using MOSI for both sending and receiving data.

enumerator DUAL

Two bits per clock for Data, 1-bit for Command and Address.

enumerator DIO

Two bits per clock for Address and Data, 1-bit for Command.

enumerator SDI

Two bits per clock for Command, Address and Data.

enumerator QUAD

Four bits per clock for Data, 1-bit for Command and Address.

enumerator QIO

Four bits per clock for Address and Data, 1-bit for Command.

enumerator SQI

Four bits per clock for Command, Address and Data.

enumerator MAX
enum class HSPI::PinSet

How SPI hardware pins are connected.

Values:

enumerator none

Disabled.

enumerator normal

Standard HSPI pins.

enumerator manual

HSPI pins with manual chip select.

enumerator overlap

Overlapped with SPI 0.

struct Request

Defines an SPI Request Packet.

Request fields may be accessed directly or by use of helper methods.

Application is responsible for managing Request object construction/destruction. Queuing is managed as a linked list so the objects aren’t copied.

Applications will typically only require a couple of Request objects, so one can be prepared whilst the other is in flight. This helps to minimises the setup latency between SPI transactions.

Set value for command phase

inline void setCommand(uint16_t command, uint8_t bitCount)
Parameters:
  • command

  • bitCount – Length of command in bits

inline void setCommand8(uint8_t command)

Set 8-bit command.

Parameters:

command

inline void setCommand16(uint16_t command)

Set 16-bit command.

Parameters:

command

Set value for address phase

inline void setAddress(uint32_t address, uint8_t bitCount)
Parameters:
  • address

  • bitCount – Length of address in bits

inline void setAddress24(uint32_t address)

Set 24-bit address.

Parameters:

address

Public Functions

inline void setAsync(Callback callback = nullptr, void *param = nullptr)

Set request to asynchronous execution with optional callback.

Public Members

Device *device = {nullptr}

Target device for this request.

Request *next = {nullptr}

Controller uses this to queue requests.

uint16_t cmd = {0}

Command value.

uint8_t cmdLen = {0}

Command bits, 0 - 16.

uint8_t async

Set for asynchronous operation.

uint8_t task

Controller will execute this request in task mode.

uint8_t busy

Request in progress.

uint32_t addr = {0}

Address value.

uint8_t addrLen = {0}

Address bits, 0 - 32.

uint8_t dummyLen = {0}

Dummy read bits between address and read data, 0 - 255.

uint8_t sizeAlign = {0}

Required size alignment of each transaction (if split up)

Data out

Outgoing data.

Data in

Incoming data.

Callback callback = {nullptr}

Completion routine.

void *param = {nullptr}

User parameter.

struct Data

Specifies a block incoming or outgoing data.

Data can be specified directly within Data, or as a buffer reference.

Command or address are stored in native byte order and rearranged according to the requested byteOrder setting. Data is always sent and received LSB first (as stored in memory) so any re-ordering must be done by the device or application.

Set internal data value of 1-4 bytes

Note

Data is sent LSB, MSB (native byte order)

inline void set8(uint8_t data)

Set to single 8-bit value.

Parameters:

data

inline void set16(uint16_t data)

Set to single 16-bit value.

Parameters:

data

inline void set32(uint32_t data, uint8_t len = 4)

Set to 32-bit data.

Parameters:
  • data

  • len – Length in bytes (1 - 4)

Public Functions

inline void clear()

Reset to zero-length.

inline void set(const void *data, uint16_t count)

Set to reference external data block.

Parameters:
  • data – Location of data

  • count – Number of bytes

Public Members

void *ptr

Pointer to data.

uint16_t length

Number of bytes of data.

uint16_t isPointer

If set, data is referenced indirectly, otherwise it’s stored directly.

class Device

Manages a specific SPI device instance attached to a controller.

Subclassed by Graphics::SpiDisplay, Graphics::XPT2046, HSPI::MemoryDevice

Public Functions

inline bool begin(PinSet pinSet, uint8_t chipSelect, uint32_t clockSpeed)

Register device with controller and prepare for action.

Parameters:
  • pinSet – Use PinSet::normal for Esp32, other values for Esp8266

  • chipSelect – Identifies the CS number for ESP8266, or the GPIO pin for ESP32

  • clockSpeed – Bus speed

inline bool isReady() const

Determine if the device is initialised.

Return values:

bool

virtual IoModes getSupportedIoModes() const = 0

Return set of IO modes supported by a device implementation.

inline bool isSupported(IoMode mode) const

Determine if the device/controller combination supports an IO mode Must be called after begin() as other settings (e.g. pinset) can affect support.

inline void onTransfer(Callback callback)

Set a callback to be invoked before a request is started, and when it has finished.

Parameters:

callback – Invoked in interrupt context, MUST be in IRAM

class MemoryDevice : public HSPI::Device

Base class for read/write addressable devices.

Subclassed by HSPI::RAM::IS62_65, HSPI::RAM::PSRAM64

Prepare a write request

virtual void prepareWrite(HSPI::Request &req, uint32_t address) = 0

Prepare request without data.

inline void prepareWrite(HSPI::Request &req, uint32_t address, const void *data, size_t len)

Prepare request with data.

Parameters:
  • dataData to send

  • len – Size of data in bytes

Prepare a read request

virtual void prepareRead(HSPI::Request &req, uint32_t address) = 0

Prepare without buffer.

inline void prepareRead(HSPI::Request &req, uint32_t address, void *buffer, size_t len)

Prepare with buffer.

Parameters:
  • buffer – Where to write incoming data

  • len – Size of buffer

Public Functions

inline void write(uint32_t address, const void *data, size_t len)

Write a block of data.

Note

Limited by current operating mode

Parameters:
  • address

  • data

  • len

inline void read(uint32_t address, void *buffer, size_t len)

Read a block of data.

Note

Limited by current operating mode

Parameters:
  • address

  • data

  • len

class PSRAM64 : public HSPI::MemoryDevice

PSRAM64(H) pseudo-SRAM.

class IS62_65 : public HSPI::MemoryDevice

IS62/65WVS2568GALL fast serial RAM.

class Controller : public ControllerBase

Manages access to SPI hardware.

Public Types

using SelectDevice = void (*)(uint8_t chipSelect, bool active)

Interrupt callback for custom Controllers.

For manual CS (PinSet::manual) the actual CS GPIO must be asserted/de-asserted.

Expanding the SPI bus using a HC138 3:8 multiplexer, for example, can also be handled here, setting the GPIO address lines appropriately.

Param chipSelect:

The value passed to startDevice()

Param active:

true when transaction is about to start, false when completed

Public Functions

void end()

Disable HSPI controller.

Note

Reverts HSPI pins to GPIO and disables the controller

IoModes getSupportedIoModes(const Device &dev) const

Determine which IO modes are supported for the given device.

May be restricted by both controller and device capabilities.

inline void onSelectDevice(SelectDevice callback)

Set interrupt callback to use for manual CS control (PinSet::manual) or if CS pin is multiplexed.

Note

Callback MUST be marked IRAM_ATTR

virtual bool startDevice(Device &dev, PinSet pinSet, uint8_t chipSelect, uint32_t clockSpeed)

Assign a device to a CS# using a specific pin set. Only one device may be assigned to any CS.

Custom controllers should override this method to verify/configure chip selects, and also provide a callback (via onSelectDevice()).

virtual void stopDevice(Device &dev)

Release CS for a device.

void configChanged(Device &dev)

Devices call this method to tell the Controller about configuration changes. Internally, we just set a flag and update the register values when required.

inline SpiBus getBusId() const

Get the active bus identifier.

On successful call to begin() returns actual bus in use.

bool loopback(bool enable)

For testing, tie MISO <-> MOSI internally.

enable true to enable loopback, false for normal receive operation

Return values:

true – on success, false if loopback not supported

class StreamAdapter

Helper class for streaming data to/from SPI devices.

References
Used by
Environment Variables
  • HSPI_ENABLE_STATS

  • HSPI_ENABLE_TESTPINS

  • HSPI_TESTPIN1

  • HSPI_TESTPIN2

SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Hue Emulator

A framework for emulating Hue smart light devices via the Hue::Bridge class.

A real bridge talks to Hue devices via ZigBee, however with Sming you can control anything you want using the published API. Refer to specifications available at https://developers.meethue.com (free account login required).

Setup

Refer to the Basic Alexa sample for details of how to use this library. Here are a few key notes.

A HttpServer object is required to allow the framework to respond to requests. Note that Gen 3+ Hue Bridges listen on port 80 (standard HTTP), however older versions use port 1901. This library has only been tested on port 80.

In your application, remember to add bodyparsers for JSON and XML:

server.setBodyParser(MIME_JSON, bodyToStringParser);
server.setBodyParser(MIME_XML, bodyToStringParser);

Without these, you’ll get empty bodies for incoming requests.

The sample demonstrates use of provided On/Off, Dimmable and Colour device types with a global callback function.

Ideally you should provide your own custom Hue devices by inheriting from Hue::Device. This is demonstrated using MyHueDevice. The device ID is 666.

API
class Bridge : public UPnP::schemas_upnp_org::device::Basic1Template<Bridge>

Public Types

using ConfigDelegate = Delegate<void(const Config &config)>

Called when a new user key is created.

The application should use this to store new users in persistent memory. At startup, these should be passed back via the configure() method.

using StateChangeDelegate = Delegate<void(const Hue::Device &device, Hue::Device::Attributes attr)>

A global callback may be provided to perform actions when device states change.

The callback is invoked only when all request actions have been completed. The current state may be quereied using device::getAttribute.

Param device:

The device which has been updated

Param attr:

A set of flags indicating which attributes were changed

Public Functions

inline Bridge(Hue::Device::Enumerator &devices)

Constructor.

Parameters:

devices – List of devices to present

void configure(const Config &config)

Perform a configuration action.

Parameters:

config – The action to perform

inline void enablePairing(bool enable)

Enable creation of new users.

This could be enabled via web page on local Access Point, or physical push-button. It should also be time limited, so exits pairing mode after maybe 30 seconds. If a user creation request is received then this is disabled automatically.

Note

DO NOT leave this permanently enabled!

inline const Stats &getStats()

Get bridge statistics.

Return values:

const – Stats&

inline void resetStats()

Clear the bridge statistics.

inline const UserMap &getUsers() const

Access the list of users.

Return values:

const – UserMap&

void getStatusInfo(JsonObject json)

Get bridge status information in JSON format.

Parameters:

json – Where to write information

inline void deviceStateChanged(const Hue::Device &device, Hue::Device::Attributes changed)

Devices call this method when their state has been updated.

Note

Applications should not call this method

struct Config

Public Members

Type type

Configuration action to perform.

String deviceType

How device identifies itself.

String name

Randomly generated key.

class Device : public UPnP::Item

Subclassed by Hue::OnOffDevice

Public Types

using Callback = Delegate<void(Status status, int errorCode)>

Callback invoked when setAttribute() has completed.

Note

Any status other than success is considered a failure

Param status:

Result of the operation

Param errorCode:

Application-specific error code

Public Functions

virtual Status setAttribute(Attribute attr, unsigned value, Callback callback) = 0

Set a device attribute.

Note

DO NOT invoke the callback directly: only use it if pended.

Parameters:
  • attr – The attribute to change

  • value – Value for the attribute (exact type is attribute-specific)

  • callback – If you return Status::pending, invoke this callback when completed

Return values:

Status

virtual bool getAttribute(Attribute attr, unsigned &value) const = 0

Get the (cached) device attribute value.

Parameters:
  • attr

  • value

Return values:

bool – true on success, false if attribute not supported or value unknown

virtual String getUniqueId() const

Returns the unique device ID string.

Note

Other forms of ID string may be used, however for maximum compatibility the standard format should be used. By default, this method uses the WiFi station MAC address, with 00:11 appended plus the 8-bit device ID.

Return values:

String – Unique ID of the form AA:BB:CC:DD:EE:FF:00:11-XX, consisting of a 64-bit Zigbee MAC address plus unique endpoint ID.

inline bool operator==(const Device &dev) const

Two devices are considered equal if they have the same ID.

class Enumerator : public UPnP::Enumerator<Device, Enumerator>

Abstract class to manage a list of devices.

Note

Applications must provide an implementation of this for the bridge. Returned device objects may only be considered valid for the duration of the current task call as they may be destroyed at any time.

Subclassed by Hue::DeviceListEnumerator

Public Functions

virtual Device *find(Device::ID id)

Lookup device by ID.

Note

With default implementation, enumerator position is updated

Return values:

Device* – nullptr if not found

virtual Device *find(const String &name)

Lookup device by name.

Note

With default implementation, enumerator position is updated

Return values:

Device* – nullptr if not found

class OnOffDevice : public Hue::Device

Subclassed by Hue::DimmableDevice

class DimmableDevice : public Hue::OnOffDevice

Subclassed by Hue::ColourDevice

class ColourDevice : public Hue::DimmableDevice
enum class Hue::Status

Status of a setAttribute request.

Values:

enumerator success

The action was performed immediately without error.

enumerator pending

The action was accepted but requires further processing.

Use this to perform requests asynchronously. You MUST invoked the provided Callback function to complete the request.

When controlling remote devices, for example connected via serial link, you might issue the command immediately and then return pending. When the serial response is received, or a timeout occurs, then the request can be completed. Note that the error code passed to the callback is optional and will be specific to your application: it will be output in verbose debug mode so may be useful.

enumerator error

Action could not be completed.

If the Attribute not supported by your device, or an internal I/O error occurred then return this value.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

I2C Device Class

This is a generic class which abstracts bit and byte I2C R/W functions. It is part of The I2C Device Library.

There are examples in many of the classes that demonstrate basic usage patterns. The I2Cdev class is built to be used statically, reducing the memory requirement if you have multiple I2C devices in your project. Only one instance of the I2Cdev class is required.

https://github.com/jrowberg/i2cdevlib.git

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

IO Control

Introduction

This library is an asynchronous Input/Output device stack for embedded devices such as the ESP8266. The main reason for building this was to provide a mechanism for serialising modbus requests. We also needed to serialise commands to a 433MHz RF switch.

The goal is to abstract away differences between different communication protocols and provide a uniform API for controlling a network of device nodes.

To keep objects generic (i.e. not too modbus-centric) we define the following objects and features:

Controller

The hardware which talks over RS485 (or whatever). There is one controller instance for each physical device available. The controller serialises requests.

Device

An instance of a modbus slave or RF switch transmitter. These sit on top of a controller to define characteristics specific to the device (or slave). Multiple devices may be attached to a controller.

Request

Encapsulates a command request for a specific device class. Requests are created, queued, executed then destroyed. Callbacks are issued to notify the start of execution and completion.

Device Manager

Keeps track of all registered controllers and provides methods for creating and issuing requests.

Configuration is managed using JSON. Controller instances are provided by the application. Devices are defined using a JSON configuration file as devices may be added or removed from a deployed network.

The system for which this library was developed uses a basic HTML/javascript front-end which deals with configuration and generating command requests (in JSON). This keeps embedded memory requirements to a minimum.

Controllers are given an ID which indicates their class, plus index within that class:

  • rs485#0 RS485 serial controller #0

  • rfswitch#0 RF Controller #0 (i-Lumos RF Light switch)

Device identifiers can be anything but must be globally unique, e.g. mb1, rf0, dmx1, etc. See Basic RS485 sample, config/devices.json.

We can command a device thus:

{ "device": "mb1", "node": 1, "command": "toggle" }
{ "device": "rf0", "code": 0x123456 }

Commands are defined using a command set of codes via IO::Command. Not all device types support all commands.

API Documentation
DMX512

https://en.wikipedia.org/wiki/DMX512

_images/dmx512-decoder.jpg

Written to support DMX lighting controllers for dimmable lights. The model shown is for controlling RGB LED strip lighting.

DMX512 uses the RS485 physical interface, but a different protocol and usually higher baud rates than MODBUS. Nevertheless, it is possible to mix difference device types on the same network.

Requests to other devices will generally appear as garbage so shouldn’t have any bad side-effects.

namespace DMX512

DMX512/Device.h

Created on: 5 November 2018

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

DMX512/Request.h

Created on: 5 November 2018

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

struct NodeData
#include <Device.h>
class Device : public IO::RS485::Device
#include <Device.h>

Public Functions

virtual IO::Request *createRequest() override

Create a request object for this device.

Return values:

Request* – Caller must destroy or submit the request

inline virtual uint16_t maxNodes() const override

Determine maximum number of nodes supported by the device.

Return values:

uint16_t – 0 if device doesn’t support nodes

virtual void handleEvent(IO::Request *request, Event event) override

Implementations may override this method to customise event handling.

struct Config
#include <Device.h>

DMX512 Device Configuration.

Public Members

IO::RS485::Device::Config rs485

RS485 config.

uint8_t nodeCount

Number of nodes controlled by this device.

bool fade

Default node fade enable.

class Factory : public IO::RS485::Device::FactoryTemplate<Device>
#include <Device.h>

Public Functions

inline virtual const FlashString &deviceClass() const override

Return the Device class name, e.g. ‘r421a’.

class Request : public IO::Request
#include <Request.h>

Public Functions

virtual ErrorCode parseJson(JsonObjectConst json) override

Fill this request from a JSON description.

virtual void getJson(JsonObject json) const override

Get result of a completed request in JSON format.

virtual bool setNode(DevNode node) override

If nodes are supported, implement this method.

/

/**

inline virtual bool setValue(int value) override

If nodes support values, implement this method.

virtual void submit() override

Submit a request.

The request is added to the controller’s queue. If the queue is empty, it starts execution immediately. The result of the request is posted to the callback routine.

MODBUS

https://en.wikipedia.org/wiki/Modbus

Implements MODBUS-RTU protocols.

Can use this directly, but generally preferable to create custom Device and Request classes. See R421A Relay Boards for an example.

Device properties include:

Address

The address of a modbus slave. Modbus docs. call this the slave ID.

Node

Represents something a slave device does. Modbus relay boards have one node for each output it controls.

Node ID

The channel a node lives on. For the R421Axx relay boards this is the address or channel number. In a modbus transaction this is the address field.

For efficiency, nodes aren’t implemented as objects, just identifiers used by a Device / Controller. Nodes can be controlled using generic commands such as ‘open’, ‘close’, ‘toggle’, etc.

namespace Modbus

Modbus/ADU.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Modbus/Debug.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Modbus/Device.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Modbus/Exception.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Modbus/Function.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Modbus/GenericRequest.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Modbus/PDU.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Modbus/Request.h

Created on: 1 May 2018

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Modbus/Slave.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Debug print support, outputs contents of packet

size_t printRequest(Print &p, const PDU &pdu)
size_t printRequest(Print &p, const ADU &adu)
size_t printResponse(Print &p, const PDU &pdu)
size_t printResponse(Print &p, const ADU &adu)

Enums

enum class Exception

Modbus exception codes returned in response packets.

Values:

enumerator Success

No exception, transaction completed normally.

enumerator IllegalFunction

Function not allowed/supported/implemented, or device in wrong state to process request.

For example, an unconfigured device may be unable to return register values.

enumerator IllegalDataAddress

Data address not allowed.

More specifically, the combination of reference number and transfer length is invalid. For a controller with 100 registers, the ADU addresses the first register as 0, and the last one as 99.

If a request is submitted with a starting register address of 96 and a quantity of registers of 4, then this request will successfully operate (address-wise at least) on registers 96, 97, 98, 99.

If a request is submitted with a starting register address of 96 and a quantity of registers of 5, then this request will fail with Exception Code 0x02 “Illegal Data Address” since it attempts to operate on registers 96, 97, 98, 99 and 100, and there is no register with address 100.

enumerator IllegalDataValue

Data value not allowed.

This indicates a fault in the structure of the remainder of a complex request, such as that the implied length is incorrect. It specifically does NOT mean that a data item submitted for storage in a register has a value outside the expectation of the application program, since the MODBUS protocol is unaware of the significance of any particular value of any particular register.

enumerator SlaveDeviceFailure

Protocol slave device failure exception.

An unrecoverable error occurred while the server (or slave) was attempting to perform the requested action.

enum class Function

Values:

enumerator XX

Functions

inline bool operator!(Exception exception)
ErrorCode readRequest(RS485::Controller &controller, ADU &adu)
void sendResponse(RS485::Controller &controller, ADU &adu)
struct ADU
#include <ADU.h>

Prepare outgoing packet

The slaveAddress and pdu fields must be correctly initialised.

retval size_t:

Size of ADU, 0 on error

Parse a received packet

param receivedSize:

How much data was received

retval ErrorCode:

Result of parsing

class Device : public IO::RS485::Device
#include <Device.h>

A virtual device, represents a modbus slave device.

Actual devices must implement:

Subclassed by IO::Modbus::NT18B07::Device, IO::Modbus::R421A::Device, IO::Modbus::RID35::Device, IO::Modbus::STM8Relay::Device, IO::Modbus::STS::Fan::Device

Public Functions

inline virtual void onBroadcast(const ADU &adu)

Handle a broadcast message.

inline virtual void onRequest(ADU &adu)

Handle a message specifically for this device.

virtual IO::Request *createRequest() override

Create a request object for this device.

Return values:

Request* – Caller must destroy or submit the request

virtual void handleEvent(IO::Request *request, Event event) override

Implementations may override this method to customise event handling.

class Factory : public IO::RS485::Device::FactoryTemplate<Device>
#include <Device.h>

Public Functions

inline virtual const FlashString &deviceClass() const override

Return the Device class name, e.g. ‘r421a’.

class GenericRequest : public IO::Modbus::Request
#include <GenericRequest.h>

Public Functions

virtual ErrorCode parseJson(JsonObjectConst json) override

Fill this request from a JSON description.

virtual void getJson(JsonObject json) const override

Get result of a completed request in JSON format.

virtual ErrorCode callback(PDU &pdu) override

Process a received PDU.

Parameters:

pdu

Return values:

ErrorCode – If request is re-submitted, return Error::pending, otherwise request will be completed with given error.

struct PDU
#include <PDU.h>

Protocol Data Unit.

Content is independent of the communication layer. Structure does NOT represent over-the-wire format as packing issues make handling PDU::Data awkward. Therefore, the other fields are unaligned and adjusted as required when sent over the wire.

byteCount field

Some functions with a byteCount field are marked as calculated. This means it doesn’t need to be set when constructing requests or responses, and will be filled in later based on the values of other fields.

In other cases, a setCount method is provided to help ensure the byteCount field is set correctly.

Get PDU size based on content

Calculation uses byte count so doesn’t access any 16-bit fields

union Data
#include <PDU.h>

Public Members

uint8_t exceptionCode
ReadCoils readCoils
ReadDiscreteInputs readDiscreteInputs
ReadRegisters readHoldingRegisters
ReadRegisters readInputRegisters
WriteSingleCoil writeSingleCoil
WriteSingleRegister writeSingleRegister
ReadExceptionStatus readExceptionStatus
GetComEventCounter getComEventCounter
GetComEventLog getComEventLog
WriteMultipleCoils writeMultipleCoils
WriteMultipleRegisters writeMultipleRegisters
ReportServerId reportServerId
MaskWriteRegister maskWriteRegister
ReadWriteMultipleRegisters readWriteMultipleRegisters
union GetComEventCounter
#include <PDU.h>

Public Members

Response response
struct Response
#include <PDU.h>
union GetComEventLog
#include <PDU.h>

Public Members

Response response
struct Response
#include <PDU.h>

Public Members

uint8_t byteCount

Calculated.

union MaskWriteRegister
#include <PDU.h>

Public Types

using Response = Request

Public Members

Request request
Response response
struct Request
#include <PDU.h>
union ReadCoils
#include <PDU.h>

Public Members

Request request
Response response
struct Request
#include <PDU.h>
struct Response
#include <PDU.h>

Public Members

uint8_t byteCount

Use setCount()

uint8_t coilStatus[250]

Use setCoil()

union ReadDiscreteInputs
#include <PDU.h>

Public Members

Request request
Response response
struct Request
#include <PDU.h>
struct Response
#include <PDU.h>

Public Members

uint8_t byteCount

Calculated.

union ReadExceptionStatus
#include <PDU.h>

Public Members

Response response
struct Response
#include <PDU.h>
union ReadRegisters
#include <PDU.h>

Public Members

Request request
Response response
struct Request
#include <PDU.h>
struct Response
#include <PDU.h>

Public Members

uint8_t byteCount

Calculated.

union ReadWriteMultipleRegisters
#include <PDU.h>

Public Members

Request request
Response response
struct Request
#include <PDU.h>

Public Members

uint8_t writeByteCount

Calculated.

struct Response
#include <PDU.h>
union ReportServerId
#include <PDU.h>

Public Members

Response response
struct Response
#include <PDU.h>

Public Members

uint8_t data[248]

Content is specific to slave device.

union WriteMultipleCoils
#include <PDU.h>

Public Members

Request request
Response response
struct Request
#include <PDU.h>

Public Members

uint8_t byteCount

Calculated.

struct Response
#include <PDU.h>
union WriteMultipleRegisters
#include <PDU.h>

Public Members

Request request
Response response
struct Request
#include <PDU.h>

Public Members

uint8_t byteCount

Calculated.

struct Response
#include <PDU.h>
union WriteSingleCoil
#include <PDU.h>

Public Types

using Response = Request

Public Members

Request request
Response response
struct Request
#include <PDU.h>
union WriteSingleRegister
#include <PDU.h>

Public Types

using Response = Request

Public Members

Request request
Response response
struct Request
#include <PDU.h>
class Request : public IO::RS485::Request
#include <Request.h>

Subclassed by IO::Modbus::GenericRequest, IO::Modbus::NT18B07::Request, IO::Modbus::R421A::Request, IO::Modbus::RID35::Request, IO::Modbus::STM8Relay::Request, IO::Modbus::STS::Fan::Request

Public Functions

inline virtual bool setNode(DevNode node) override

If nodes are supported, implement this method.

/

/**

inline virtual uint16_t getAddress() const

Allows per-request address override (e.g. for broadcast)

virtual ErrorCode callback(PDU &pdu) = 0

Process a received PDU.

Parameters:

pdu

Return values:

ErrorCode – If request is re-submitted, return Error::pending, otherwise request will be completed with given error.

namespace NT18B07

Modbus/NT18B07/Device.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Rayleigh Instruments RI-D35 energy meter

Device registers are read-only. The Node ID corresponds to the register address.

Modbus/NT18B07/Request.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Typedefs

using TempData = int16_t[channelCount]

Variables

const size_t channelCount = {7}
class Device : public IO::Modbus::Device
#include <Device.h>

Public Functions

virtual IO::Request *createRequest() override

Create a request object for this device.

Return values:

Request* – Caller must destroy or submit the request

int16_t getIntValue(uint8_t channel) const

Get temperature value in 0.1C increments (avoids floating point)

inline virtual uint16_t maxNodes() const override

Determine maximum number of nodes supported by the device.

Return values:

uint16_t – 0 if device doesn’t support nodes

struct Config
#include <Device.h>

NT18B07 device configuration.

Public Members

Modbus::Device::Config modbus

Basic modbus configuration.

struct Comp
#include <Device.h>
class Factory : public IO::RS485::Device::FactoryTemplate<Device>
#include <Device.h>

Public Functions

inline virtual const FlashString &deviceClass() const override

Return the Device class name, e.g. ‘r421a’.

class Request : public IO::Modbus::Request
#include <Request.h>

Public Functions

virtual void getJson(JsonObject json) const override

Get result of a completed request in JSON format.

virtual ErrorCode callback(PDU &pdu) override

Process a received PDU.

Parameters:

pdu

Return values:

ErrorCode – If request is re-submitted, return Error::pending, otherwise request will be completed with given error.

namespace R421A

Modbus/R421A/Device.h

Created on: 1 May 2018

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

R421A08 modbus 8-channel relay board

Channels are numbered 1 - 8. This is the 16-bit address field in a request.

To simplify operation we use a bitmask to specify relay states. We use the channel number directly so values can range between 0x0001 and 0x01FE

The query command returns a range of states, but other commands work with only a single channel. We therefore implement a mechanism to iterate through all requested channels using the same request.

There is a similar 4-channel board but with no markings and (as yet) no documentation. However all commands appear to work so a designation of R421A04 seems appropriate. Some typos in the 8-channel datasheet indicate that it was adapted from that of a 4-channel version.

R421A04 - 32 addresses set with DIP1-5, DIP6 ON for RTU mode R421A08 - 64 addresses set with DIP1-6, RTU mode only

Modbus/R421A/Request.h

Created on: 1 May 2018

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Variables

constexpr uint8_t R421_CHANNEL_MIN = 1
constexpr uint8_t R421A_MAX_CHANNELS = 16
struct StateMask
#include <Device.h>

Tracks state of multiple relays.

Public Members

BitSet32 channelMask

Identifies valid channels.

class Device : public IO::Modbus::Device
#include <Device.h>

Public Functions

virtual IO::Request *createRequest() override

Create a request object for this device.

Return values:

Request* – Caller must destroy or submit the request

inline virtual DevNode::ID nodeIdMin() const override

Get minimum valid Node ID for this device.

Typically devices have a contiguous valid range of node IDs

inline virtual uint16_t maxNodes() const override

Determine maximum number of nodes supported by the device.

Return values:

uint16_t – 0 if device doesn’t support nodes

virtual DevNode::States getNodeStates(DevNode node) const override

Return the current set of states for all nodes controlled by this device.

Used to determine if, say, all nodes are ON, OFF or a combination.

virtual void handleEvent(IO::Request *request, Event event) override

Implementations may override this method to customise event handling.

struct Config
#include <Device.h>

R421A device configuration.

Public Members

Modbus::Device::Config modbus

Basic modbus configuration.

uint8_t channels

Number of channels (typically 4 or 8)

class Factory : public IO::RS485::Device::FactoryTemplate<Device>
#include <Device.h>

Public Functions

inline virtual const FlashString &deviceClass() const override

Return the Device class name, e.g. ‘r421a’.

class Request : public IO::Modbus::Request
#include <Request.h>

Public Functions

virtual ErrorCode parseJson(JsonObjectConst json) override

Fill this request from a JSON description.

virtual void getJson(JsonObject json) const override

Get result of a completed request in JSON format.

virtual bool setNode(DevNode node) override

If nodes are supported, implement this method.

/

/**

virtual DevNode::States getNodeStates(DevNode node) override

Query node status from response.

virtual ErrorCode callback(PDU &pdu) override

Process a received PDU.

Parameters:

pdu

Return values:

ErrorCode – If request is re-submitted, return Error::pending, otherwise request will be completed with given error.

namespace RID35

Modbus/RID35/Request.h

Created on: 24 March 2022

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Enums

enum class Unit

Values:

enumerator NONE
enumerator KW
enumerator KVAR
enumerator KVA
enumerator KWH
enumerator KVARH
enumerator KVAH
enumerator VOLT
enumerator AMP
enumerator HERTZ
enum class Register

Values:

enumerator XX
enumerator RID35_STDREG_MAP

Variables

constexpr uint16_t stdRegBase = 0x01
constexpr uint16_t ovfRegBase = 0x96
constexpr size_t stdRegCount = (1 + unsigned(Register::MaxDemandApparentPower)) * 2
constexpr size_t ovfRegCount = 1 + unsigned(Register::Kvah) - unsigned(Register::TotalKwh)
constexpr size_t registerCount = stdRegCount + ovfRegCount
class Device : public IO::Modbus::Device
#include <Device.h>

Public Functions

virtual IO::Request *createRequest() override

Create a request object for this device.

Return values:

Request* – Caller must destroy or submit the request

class Factory : public IO::RS485::Device::FactoryTemplate<Device>
#include <Device.h>

Public Functions

inline virtual const FlashString &deviceClass() const override

Return the Device class name, e.g. ‘r421a’.

class Request : public IO::Modbus::Request
#include <Request.h>

Public Functions

virtual void getJson(JsonObject json) const override

Get result of a completed request in JSON format.

virtual ErrorCode callback(PDU &pdu) override

Process a received PDU.

Parameters:

pdu

Return values:

ErrorCode – If request is re-submitted, return Error::pending, otherwise request will be completed with given error.

namespace STM8Relay

Modbus/STM8Relay/Device.h

Created on: 24 June 2022

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

STM8S103 modbus relay board

4-channel relay boards with optocouplers and digital inputs. Advertised as using STM8S103 microcontroller, but no model number. Relays controlled using Modbus ‘coil’ commands. Digital inputs read as ‘discrete inputs’. Connected directly to 3.3v microcontroller. Grounding pin sets bit.

Modbus/STM8Relay/Request.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Variables

constexpr uint8_t RELAY_CHANNEL_MIN = 1
constexpr uint8_t RELAY_MAX_CHANNELS = 16
struct StateMask
#include <Device.h>

Tracks state of multiple relays.

Public Members

BitSet32 channelMask

Identifies valid channels.

class Device : public IO::Modbus::Device
#include <Device.h>

Public Functions

virtual IO::Request *createRequest() override

Create a request object for this device.

Return values:

Request* – Caller must destroy or submit the request

inline virtual DevNode::ID nodeIdMin() const override

Get minimum valid Node ID for this device.

Typically devices have a contiguous valid range of node IDs

inline virtual uint16_t maxNodes() const override

Determine maximum number of nodes supported by the device.

Return values:

uint16_t – 0 if device doesn’t support nodes

virtual DevNode::States getNodeStates(DevNode node) const override

Return the current set of states for all nodes controlled by this device.

Used to determine if, say, all nodes are ON, OFF or a combination.

virtual void handleEvent(IO::Request *request, Event event) override

Implementations may override this method to customise event handling.

struct Config
#include <Device.h>

R421A device configuration.

Public Members

Modbus::Device::Config modbus

Basic modbus configuration.

uint8_t channels

Number of channels (typically 4 or 8)

class Factory : public IO::RS485::Device::FactoryTemplate<Device>
#include <Device.h>

Public Functions

inline virtual const FlashString &deviceClass() const override

Return the Device class name, e.g. ‘r421a’.

class Request : public IO::Modbus::Request
#include <Request.h>

Public Functions

virtual ErrorCode parseJson(JsonObjectConst json) override

Fill this request from a JSON description.

virtual void getJson(JsonObject json) const override

Get result of a completed request in JSON format.

virtual bool setNode(DevNode node) override

If nodes are supported, implement this method.

/

/**

virtual DevNode::States getNodeStates(DevNode node) override

Query node status from response.

virtual ErrorCode callback(PDU &pdu) override

Process a received PDU.

Parameters:

pdu

Return values:

ErrorCode – If request is re-submitted, return Error::pending, otherwise request will be completed with given error.

namespace STS
namespace Fan

Modbus/STS/Fan/Device.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

STS Fan Controller - see samples/Fan

Modbus/STS/Fan/Request.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Variables

const size_t channelCount = {3}
struct FanData
#include <Device.h>
class Device : public IO::Modbus::Device
#include <Device.h>

Public Functions

virtual IO::Request *createRequest() override

Create a request object for this device.

Return values:

Request* – Caller must destroy or submit the request

inline virtual uint16_t maxNodes() const override

Determine maximum number of nodes supported by the device.

Return values:

uint16_t – 0 if device doesn’t support nodes

class Factory : public IO::RS485::Device::FactoryTemplate<Device>
#include <Device.h>

Public Functions

inline virtual const FlashString &deviceClass() const override

Return the Device class name, e.g. ‘r421a’.

class Request : public IO::Modbus::Request
#include <Request.h>

Public Functions

inline virtual bool setNode(DevNode node) override

If nodes are supported, implement this method.

/

/**

inline virtual bool setValue(int value) override

If nodes support values, implement this method.

virtual ErrorCode callback(PDU &pdu) override

Process a received PDU.

Parameters:

pdu

Return values:

ErrorCode – If request is re-submitted, return Error::pending, otherwise request will be completed with given error.

virtual void getJson(JsonObject json) const override

Get result of a completed request in JSON format.

NT18B07 7-channel NTC temperature interface board
_images/nt18b07.jpg
  • Registers 0-6 contain temperature readings in 0.1 degree celcius increments

  • Register 254 contains slave ID, R/W, default 1

namespace NT18B07

Modbus/NT18B07/Device.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Rayleigh Instruments RI-D35 energy meter

Device registers are read-only. The Node ID corresponds to the register address.

Modbus/NT18B07/Request.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Typedefs

using TempData = int16_t[channelCount]

Variables

const size_t channelCount = {7}
class Device : public IO::Modbus::Device
#include <Device.h>

Public Functions

virtual IO::Request *createRequest() override

Create a request object for this device.

Return values:

Request* – Caller must destroy or submit the request

int16_t getIntValue(uint8_t channel) const

Get temperature value in 0.1C increments (avoids floating point)

inline virtual uint16_t maxNodes() const override

Determine maximum number of nodes supported by the device.

Return values:

uint16_t – 0 if device doesn’t support nodes

struct Config
#include <Device.h>

NT18B07 device configuration.

Public Members

Modbus::Device::Config modbus

Basic modbus configuration.

struct Comp
#include <Device.h>
class Factory : public IO::RS485::Device::FactoryTemplate<Device>
#include <Device.h>

Public Functions

inline virtual const FlashString &deviceClass() const override

Return the Device class name, e.g. ‘r421a’.

class Request : public IO::Modbus::Request
#include <Request.h>

Public Functions

virtual void getJson(JsonObject json) const override

Get result of a completed request in JSON format.

virtual ErrorCode callback(PDU &pdu) override

Process a received PDU.

Parameters:

pdu

Return values:

ErrorCode – If request is re-submitted, return Error::pending, otherwise request will be completed with given error.

R421A Relay Boards
_images/r421a08.png

These are inexpensive and readily available MODBUS multi-channel relay boards. Both 8 and 4-channel versions have been tested.

Device properties include:

Address

The address of a modbus slave. Modbus docs. call this the slave ID.

Node

Represents something a slave device does. Modbus relay boards have one node for each output it controls.

Node ID

The channel a node lives on. For the R421Axx relay boards this is the address or channel number. In a modbus transaction this is the address field.

8-channel
  • DIP switches set the slave address, from 0x00 to 0x3f. e.g. A0 ON=#1, A1 ON=#2, etc.

4-channel
  • Set A5 ON for MODBUS RTU mode (OFF is for AT mode).

  • Set A0-A4 to slave address, from 0x00 to 0x1f.

API
namespace R421A

Modbus/R421A/Device.h

Created on: 1 May 2018

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

R421A08 modbus 8-channel relay board

Channels are numbered 1 - 8. This is the 16-bit address field in a request.

To simplify operation we use a bitmask to specify relay states. We use the channel number directly so values can range between 0x0001 and 0x01FE

The query command returns a range of states, but other commands work with only a single channel. We therefore implement a mechanism to iterate through all requested channels using the same request.

There is a similar 4-channel board but with no markings and (as yet) no documentation. However all commands appear to work so a designation of R421A04 seems appropriate. Some typos in the 8-channel datasheet indicate that it was adapted from that of a 4-channel version.

R421A04 - 32 addresses set with DIP1-5, DIP6 ON for RTU mode R421A08 - 64 addresses set with DIP1-6, RTU mode only

Modbus/R421A/Request.h

Created on: 1 May 2018

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Variables

constexpr uint8_t R421_CHANNEL_MIN = 1
constexpr uint8_t R421A_MAX_CHANNELS = 16
struct StateMask
#include <Device.h>

Tracks state of multiple relays.

Public Members

BitSet32 channelMask

Identifies valid channels.

class Device : public IO::Modbus::Device
#include <Device.h>

Public Functions

virtual IO::Request *createRequest() override

Create a request object for this device.

Return values:

Request* – Caller must destroy or submit the request

inline virtual DevNode::ID nodeIdMin() const override

Get minimum valid Node ID for this device.

Typically devices have a contiguous valid range of node IDs

inline virtual uint16_t maxNodes() const override

Determine maximum number of nodes supported by the device.

Return values:

uint16_t – 0 if device doesn’t support nodes

virtual DevNode::States getNodeStates(DevNode node) const override

Return the current set of states for all nodes controlled by this device.

Used to determine if, say, all nodes are ON, OFF or a combination.

virtual void handleEvent(IO::Request *request, Event event) override

Implementations may override this method to customise event handling.

struct Config
#include <Device.h>

R421A device configuration.

Public Members

Modbus::Device::Config modbus

Basic modbus configuration.

uint8_t channels

Number of channels (typically 4 or 8)

class Factory : public IO::RS485::Device::FactoryTemplate<Device>
#include <Device.h>

Public Functions

inline virtual const FlashString &deviceClass() const override

Return the Device class name, e.g. ‘r421a’.

class Request : public IO::Modbus::Request
#include <Request.h>

Public Functions

virtual ErrorCode parseJson(JsonObjectConst json) override

Fill this request from a JSON description.

virtual void getJson(JsonObject json) const override

Get result of a completed request in JSON format.

virtual bool setNode(DevNode node) override

If nodes are supported, implement this method.

/

/**

virtual DevNode::States getNodeStates(DevNode node) override

Query node status from response.

virtual ErrorCode callback(PDU &pdu) override

Process a received PDU.

Parameters:

pdu

Return values:

ErrorCode – If request is re-submitted, return Error::pending, otherwise request will be completed with given error.

RF Switch
_images/stx882.png

Supports basic 433MHz AM transmitter attached to a GPIO pin via 5v buffer. You may be able to get away with connecting the GPIO directly and running from 3.3v, but timing and performance won’t be as good.

Uses hardware timer to generate PWM output using interrupts.

Developed for use with i-Lumos lightswitches which use a 24-bit code. Timing parameters are programmable though so may work with other devices.

namespace RFSwitch

RFSwitch/Controller.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

RFSwitch/ControlleDevice.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

RFSwitch/Request.h

Created on: 1 May 2018

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

class Controller : public IO::Controller
#include <Controller.h>

Controller for 433MHz transmitter.

Protocol is flexible but tested only with i-Lumos light switches. Written specifically for ESP8266 and uses the hardware timer to generate PWM signal via interrupts.

Public Functions

inline virtual const FlashString &classname() const override

Get the class name for this Controller.

virtual void handleEvent(IO::Request *request, Event event) override

Implementations override this method to process events as they pass through the stack.

struct Timing
#include <Device.h>

Protocol timings in microseconds.

Public Members

uint16_t starth

Width of start High pulse.

uint16_t startl

Width of start Low pulse.

uint16_t period

Bit period.

uint16_t bit0

Width of a ‘0’ high pulse.

uint16_t bit1

Width of a ‘1’ high pulse.

uint16_t gap

Gap after final bit before repeating.

class Device : public IO::Device
#include <Device.h>

Public Functions

virtual IO::Request *createRequest() override

Create a request object for this device.

Return values:

Request* – Caller must destroy or submit the request

struct Config
#include <Device.h>
class Factory : public IO::Device::Factory
#include <Device.h>

Public Functions

inline virtual IO::Device *createDevice(IO::Controller &controller, const char *id) const override

Create a new device instance.

Called by DeviceManager::createDevice()

Parameters:
  • controller – The owning controller

  • id – Unique identifier for the device

Return values:

Device* – The constructed instance

inline virtual const FlashString &controllerClass() const override

Return the expected controller type for this device class, e.g. ‘rs485’.

The Device Manager uses this value to verify that devices are constructed using the correct controller.

inline virtual const FlashString &deviceClass() const override

Return the Device class name, e.g. ‘r421a’.

class Request : public IO::Request
#include <Request.h>

Public Functions

virtual ErrorCode parseJson(JsonObjectConst json) override

Fill this request from a JSON description.

virtual void getJson(JsonObject json) const override

Get result of a completed request in JSON format.

inline virtual bool setNode(DevNode node) override

If nodes are supported, implement this method.

/

/**

RI-D35 Energy Meter
_images/ri-d35.jpg

These are inexpensive and readily available MODBUS single-phase energy meters.

namespace RID35

Modbus/RID35/Request.h

Created on: 24 March 2022

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Enums

enum class Unit

Values:

enumerator NONE
enumerator KW
enumerator KVAR
enumerator KVA
enumerator KWH
enumerator KVARH
enumerator KVAH
enumerator VOLT
enumerator AMP
enumerator HERTZ
enum class Register

Values:

enumerator XX
enumerator RID35_STDREG_MAP

Variables

constexpr uint16_t stdRegBase = 0x01
constexpr uint16_t ovfRegBase = 0x96
constexpr size_t stdRegCount = (1 + unsigned(Register::MaxDemandApparentPower)) * 2
constexpr size_t ovfRegCount = 1 + unsigned(Register::Kvah) - unsigned(Register::TotalKwh)
constexpr size_t registerCount = stdRegCount + ovfRegCount
class Device : public IO::Modbus::Device
#include <Device.h>

Public Functions

virtual IO::Request *createRequest() override

Create a request object for this device.

Return values:

Request* – Caller must destroy or submit the request

class Factory : public IO::RS485::Device::FactoryTemplate<Device>
#include <Device.h>

Public Functions

inline virtual const FlashString &deviceClass() const override

Return the Device class name, e.g. ‘r421a’.

class Request : public IO::Modbus::Request
#include <Request.h>

Public Functions

virtual void getJson(JsonObject json) const override

Get result of a completed request in JSON format.

virtual ErrorCode callback(PDU &pdu) override

Process a received PDU.

Parameters:

pdu

Return values:

ErrorCode – If request is re-submitted, return Error::pending, otherwise request will be completed with given error.

RS485

https://en.wikipedia.org/wiki/RS-485

RS485 is a very inexpensive way to wire devices together in a network. A single twisted pair (such as two wires from regular UTP network cable) at low speeds can operate over a kilometre.

Devices must be daisy-chained together, and long runs should be properly terminated typically with 120 ohm resistors at either end.

Connecting devices in star topology causes signal reflections and generally won’t work except for very short runs.

One solution is to use multiple MAX485 (or similar) transceivers to create multiple physical network segments. Only one UART is required unless you need concurrent requests.

_images/multiplex.png

This example allows for one transceiver to be in receive mode, with others in transmit. That means slave addresses must be unique across both segments. Resistors R2 and R8 are current limit resistors to guard against both transceivers being switched into receive mode at the same time.

The logic for controlling this would be in a callback registered via IO::RS485::Controller::onSetDirection().

For completeness, here’s example connections for a 3.3v transceiver:

_images/max485.png

R22 is optional, typically installed only on longer runs. D3 is additional transient protection - always a good idea as a first line of defence even if the transceiver itself has some built in.

namespace RS485

RS485/Controller.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

RS485/Device.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

RS485/Request.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Variables

constexpr unsigned DEFAULT_BAUDRATE = 9600
constexpr unsigned DEFAULT_TIMEOUT = 800
class Controller : public IO::Controller
#include <Controller.h>

Public Types

using SetDirectionCallback = void (*)(uint8_t segment, Direction direction)

Callback to handle hardware transmit/receive selection Typically called from interrupt context so implementation MUST be marked IRAM_ATTR.

Param segment:

Identifies physical connection for shared/multiplexed port

Param direction:

Public Functions

inline virtual const FlashString &classname() const override

Get the class name for this Controller.

virtual void start() override

Start the controller.

Controllers may now initiate communications with registered devices. Typically they’ll query device status, etc.

virtual void stop() override

Stop all controllers.

This method is called by the Device Manager, applications shouldn’t need it.

Note

MUST call canStop() first to ensure it’s safe to stop!

inline void onSetDirection(SetDirectionCallback callback)

Set the transmit callback handler.

Typically with RS485 a GPIO is used to toggle between transmit/receive modes. Using a callback allows flexibility for your particular hardware implementation. You don’t need to set this up if your hardware handles the switch automatically.

Parameters:

callback

inline void setDirection(IO::Direction direction)

Whilst a port is acquired, call this method to being or end transmission.

Note

Port should normally be left in receive mode on request completion.

Parameters:

direction

class Device : public IO::Device
#include <Device.h>

Base device class for communicating with an RS485 slave.

Subclassed by IO::DMX512::Device, IO::Modbus::Device

Public Functions

inline virtual uint16_t address() const override

Devices with a numeric address should implement this method.

virtual void handleEvent(IO::Request *request, Event event) override

Implementations may override this method to customise event handling.

struct Config
#include <Device.h>

RS485 configuration.

struct Slave
#include <Device.h>

Public Members

uint16_t address

Network device address or ‘slave ID’

uint8_t segment

Application-defined value for multiplexed serial ports. For example, several MAX485 transceivers can be connected to one serial port with appropriate multiplexing logic.

See IO::RS485::Controller::SetDirectionCallback

unsigned baudrate

Serial link speed

unsigned timeout

Max time between command/response in milliseconds.

template<class DeviceClass>
class FactoryTemplate : public IO::Device::Factory
#include <Device.h>

Subclassed by IO::DMX512::Device::Factory, IO::Modbus::Device::Factory, IO::Modbus::NT18B07::Device::Factory, IO::Modbus::R421A::Device::Factory, IO::Modbus::RID35::Device::Factory, IO::Modbus::STM8Relay::Device::Factory, IO::Modbus::STS::Fan::Device::Factory

Public Functions

inline virtual IO::Device *createDevice(IO::Controller &controller, const char *id) const override

Create a new device instance.

Called by DeviceManager::createDevice()

Parameters:
  • controller – The owning controller

  • id – Unique identifier for the device

Return values:

Device* – The constructed instance

inline virtual const FlashString &controllerClass() const override

Return the expected controller type for this device class, e.g. ‘rs485’.

The Device Manager uses this value to verify that devices are constructed using the correct controller.

class Request : public IO::Request
#include <Request.h>

Subclassed by IO::Modbus::Request

STM8S103 Relay Boards
_images/stm8relay.jpg
General

These are MODBUS multi-channel relay boards with optocouplers and digital inputs. Advertised as using STM8S103F3 microcontroller, but no model number. 1, 2, 4 and 8-channel versions are available. Only 4-channel version has been tested.

There are a number of improvements over the cheaper boards (such as R421A):

  • Barrel jack power connector provided in addition to screw terminals

  • Relays are driven using optocouplers

  • Relay outputs use pluggable board connectors

  • PCB layout has good electrical isolation between relay outputs and coil side

  • Readable digital inputs - 3.3v logic-level, active low, connected directly to microcontroller.

  • Programming connections are made available so can be programmed with custom firmware.

  • Slave address default is 1, can be changed using custom commands (no DIP switches)

Device properties include:

Address

The address of a modbus slave. Modbus docs. call this the slave ID.

Node

Represents something a slave device does. Modbus relay boards have one node for each output it controls.

Node ID

The channel a node lives on. In a modbus transaction this is the address field.

Commands
ReadCoil, WriteCoil

Read or set state of Relays

ReadDiscreteInputs

Read state of digital inputs. Bits are set to ‘1’ if input is pulled to ground.

ReadHoldingRegister

Registers must be read individually. Attempting to read multiple registers returns invalid data. 0x0004 Software month as 2 characters, e.g. “Ap” 0x0008 Software year in BCD, e.g. 0x2018 for 2018 0x0010 Software time in BCD, e.g. 0x1459 for 14:59 hrs 0x0020 Hardware version, e.g. 0x006a 0x4000 Slave address, typically broadcast (to address #0)

WriteHoldingRegister

0x4000 Set slave address, typically broadcast (to address #0)

namespace STM8Relay

Modbus/STM8Relay/Device.h

Created on: 24 June 2022

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

STM8S103 modbus relay board

4-channel relay boards with optocouplers and digital inputs. Advertised as using STM8S103 microcontroller, but no model number. Relays controlled using Modbus ‘coil’ commands. Digital inputs read as ‘discrete inputs’. Connected directly to 3.3v microcontroller. Grounding pin sets bit.

Modbus/STM8Relay/Request.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Variables

constexpr uint8_t RELAY_CHANNEL_MIN = 1
constexpr uint8_t RELAY_MAX_CHANNELS = 16
struct StateMask
#include <Device.h>

Tracks state of multiple relays.

Public Members

BitSet32 channelMask

Identifies valid channels.

class Device : public IO::Modbus::Device
#include <Device.h>

Public Functions

virtual IO::Request *createRequest() override

Create a request object for this device.

Return values:

Request* – Caller must destroy or submit the request

inline virtual DevNode::ID nodeIdMin() const override

Get minimum valid Node ID for this device.

Typically devices have a contiguous valid range of node IDs

inline virtual uint16_t maxNodes() const override

Determine maximum number of nodes supported by the device.

Return values:

uint16_t – 0 if device doesn’t support nodes

virtual DevNode::States getNodeStates(DevNode node) const override

Return the current set of states for all nodes controlled by this device.

Used to determine if, say, all nodes are ON, OFF or a combination.

virtual void handleEvent(IO::Request *request, Event event) override

Implementations may override this method to customise event handling.

struct Config
#include <Device.h>

R421A device configuration.

Public Members

Modbus::Device::Config modbus

Basic modbus configuration.

uint8_t channels

Number of channels (typically 4 or 8)

class Factory : public IO::RS485::Device::FactoryTemplate<Device>
#include <Device.h>

Public Functions

inline virtual const FlashString &deviceClass() const override

Return the Device class name, e.g. ‘r421a’.

class Request : public IO::Modbus::Request
#include <Request.h>

Public Functions

virtual ErrorCode parseJson(JsonObjectConst json) override

Fill this request from a JSON description.

virtual void getJson(JsonObject json) const override

Get result of a completed request in JSON format.

virtual bool setNode(DevNode node) override

If nodes are supported, implement this method.

/

/**

virtual DevNode::States getNodeStates(DevNode node) override

Query node status from response.

virtual ErrorCode callback(PDU &pdu) override

Process a received PDU.

Parameters:

pdu

Return values:

ErrorCode – If request is re-submitted, return Error::pending, otherwise request will be completed with given error.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

IRremoteESP8266 Library

Build Status arduino-library-badge Average time to resolve an issue Percentage of issues still open GitLicense

This library enables you to send *and* receive infra-red signals on an ESP8266 or an ESP32 using the Arduino framework using common 940nm IR LEDs and common IR receiver modules. e.g. TSOP{17,22,24,36,38,44,48}* demodulators etc.

v2.7.7 Now Available

Version 2.7.7 of the library is now available. You can view the Release Notes for all the significant changes.

Upgrading from pre-v2.0

Usage of the library has been slightly changed in v2.0. You will need to change your usage to work with v2.0 and beyond. You can read more about the changes required on our Upgrade to v2.0 page.

Upgrading from pre-v2.5

The library has changed from using constants declared as #define to const with the appropriate naming per the C++ style guide. This may potentially cause old programs to not compile. The most likely externally used #defines have been aliased for limited backward compatibility for projects using the old style. Going forward, only the new kConstantName style will be supported for new protocol additions.

In the unlikely case, it does break your code, then you may have been referencing something you likely should not have. You should be able to quickly determine the new name from the old. e.g. CONSTANT_NAME to kConstantName. Use common sense or examining the library’s code if this does affect code.

Supported Protocols

You can find the details of which protocols & devices are supported here.

Troubleshooting

Before reporting an issue or asking for help, please try to follow our Troubleshooting Guide first.

Frequently Asked Questions

Some common answers to common questions and problems are on our F.A.Q. wiki page.

Library API Documentation

This library uses Doxygen to automatically document the library’s API. You can find it here.

Installation
  1. Click the “Sketch” -> “Include Library” -> “Manage Libraries…” Menu items.

  2. Enter IRremoteESP8266 into the “Filter your search…” top right search box.

  3. Click on the IRremoteESP8266 result of the search.

  4. Select the version you wish to install and click “Install”.

  1. Click on “Clone or Download” button, then “`Download ZIP <https://github.com/crankyoldgit/IRremoteESP8266/archive->master.zip>`_“ on the page.

  2. Extract the contents of the downloaded zip file.

  3. Rename the extracted folder to “IRremoteESP8266”.

  4. Move this folder to your libraries directory. (under windows: C:\Users\YOURNAME\Documents\Arduino\libraries\)

  5. Restart your Arduino IDE.

  6. Check out the examples.

cd ~/Arduino/libraries
git clone https://github.com/crankyoldgit/IRremoteESP8266.git
cd ~/Arduino/libraries/IRremoteESP8266 && git pull
Contributing

If you want to contribute to this project, consider:

Contributors

Available here

Library History

This library was originally based on Ken Shirriff’s work (https://github.com/shirriff/Arduino-IRremote/)

Mark Szabo has updated the IRsend class to work on ESP8266 and Sebastien Warin the receiving & decoding part (IRrecv class).

As of v2.0, the library was almost entirely re-written with the ESP8266’s resources in mind.

References
Used by
SoC support
  • esp8266

Nextion Serial Displays

Introduction

Nextion Arduino library provides an easy-to-use way to manipulate Nextion serial displays. Users can use the libarry freely, either in commercial projects or open-source prjects, without any additional condiitons.

For more information about the Nextion display project, please visit the wiki。
The wiki provides all the necessary technical documnets, quick start guide, tutorials, demos, as well as some useful resources.

To get your Nextion display, please visit iMall.

To discuss the project? Request new features? Report a BUG? please visit the Forums

Download Source Code

Latest version is unstable and a mass of change may be applied in a short time without any notification for users. Commonly, it is for developers of this library.

Release version is recommended for you, unless you are one of developers of this library.

Release notes is at https://github.com/itead/ITEADLIB_Arduino_Nextion/blob/master/release_notes.md.

Latest(unstable)

Latest source code(master branch) can be downloaded: https://github.com/itead/ITEADLIB_Arduino_Nextion/archive/master.zip.

You can also clone it via git:

git clone https://github.com/itead/ITEADLIB_Arduino_Nextion
Releases(stable)

All releases can be available from: https://github.com/itead/ITEADLIB_Arduino_Nextion/releases.

Documentation

Offline Documentation’s entry doc/Documentation/index.html shipped with source code can be open in your browser such as Chrome, Firefox or any one you like.

Supported Mainboards

All boards, which has one or more hardware serial, can be supported.

For example:

  • Iteaduino MEGA2560

  • Iteaduino UNO

  • Arduino MEGA2560

  • Arduino UNO

Configuration

In configuration file NexConfig.h, you can find two macros below:

  • dbSerial: Debug Serial (baudrate:9600), needed by beginners for debug your nextion applications or sketches. If your complete your work, it will be a wise choice to disable Debug Serial.

  • nexSerial: Nextion Serial, the bridge of Nextion and your mainboard.

Note: the default configuration is for MEGA2560.

Redirect dbSerial and nexSerial

If you want to change the default serial to debug or communicate with Nextion , you need to modify the line in configuration file:

#define dbSerial Serial    ---> #define dbSerial Serialxxx
#define nexSerial Serial2  ---> #define nexSeria Serialxxx
Disable Debug Serial

If you want to disable the debug information,you need to modify the line in configuration file:

#define DEBUG_SERIAL_ENABLE ---> //#define DEBUG_SERIAL_ENABLE
UNO-like Mainboards

If your board has only one hardware serial, such as UNO, you should disable dbSerial and redirect nexSerial to Serial(Refer to section:Serial configuration).

License

The MIT License (MIT)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Liquid Crystal

Introduction
LCD library

Welcome to the LCD Library for Arduino and Chipkit. It is a derivate of the original LiquidCrystal Library as sourced in the Arduino SDK. It has been developed to be compatible with the current LiquidCrystal library, its performance is almost 5 times faster and fully extendable if need be.

It supports most Hitachi HD44780 based LCDs, or compatible, connected to any project using: 4, 8 wire parallel interface, I2C IO port expander (native I2C and bit bang) and Shift Register.

It currently supports 4 types of connections:

  • 4 bit parallel LCD interface

  • 8 bit parallel LCD interface

  • I2C IO bus expansion board with the PCF8574* I2C IO expander ASIC such as I2C LCD extra IO.

  • ShiftRegister adaptor board as described Shift Register project home or in the HW configuration described below, 2 and 3 wire configurations supported.

  • ShiftRegister 3 wire latch adaptor board as described ShiftRegister 3 Wire Home

  • Support for 1 wire shift register ShiftRegister 1 Wire

  • I2C bus expansion using general purpose IO lines.

How do I get set up?
  • Please refer to the project’s wiki

Contributors

The library has had the invaluable contribution of:

  • piccaso - Florian Fida - Flo, thanks for testing and improving the SR library, initial version of the 1 wire interface and speed improvements.

    1. Perry - bperrybap@opensource.billsworld.billandterrie.com, with his thoughtful contribution, speed improvements and development support for the SR2W library.

  • Adrian Piccioli, with his contribution to the i2c GPIO support.

  • todbot Tod E. Kurt for the softwarei2cmaster library.

  • felias-fogg - Bernhard for the softwarei2cmaster fast

Contribution guidelines
  • Writing tests

  • Code review

  • Help out with bug fixing

  • Setup a project logo

  • Write new drivers to support more LCDs.

Who do I talk to?
  • Repo owner or admin

  • For SoftI2CMaster latest versions, updates and support, please refer to SoftI2CMaster

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

LittleFS

Sming IFS integration of the LittleFS filesystem https://github.com/littlefs-project/littlefs.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: `littlefs <>`__
littlefs

A little fail-safe filesystem designed for microcontrollers.

   | | |     .---._____
  .-----.   |          |
--|o    |---| littlefs |
--|     |---|          |
  '-----'   '----------'
   | | |

Power-loss resilience - littlefs is designed to handle random power failures. All file operations have strong copy-on-write guarantees and if power is lost the filesystem will fall back to the last known good state.

Dynamic wear leveling - littlefs is designed with flash in mind, and provides wear leveling over dynamic blocks. Additionally, littlefs can detect bad blocks and work around them.

Bounded RAM/ROM - littlefs is designed to work with a small amount of memory. RAM usage is strictly bounded, which means RAM consumption does not change as the filesystem grows. The filesystem contains no unbounded recursion and dynamic memory is limited to configurable buffers that can be provided statically.

Example

Here’s a simple example that updates a file named boot_count every time main runs. The program can be interrupted at any time without losing track of how many times it has been booted and without corrupting the filesystem:

#include "lfs.h"

// variables used by the filesystem
lfs_t lfs;
lfs_file_t file;

// configuration of the filesystem is provided by this struct
const struct lfs_config cfg = {
    // block device operations
    .read  = user_provided_block_device_read,
    .prog  = user_provided_block_device_prog,
    .erase = user_provided_block_device_erase,
    .sync  = user_provided_block_device_sync,

    // block device configuration
    .read_size = 16,
    .prog_size = 16,
    .block_size = 4096,
    .block_count = 128,
    .cache_size = 16,
    .lookahead_size = 16,
    .block_cycles = 500,
};

// entry point
int main(void) {
    // mount the filesystem
    int err = lfs_mount(&lfs, &cfg);

    // reformat if we can't mount the filesystem
    // this should only happen on the first boot
    if (err) {
        lfs_format(&lfs, &cfg);
        lfs_mount(&lfs, &cfg);
    }

    // read current count
    uint32_t boot_count = 0;
    lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
    lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count));

    // update boot count
    boot_count += 1;
    lfs_file_rewind(&lfs, &file);
    lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count));

    // remember the storage is not updated until the file is closed successfully
    lfs_file_close(&lfs, &file);

    // release any resources we were using
    lfs_unmount(&lfs);

    // print the boot count
    printf("boot_count: %d\n", boot_count);
}
Usage

Detailed documentation (or at least as much detail as is currently available) can be found in the comments in lfs.h.

littlefs takes in a configuration structure that defines how the filesystem operates. The configuration struct provides the filesystem with the block device operations and dimensions, tweakable parameters that tradeoff memory usage for performance, and optional static buffers if the user wants to avoid dynamic memory.

The state of the littlefs is stored in the lfs_t type which is left up to the user to allocate, allowing multiple filesystems to be in use simultaneously. With the lfs_t and configuration struct, a user can format a block device or mount the filesystem.

Once mounted, the littlefs provides a full set of POSIX-like file and directory functions, with the deviation that the allocation of filesystem structures must be provided by the user.

All POSIX operations, such as remove and rename, are atomic, even in event of power-loss. Additionally, file updates are not actually committed to the filesystem until sync or close is called on the file.

Other notes

Littlefs is written in C, and specifically should compile with any compiler that conforms to the C99 standard.

All littlefs calls have the potential to return a negative error code. The errors can be either one of those found in the enum lfs_error in lfs.h, or an error returned by the user’s block device operations.

In the configuration struct, the prog and erase function provided by the user may return a LFS_ERR_CORRUPT error if the implementation already can detect corrupt blocks. However, the wear leveling does not depend on the return code of these functions, instead all data is read back and checked for integrity.

If your storage caches writes, make sure that the provided sync function flushes all the data to memory and ensures that the next read fetches the data from memory, otherwise data integrity can not be guaranteed. If the write function does not perform caching, and therefore each read or write call hits the memory, the sync function can simply return 0.

Design

At a high level, littlefs is a block based filesystem that uses small logs to store metadata and larger copy-on-write (COW) structures to store file data.

In littlefs, these ingredients form a sort of two-layered cake, with the small logs (called metadata pairs) providing fast updates to metadata anywhere on storage, while the COW structures store file data compactly and without any wear amplification cost.

Both of these data structures are built out of blocks, which are fed by a common block allocator. By limiting the number of erases allowed on a block per allocation, the allocator provides dynamic wear leveling over the entire filesystem.

                    root
                   .--------.--------.
                   | A'| B'|         |
                   |   |   |->       |
                   |   |   |         |
                   '--------'--------'
                .----'   '--------------.
       A       v                 B       v
      .--------.--------.       .--------.--------.
      | C'| D'|         |       | E'|new|         |
      |   |   |->       |       |   | E'|->       |
      |   |   |         |       |   |   |         |
      '--------'--------'       '--------'--------'
      .-'   '--.                  |   '------------------.
     v          v              .-'                        v
.--------.  .--------.        v                       .--------.
|   C    |  |   D    |   .--------.       write       | new E  |
|        |  |        |   |   E    |        ==>        |        |
|        |  |        |   |        |                   |        |
'--------'  '--------'   |        |                   '--------'
                         '--------'                   .-'    |
                         .-'    '-.    .-------------|------'
                        v          v  v              v
                   .--------.  .--------.       .--------.
                   |   F    |  |   G    |       | new F  |
                   |        |  |        |       |        |
                   |        |  |        |       |        |
                   '--------'  '--------'       '--------'

More details on how littlefs works can be found in DESIGN.md and SPEC.md.

  • DESIGN.md - A fully detailed dive into how littlefs works. I would suggest reading it as the tradeoffs at work are quite interesting.

  • SPEC.md - The on-disk specification of littlefs with all the nitty-gritty details. May be useful for tooling development.

Testing

The littlefs comes with a test suite designed to run on a PC using the emulated block device found in the emubd directory. The tests assume a Linux environment and can be started with make:

make test
License

The littlefs is provided under the BSD-3-Clause license. See LICENSE.md for more information. Contributions to this project are accepted under the same license.

Individual files contain the following tag instead of the full license text.

SPDX-License-Identifier:    BSD-3-Clause

This enables machine processing of license information based on the SPDX License Identifiers that are here available: http://spdx.org/licenses/

MCP23008 Port Expander

Library for communicating with MCP23008 8-bit port expander. Created by Garrett Blanton January, 24, 2014. Released into the public domain.

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

MCP23017 I2C Port Expander

https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library.git

This is a library for the MCP23017 I2c Port Expander

These chips use I2C to communicate, 2 pins required to interface

Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!

Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, check license.txt for more information All text above must be included in any redistribution

To download. click the DOWNLOADS button in the top right corner, rename the uncompressed folder Adafruit_MCP23017. Check that the Adafruit_MCP23017 folder contains Adafruit_MCP23017.cpp and Adafruit_MCP23017.h

Place the Adafruit_MCP23017 library folder your /libraries/ folder. You may need to create the libraries subfolder if its your first library. Restart the IDE.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

MCP23S17 SPI Port Expander

https://github.com/n0mjs710/MCP23S17.git

Arduino Driver for Microchip MCP23S17

MCP23S17 Class for Arduino

Introduction:

This class is written to simplify using the Microchip MCP23S17 general purpose I/O expander IC in the Arduino environment. Some understanding of the MCP23S17 is required, so if you are not familiar with it, download the datasheet for it and have a look. The rest of this description will assume a basic understanding of the chip.

Implementation:

The goal of this implementation is to provide a software interface that mimics the existing Arduino I/O functions:

  • pinMode(pin, mode)

  • digitalWrite(pin, value)

  • digitalRead(pin)

The class does include several more methods that can be used to simplify configuration in the same “Arduino-ish” way, methods for writing/reading 8-bit registers (configuration and I/O ports) at once, as well as writing/reading consecutive registers (allowing all 16 bits to be read or written with one method call). The interrupt features of the chip are not directly supported with method for specifically configuring them, however, the byte and word read/write methods may be used to configure and use the interrupt features. These features can get somewhat complicated, and any user prepared to use them will likely prefer the more generic methods for controlling them.

Upon initialization of an MCP23S17 as an object, ALL MCP23S17s on the SPI bus (sharing the same slave select) will be placed into hardware addressing mode. This allows up to 8 MCP23S17s to be used with a single slave select.

**Methods:**

MCP()

Description

Instantiate an MCP23S17 device as an object.

Syntax

MCP object_name(address, slave_select)

Parameters

object_name: any arbitrary name given to create the object

address: address (0-7) of the device configured with address (pins A0, A1, A2)

slave_select: a valid *Arduino* pin number.

Returns

none

Example

MCP onechip(1, 10);    // create an object at address 1 called "onechip", using pin 10 as slave select
MCP twochip(2, 10); // create an object at address 2 called "twochip", using pin 10 as slave select

pinMode()

Description

Configure pin(s) as either input or output on the selected object (device specified by an address)

Syntax

object.name.pinMode(pin, mode);
  or
object.name.pinMode(mode);

Parameters

object_name: the name given when this object was created

pin: the pin number (1-16) on which to set as input or output

mode: if a pin is specified, either a "HIGH" (1) for input (default) or a "LOW" (0) for output. If a pin is not specified, mode should be a word indicating the mode of each of the 16 I/O pins on the chip.

Returns

none

Example

void setup() {
  onechip.pinMode(4, HIGH); // sets pin 4 as an input
  onechip.pinMode(16, LOW); // sets pin 16 as an output
  twochip.pinMode(0B0000111100001111); // sets pins 1-4 and 9-12 as input, 5-8 and 13-16 as output
}

pullupMode()

Description

Configure the weak pull-up resistors on pins defined as inputs

This has no effect on pins that are configured as outputs.

Syntax

object_name.pullupMode(pin, mode);
 or
object_name.pullupMode(mode);

Parameters

object_name: the name given when this object was created

pin: the pin number (1-16) on which to enable or disable the internal weak pull-up resistor

mode: if a pin is specified, either "HIGH" (1) to enable or "LOW" (0) to disable (default) the weak pull-up resistor on the specified pin. If a pin is not specified, mode should be a word indicating the pull-up mode of each of the 16 pins on the chip. Configuring pull-up has no effect on pins while they are configured as output.

Returns

none

Example

void setup() {
  onechip.pullupMode(4, HIGH); // enable the pull-up on pin 4
  twochip.pullupMode(0B0000111100000000); // enable the pull-ups on pins 9-12
}

inputInvert()

Description

Configure inversion on pins configured as inputs.

This will cause an inverted input pin to read as “LOW” (0) when it is actually in a high state, or as “HIGH” (1) when it is actually in a low state. This has no effect on pins that are configured as outputs.

Syntax

object_name.inputInvert(pin, inversion);
  or
object_name.inputInvert(inversion);

Parameters

object_name: the name given when this object was created

pin: the pin number (1-16) on which to set or clear inversion

inversion: if a pin is specified, either "HIGH" (1) is specified to enable, or "LOW" (0) to disable (default) inversion on the specified pin. If a pin is not specified, mode should be a word indicating the inversion state of each of the 16 pins on the chip. Configuring inversion has no effect on pins while they are configured as output.

Returns

none

Example

void setup() {
  onechip.inputInvert(4, LOW); // disable inversion on pin 4
  twochip.inputInvert(0B0000000000001111); // enable inversion on pins 1-4
}

digitalWrite()

Description

Write a “HIGH” or “LOW” value to a digital I/O pin(s)

Syntax

object_name.digitalWrite(pin, value);
  or
object_name.digitalWrite(value);

Parameters

object_name: the name given when this object was created

pin: the pin number (1-16) who's value will be set

value: if a pin is specified, either a "HIGH" (1) or a "LOW" (0) value may be set on the specified pin. If a pin is not specified, value should be a word indicating the output state of all 16 pins on the device. Writing pins configured as inputs has no effect.

Returns

none

Example

void loop() {
   onechip.digitalWrite(16, HIGH); // set pin 16 to "HIGH"
   twochip.digitalWrite(0B1100000000110000); // Set 5, 6, 15 & 16 to high, 7,8, 13 & 14 to low - inputs ignored
}

digitalRead()

Description

Reads the value of input pin(s), either “HIGH” (“1”) or “LOW” (“0)

Syntax

object_name.digitalRead(pin);
  or
object_name.digitalRead();

Parameters

object_name: the name given when this object was created

pin: the pin number (1-16) who's value will be read. If no pin number is supplied, a word will be read containing the input state of all pins. The values for pins configured as output should be disregarded if the "word-mode" version is used.

Returns

"HIGH" (1) or "LOW" (0) if a pin is supplied. a word (16 bits) is returned if no pin argument is given

Example

void loop() {
  int onevalue;
  int twovalue;

  onevalue = onechip.digitalRead(4); // assigns the value of pin4 to onevalue
  twovalue = twochip.digitalRead(); // assigns the value of all 16 I/O pins to twovalue
}

wordWrite()

Description:

This is an advanced method to write a register pair in the MCP23S17. This class operates the MCP23S17 in “BANK=0” mode. The intention is that a registers for both ports may be written by supplying a single word as an argument. The low byte is written to the register address supplied, and the high byte to the next higher register address.

Syntax”

object_name.wordWrite(base register, value);

Parameters:

object_name: the name given when this object was created

base register: the beginning register address to write, for example, if 0x02 is given, the low byte of "value" will be written to 0x02 and the high byte of "value" to the register at 0x03

value: a word (unsigned int) that will be broken into two bytes and written to two consecutive registers, starting with the "base register" address

Returns:

none

Example

void loop() {
  onechip.wordWrite(0x12, 0xFF00); // Set GPIOA to 0x00 and GPIOB to OxFF
}

byteWrite()

Description:

This is an advanced method to write any single register in the MCP23S17.

Syntax:

object_name.byteWrite(register, value);

Parameters:

object_name: the name given when this object was created

register: the register address to write

value: a byte (unsigned char) that will be written to the specified registers

Returns:

none

Example:

void loop() {
  twochip.byteWrite(0x13, 0xF0); // Set GPIOB (portB) to 0xF0 (0B11110000)
}

byteRead()

Description:

This is an advanced method to read any single register in the MCP23S17.

Syntax:

object_name.byteRead(register);

Parameters:

object_name: the name given when this object was created

register: the register address to be read

Returns:

unsigned char (uint8_t)

Example:

void loop() {
  int twovalue;
  twovalue = twochip.byteRead(0x12); // Read GPIOA (portA)
}

Full Example:

MCP onechip(1, 10);    // create an object at address 1 called "onechip", using pin 10 as slave select
MCP twochip(2, 10); // create an object at address 2 called "twochip", using pin 10 as slave select

void setup() {
    onechip.pinMode(4, HIGH); // sets pin 4 as an input
    onechip.pinMode(16, LOW); // sets pin 16 as an output
    twochip.pinMode(0B0000111100001111); // sets pins 1-4 and 9-12 as input, 5-8 and 13-16 as output

    onechip.pullupMode(4, HIGH); // enable the pull-up on pin 4
    twochip.pullupMode(0B0000111100000000); // enable the pull-ups on pins 9-1

    onechip.inputInvert(4, LOW); // disable inversion on pin 4
    twochip.inputInvert(0B0000000000001111); // enable inversion on pins 1-4
}

void loop() {
  int onevalue;
  int twovalue;

  onechip.digitalWrite(16, HIGH); // set pin 16 to "HIGH"
  twochip.digitalWrite(0B1100000000110000); // Set 5, 6, 15 & 16 to high, 7,8, 13 & 14 to low - inputs ignored

  onevalue = onechip.digitalRead(4); // assigns the value of pin4 to onevalue
  twovalue = twochip.digitalRead(); // assigns the value of all 16 I/O pins to twovalue

  /* These are for context only - use them only if you really know what you're doing
  onechip.wordWrite(0x12, 0xFF00); // Set GPIOA to 0x00 and GPIOB to OxFF
  twochip.byteWrite(0x13, 0xF0); // Set GPIOB (portB) to 0xF0 (0B11110000)
  twovalue = twochip.byteRead(0x12); // Read GPIOA (portA)
  */
}
References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

MCP_CAN Library for Arduino

MCP_CAN library v1.5 This library is compatible with any shield or board that uses the MCP2515 or MCP25625 CAN protocol controller.

This version supports setting the ID filter mode of the protocol controller, the BAUD rate with clock speed with the begin() function. Baudrates 5k, 10k, 20k, 50k, 100k, 125k, 250k, 500k, & 1000k using 16MHz clock on the MCP2515 are confirmed to work using a Peak-System PCAN-USB dongle as a reference. Baudrates for 8MHz and 20MHz crystals are yet to be confirmed but were calculated appropriately.

The readMsgBuf() functions bring in the message ID. The getCanId() function is obsolete and no longer exists, don’t use it.

The readMsgBuf(*ID, *DLC, *DATA) function will return the ID type (extended or standard) and it will bring back the remote request status bit.
If the ID AND 0x80000000 EQUALS 0x80000000, the ID is of the Extended type, otherwise it is standard.
If the ID AND 0x40000000 EQUALS 0x40000000, the message is a remote request.

The readMsgBuf(*ID, *EXT, *DLC, *DATA) function will return the ID unaltered and doesn’t inform us of a remote request.
If EXT is true, the ID is extended.

The sendMsgBuf(ID, DLC, DATA) function can send extended or standard IDs.
To mark an ID as extended, OR the ID with 0x80000000.
To send a remote request, OR the ID with 0x40000000.

The sendMsgBuf(ID, EXT, DLC, DATA) has not changed other than fixing return values.

Using the setMode() function the sketch can now put the protocol controller into sleep, loop-back, or listen-only modes as well as normal operation. Right now the code defaults to loop-back mode after the begin() function runs. I have found this to increase the stability of filtering when the controller is initialized while connected to an active bus.

User can enable and disable (default) One-Shot transmission mode from the sketch using enOneShotTX() or disOneShotTX() respectively.

To wake up from CAN bus activity while in sleep mode enable the wake up interrupt with setSleepWakeup(1). Passing 0 will disable the wakeup interrupt (default).

Installation

Copy this into the “[…/MySketches/]libraries/” folder and restart the Arduino editor.

NOTE: If an older version of the library exists (e.g. CAN_BUS_Shield) be sure to remove it from the libraries folder or replace the files with those in this library to avoid conflicts.

Help and Support

This is primarily for non-bug related issues: Please start a new thread in an appropriate area at Seeedstudio forums or Arduino.cc forums and then send me (coryjfowler) a link through the PM system, my user name is the same as it is here. I will receive an email about the PM and generally get to it with-in a week or less. Keep in mind, I do this in my spare time.

Happy Coding!

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

mDNS: Multicast Domain Name System

https://en.wikipedia.org/wiki/Multicast_DNS

Responder

Sming provides the mDNS::Responder class to allow applications to advertise themselves on the local network.

To use:

  1. Add COMPONENT_DEPENDS += MDNS to your application componenent.mk file.

  2. Add these lines to your application:

    #include <Mdns/Responder.h>
    
    static mDNS::Responder responder;
    
    // Call when IP address has been obtained
    void startmDNS()
    {
       responder.begin(F("myhostname");
    }
    

This will advertise the device as myhostname.local.

To provide a custom service, implement a mDNS::Service class and call mDNS::Responder::addService().

See the UDP Server mDNS sample application.

Discovery

This library also provides support for device discovery using a separate set of classes, based on the mDNS::Server. See Basic MDNS for an example.

Testing

For linux, you can use avahi to perform mDNS queries and confirm output is as expected:

sudo apt install avahi
avahi-browse --all -r
References
API Documentation
namespace mDNS

Typedefs

using ResourceType = Resource::Type

Functions

void printQuestion(Print &p, Question &question)
void printAnswer(Print &p, Answer &answer)
void printMessage(Print &p, Message &message)

Variables

constexpr uint32_t MDNS_IP = {0xFB0000E0}
constexpr uint16_t MDNS_TARGET_PORT = {5353}
constexpr uint16_t MDNS_SOURCE_PORT = {5353}
constexpr uint16_t MDNS_TTL = {255}
constexpr uint16_t MAX_PACKET_SIZE = {1024}
Server server
class Answer : public LinkedObjectTemplate<Answer>
#include <Answer.h>

A single mDNS Answer.

class Handler : public LinkedObjectTemplate<Handler>
#include <Handler.h>

Virtual base class used for chaining message handlers.

Subclassed by mDNS::Responder

class Message
#include <Message.h>

Encapsulates a message packet for flexible introspection.

Subclassed by mDNS::Request

class Name
#include <Name.h>

Encoded DNS name.

mDNS-SD names are represented as instance.service.domain. See https://tools.ietf.org/html/rfc6763#section-4.1

Instance names should be friendly and not attempt to be unique. See https://tools.ietf.org/html/rfc6763#appendix-D

Example: “UDP Server._http._tcp.local” instance: “UDP Server” service: “_http._tcp” name: “http” protocol: Protocol::Tcp domain: “local”

class Question : public LinkedObjectTemplate<Question>
#include <Question.h>

A single mDNS Question.

class Request : public mDNS::Message
#include <Request.h>

Subclassed by mDNS::Query, mDNS::Reply

class Query : public mDNS::Request
#include <Request.h>
class Reply : public mDNS::Request
#include <Request.h>
class Responder : public mDNS::Handler
#include <Responder.h>

Special name for querying list of services.

class Server : protected UdpConnection
#include <Server.h>

Locates mDNS services by issuing queries.

class Service : public LinkedObjectTemplate<Service>
#include <Service.h>

Describes a basic service.

The default methods translate to a DNS-SD name of “Sming._http._tcp.local”. See :cpp:class:mDNS::Name for a description of how names are defined.

namespace Resource

Enums

enum class Type : uint16_t

Values:

enumerator XX
class Record
#include <Resource.h>

Resource Record with no specific type.

Subclassed by mDNS::Resource::A, mDNS::Resource::AAAA, mDNS::Resource::HINFO, mDNS::Resource::PTR, mDNS::Resource::SRV, mDNS::Resource::TXT

class A : public mDNS::Resource::Record
#include <Resource.h>

A’ record containing IP4 address

class PTR : public mDNS::Resource::Record
#include <Resource.h>

PTR’ record containing pointer to a canonical name

class HINFO : public mDNS::Resource::Record
#include <Resource.h>

HINFO’ record containing Host information

class TXT : public mDNS::Resource::Record
#include <Resource.h>

TXT’ record containing attribute list

Originally for arbitrary human-readable text in a DNS record. Content is a set of name=value pairs. Value can be binary.

class AAAA : public mDNS::Resource::Record
#include <Resource.h>

AAAA’ record containing 128-bit IPv6 address

class SRV : public mDNS::Resource::Record
#include <Resource.h>

SRVService Locator record

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

MFRC522 RFID Module

Note

This is a copy of the readme from 12/6/19, however the code in this library was last updated 23/3/2017.

https://github.com/miguelbalboa/rfid.git

Arduino library for MFRC522 and other RFID RC522 based modules.

Read and write different types of Radio-Frequency IDentification (RFID) cards on your Arduino using a RC522 based reader connected via the Serial Peripheral Interface (SPI) interface.

Development

The development by owner miguelbalboa has ended.

Feature status: complete freeze; no function or API change

Code status: partial freeze; just fixes/typos or documentation updates; no extensions for other boards; no new examples

Maintenance status: sporadically

Why no further development? This library has a long history and is used in many projects. This projects often do not document what version they use. Committing changes maybe brake those old project and lead to bad experience (for beginners) and support request. For those reasons the library is in freeze mode. You can still commit typo, documentation or bug fixes.

Before buy

Please notice that there are many sellers (ebay, aliexpress, ..) who sell mfrc522 boards. The quality of these boards are extremely different. Some are soldered with wrong/low quality capacitors or fake/defect mfrc522.

Please consider buying several devices from different suppliers. So the chance of getting a working device is higher.

If you got a bad board and you can tell us how to detect those boards (silk, chip description, ..), please share your knowledge.

What works and not?
  • Works

    1. Communication (Crypto1) with MIFARE Classic (1k, 4k, Mini).

    2. Communication (Crypto1) with MIFARE Classic compatible PICCs.

    3. Firmware self check of MFRC522.

    4. Set the UID, write to sector 0, and unbrick Chinese UID changeable MIFARE cards.

    5. Manage the SPI chip select pin (aka SS, SDA)

  • Works partially

    1. Communication with MIFARE Ultralight.

    2. Other PICCs (Ntag216).

    3. More than 2 modules, require a multiplexer #191.

  • Doesn’t work

    1. MIFARE DESFire, MIFARE DESFire EV1/EV2, not supported by software.

    2. Communication with 3DES or AES, not supported by software.

    3. Peer-to-peer (ISO/IEC 18092), not supported by hardware.

    4. Communication with smart phone, not supported by hardware.

    5. Card emulation, not supported by hardware.

    6. Use of IRQ pin. But there is a proof-of-concept example.

    7. With Arduino Yun see #111, not supported by software.

    8. With Intel Galileo (Gen2) see #310, not supported by software.

    9. Power reduction modes #269, not supported by software.

    10. I2C instead of SPI #240, not supported by software.

    11. UART instead of SPI #281, not supported by software.

  • Need more?

    1. If software: code it and make a pull request.

    2. If hardware: buy a more expensive like PN532 (supports NFC and many more, but costs about $15 and not usable with this library).

Compatible IDE

This library works with Arduino IDE 1.6, older versions are not supported and will cause compiler errors. The built-in library manager is supported.

If you use your own compiler, you have to enable c++11-support.

Compatible boards

!!!Only for advanced users!!!

This library is compatible with the Teensy and ESP8266 if you use the board plugin of the Arduino IDE. Not all examples are available for every board. You also have to change pins. See pin layout.

Some user made some patches/suggestions/ports for other boards:

Note that the main target/support of library is still Arduino.

Support/issue
  1. First checkout what works and not and troubleshooting .

  2. It seems to be a hardware issue or you need support to program your project?

    Please ask in the official Arduino forum, where you would get a much faster answer than on Github.

  3. It seems to be a software issue?

    Open an issue on Github.

Code style

Please use fixed integers, see stdint.h. Why? This library is compatible with different boards which use different architectures (16bit and 32bit.) Unfixed int variables have different sizes in different environments and may cause unpredictable behaviour.

Pin Layout

The following table shows the typical pin layout used:

PCD

MFRC522

Arduino

Uno / 101

Teensy

Mega

Nano v3

Leonardo / Micro

Pro Micro

2.0

++ 2.0

3.1

Signal

Pin

Pin

Pin

Pin

Pin

Pin

Pin

Pin

Pin

RST/Reset

RST

9 [1]

5 [1]

D9

RESET / ICSP-5

RST

7

4

9

SPI SS

SDA [3]

10 [2]

53 [2]

D10

10

10

0

20

10

SPI MOSI

MOSI

11 / ICSP-4

51

D11

ICSP-4

16

2

22

11

SPI MISO

MISO

12 / ICSP-1

50

D12

ICSP-1

14

3

23

12

SPI SCK

SCK

13 / ICSP-3

52

D13

ICSP-3

15

1

21

13

ESP8266

Wemos D1 mini

Signal

Pin

RST/Reset

D3

SPI SS

D8

SPI MOSI

D7

SPI MISO

D6

SPI SCK

D5

Hardware

There are three hardware components involved:

  1. Micro Controller:

  • An Arduino or compatible executing the Sketch using this library.

  • Prices vary from USD 7 for clones, to USD 75 for “starter kits” (which might be a good choice if this is your first exposure to Arduino; check if such kit already includes the Arduino, Reader, and some Tags).

  1. Proximity Coupling Device (PCD):

  • The PCD is the actual RFID Reader based on the NXP MFRC522 Contactless Reader Integrated Circuit.

  • Readers can be found on eBay for around USD 5: search for “rc522”.

  • You can also find them on several web stores. They are often included in “starter kits”, so check your favourite electronics provider as well.

  1. Proximity Integrated Circuit Card (PICC):

  • The PICC is the RFID Card or Tag using the ISO/IEC 14443A interface, for example Mifare or NTAG203.

  • One or two might be included with the Reader or “starter kit” already.

Protocols
  1. The micro controller and the reader use SPI for communication.

  • The protocol is described in the NXP MFRC522 datasheet.

  • See the Pin Layout section for details on connecting the pins.

  1. The reader and the tags communicate using a 13.56 MHz electromagnetic field.

  • The protocol is defined in ISO/IEC 14443-3:2011 Part 3 Type A.

    • Details are found in chapter 6 “Type A – Initialization and anticollision”.

    • See http://wg8.de/wg8n1496_17n3613_Ballot_FCD14443-3.pdf for a free version of the final draft (which might be outdated in some areas).

    • The reader does not support ISO/IEC 14443-3 Type B.

Security
  • The UID of a card can not be used as an unique identification for security related projects. Some Chinese cards allow to change the UID which means you can easily clone a card. For projects like access control, door opener or payment systems you must implement an additional security mechanism like a password or normal key.

  • This library only supports crypto1-encrypted communication. Crypto1 has been known as broken for a few years, so it does NOT offer ANY security, it is virtually unencrypted communication. Do not use it for any security related applications!

  • This library does not offer 3DES or AES authentication used by cards like the Mifare DESFire, it may be possible to be implemented because the datasheet says there is support. We hope for pull requests :).

Troubleshooting
  • I don’t get input from reader or WARNING: Communication failure, is the MFRC522 properly connected?

    1. Check your physical connection, see Pin Layout .

    2. Check your pin settings/variables in the code, see Pin Layout .

    3. Check your pin header soldering. Maybe you have cold solder joints.

    4. Check voltage. Most breakouts work with 3.3V.

    5. SPI only works with 3.3V, most breakouts seem 5V tolerant, but try a level shifter.

    6. SPI does not like long connections. Try shorter connections.

    7. SPI does not like prototyping boards. Try soldered connections.

    8. According to reports #101, #126 and #131, there may be a problem with the soldering on the MFRC522 breakout. You could fix this on your own.

  • Firmware Version: 0x12 = (unknown) or other random values

    1. The exact reason of this behaviour is unknown.

    2. Some boards need more time after PCD_Init() to be ready. As workaround add a delay(4) directly after PCD_Init() to give the PCD more time.

    3. If this sometimes appears, a bad connection or power source is the reason.

    4. If the firmware version is reported permanent, it is very likely that the hardware is a fake or has a defect. Contact your supplier.

  • Sometimes I get timeouts or sometimes tag/card does not work.

    1. Try the other side of the antenna.

    2. Try to decrease the distance between the MFRC522 and your tag.

    3. Increase the antenna gain per firmware: mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_max);

    4. Use better power supply.

    5. Hardware may be corrupted, most products are from china and sometimes the quality is really poor. Contact your seller.

  • My tag/card doesn’t work.

    1. Distance between antenna and token too large (>1cm).

    2. You got the wrong type PICC. Is it really 13.56 MHz? Is it really a Mifare Type A?

    3. NFC tokens are not supported. Some may work.

    4. Animal RFID tags are not supported. They use a different frequency (125 kHz).

    5. Hardware may be corrupted, most products are from china and sometimes the quality is really poor. Contact your seller.

    6. Newer versions of Mifare cards like DESFire/Ultralight maybe not work according to missing authentication, see security or different protocol.

    7. Some boards bought from Chinese manufactures do not use the best components and this can affect the detection of different types of tag/card. In some of these boards, the L1 and L2 inductors do not have a high enough current so the signal generated is not enough to get Ultralight C and NTAG203 tags to work, replacing those with same inductance (2.2uH) but higher operating current inductors should make things work smoothly. Also, in some of those boards the harmonic and matching circuit needs to be tuned, for this replace C4 and C5 with 33pf capacitors and you are all set. (Source: Mikro Elektronika)

  • My mobile phone doesn’t recognize the MFRC522 or my MFRC522 can’t read data from other MFRC522

    1. Card simulation is not supported.

    2. Communication with mobile phones is not supported.

    3. Peer to peer communication is not supported.

  • I can only read the card UID.

    1. Maybe the AccessBits have been accidentally set and now an unknown password is set. This can not be reverted.

    2. Probably the card is encrypted. Especially official cards like public transport, university or library cards. There is no way to get access with this library.

  • I need more features.

    1. If software: code it and make a pull request.

    2. If hardware: buy a more expensive chip like the PN532 (supports NFC and many more, but costs about $15)

License

This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.

In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to https://unlicense.org/

Dependency
  • Arduino.h

    • From: Arduino IDE / target specific

    • License: (target: Arduino) GNU Lesser General Public License 2.1

  • SPI.h

    • From: Arduino IDE / target specific

    • License: (target: Arduino) GNU Lesser General Public License 2.1

  • stdint.h

    • From: Arduino IDE / Compiler and target specific

    • License: different

History

The MFRC522 library was first created in Jan 2012 by Miguel Balboa (from http://circuitito.com) based on code by Dr. Leong (from http://B2CQSHOP.com) for “Arduino RFID module Kit 13.56 Mhz with Tags SPI W and R By COOQRobot”.

It was translated into English and rewritten/refactored in the fall of 2013 by Søren Thing Andersen (from http://access.thing.dk).

It has been extended with functionality to alter sector 0 on Chinese UID changeable MIFARE card in Oct 2014 by Tom Clement (from http://tomclement.nl).

Maintained by miguelbalboa until 2016. Maintained by Rotzbua from 2016 until 2018.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

MHZ19 CO2 Sensor

Sming library supporting the MH-Z19 and MH-Z19B CO2 sensors.

Essentially a re-write of https://github.com/crisap94/MHZ19 to use Hardware serial port with callbacks.

See https://github.com/WifWaf/MH-Z19 for some very detailed and helpful background to the device.

Functionality is split into two main classes:

MHZ19::Uart

Access the sensor via serial port

MHZ19::PwmReader

Asynchronously decodes PWM signal from sensor

ESP8266 Connections

Communication requires use of UART0, as UART1 is output-only.

The samples in this library reconfigure the default Serial class to use UART1 for debug output.

UART0 is reconfigured to use the alternate pins by calling HardwareSerial::swap(). Connect as follows:

From

To

Notes

MHZ19 PWM

GPIO14 (D5)

MHZ19 TX

GPIO13 (D7)

UART0 RX

MHZ19 RX

GPIO15 (D8)

UART0 TX

GPIO2 (D4)

GPIO1 (TX)

Route UART1 output to USB

Note: A similar approach is used for I2S, see Tone Generator.

References
API Documentation
namespace MHZ19

Typedefs

using MeasurementCallback = Delegate<void(Measurement &m)>

Enums

enum class DetectionRange

Device may be configured to output CO2 PPM values in various ranges.

Values:

enumerator PPM_2000
enumerator PPM_5000
enumerator PPM_10000
enum Command

Available commands.

Support differs by device variant

Values:

enumerator CMD_GasConcentration

-, B, C

enumerator CMD_CalibrateZeroPoint

-, B

enumerator CMD_CalibrateSpanPoint

-, B

enumerator CMD_SelfCalbrationOnOff

B, C.

enumerator CMD_SetDetectionRange

B.

enum class Error

Values:

enumerator success
enumerator incompleteResponse
enumerator invalidResponse
enumerator timeout

Functions

unsigned pwmRead(uint8_t pwmPin, DetectionRange range)

Read PWM output from sensor.

Note

This will hang CPU for 1-2 seconds. Use PwmReader instead.

Parameters:
  • pwmPin – GPIO to which the sensor is connected

  • range – Range sensor is configured for

class PwmReader
#include <PwmReader.h>

Reads input pulse width asynchronously.

The ESP8266 lacks any timing capture hardware but as the pulse output from the MHZ19 is very slow it can be decoded using interrupts with high accuracy.

Once started, the PwmReader runs continuously and the last value can be obtained by calling getMeasurement.

Public Types

using Callback = Delegate<void(uint16_t ppm)>

Callback for regular readings.

Public Functions

void begin(uint8_t pin, DetectionRange range)

Start the PWM reader.

Runs continuously in background until end() is called.

Parameters:
  • pin – GPIO to read PWM signal on

  • range – Configured device range

void end()

Stop the PWM reader.

uint16_t getMeasurement() const

Obtain the most recent measurement.

inline void setCallback(Callback callback)

Set a callback to be invoked whenever a valid reading is obtained.

bool suspend()

Temporarily suspend readings and disable interrupts on the PWM pin.

Return values:

bool – true on success, false if reader not initialised

bool resume()

Resume reading after a call to suspend()

Return values:

bool – true on success, false if reader not initialised

union Pulse
#include <PwmReader.h>

Used internally to measure a high/low pulse pair.

Public Functions

inline uint16_t getCycleTime() const
inline bool isValid() const
inline uint16_t calculatePpm(DetectionRange range)

Public Members

uint16_t low
uint16_t high
struct MHZ19::PwmReader::Pulse::[anonymous] [anonymous]
uint32_t value
struct Request
#include <Uart.h>
struct Response
#include <Uart.h>
struct Measurement
#include <Uart.h>
class Uart
#include <Uart.h>

Access MHZ19 sensor via serial port.

Public Functions

inline Uart(HardwareSerial &serial)

Use device in UART mode.

Parameters:

serial – Port and pins must be pre-configured

inline void setSensorNumber(uint8_t num)

Change sensor number field.

Original datasheet identifies first byte as sensor number, others as ‘reserved’. Provided if this value changes in future hardware versions.

inline uint8_t getSensorNumber() const

Get currently configured sensor number.

void setAutoCalibration(bool enable)

Enable/disable zero-point auto-calibration feature. On by default.

Calibrates 400ppm zero point reference automatically at power-on and every 24 hours. Suitable for home/office environment only. Refer to datasheet for details.

void calibrateZero()

Calibrate zero point manually.

Sensor must be in stable 400ppm CO2 environment. Refer to datasheet for detailed procedure.

void calibrateSpan(uint16_t ppm)

Calibrate span point.

Typical calibration value is 2000ppm, but must be above 1000ppm. Refer to datasheet for detailed procedure.

void setDetectionRange(DetectionRange range)

MHZ-19B has configurable detection range.

bool getMeasurement(MeasurementCallback callback)

Read measurement from device.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

MMA-7455 Accelerometer

Library for the MMA-7455 3-axis accelerometer

GIT repository

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

MPU6050 Gyro / Accelerometer

MPU6050 Six-Axis (Gyro + Accelerometer) Based on code from jrowberg/i2cdevlib @ 605a740. Most of the code is the same, except:

  • Removed MPU6050::ReadRegister function due to incompatibility. It is also not used anywhere in the original code.

  • MPU6050_6Axis_MotionApps20.h and MPU6050_9Axis_MotionApps41.h are not included due to deps to freeRTOS. helper_3dmath.h is also not included since it is only used in the above mentioned files.

  • Removed map function in favor of the Sming built-in one.

  • Adapted include path, coding style and applied clangformat

  • Deleted Calibration and Memory Block related code for code quality reason

API Documentation
class MPU6050

Public Functions

inline MPU6050()

Default constructor, uses default I2C address.

See also

MPU6050_DEFAULT_ADDRESS

inline MPU6050(uint8_t address)

Specific address constructor.

See also

MPU6050_DEFAULT_ADDRESS

See also

MPU6050_ADDRESS_AD0_LOW

See also

MPU6050_ADDRESS_AD0_HIGH

Parameters:

address – I2C address

void initialize()

Power on and prepare for general usage. This will activate the device and take it out of sleep mode (which must be done after start-up). This function also sets both the accelerometer and the gyroscope to their most sensitive settings, namely +/- 2g and +/- 250 degrees/sec, and sets the clock source to use the X Gyro for reference, which is slightly better than the default internal clock source.

inline bool testConnection()

Verify the I2C connection. Make sure the device is connected and responds as expected.

Returns:

True if connection is valid, false otherwise

inline uint8_t getAuxVDDIOLevel()

Get the auxiliary I2C supply voltage level. When set to 1, the auxiliary I2C bus high logic level is VDD. When cleared to 0, the auxiliary I2C bus high logic level is VLOGIC. This does not apply to the MPU-6000, which does not have a VLOGIC pin.

Returns:

I2C supply voltage level (0=VLOGIC, 1=VDD)

inline void setAuxVDDIOLevel(uint8_t level)

Set the auxiliary I2C supply voltage level. When set to 1, the auxiliary I2C bus high logic level is VDD. When cleared to 0, the auxiliary I2C bus high logic level is VLOGIC. This does not apply to the MPU-6000, which does not have a VLOGIC pin.

Parameters:

level – I2C supply voltage level (0=VLOGIC, 1=VDD)

inline uint8_t getRate()

Get gyroscope output rate divider. The sensor register output, FIFO output, DMP sampling, Motion detection, Zero Motion detection, and Free Fall detection are all based on the Sample Rate. The Sample Rate is generated by dividing the gyroscope output rate by SMPLRT_DIV:

Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)

where Gyroscope Output Rate = 8kHz when the DLPF is disabled (DLPF_CFG = 0 or 7), and 1kHz when the DLPF is enabled (see Register 26).

Note: The accelerometer output rate is 1kHz. This means that for a Sample Rate greater than 1kHz, the same accelerometer sample may be output to the FIFO, DMP, and sensor registers more than once.

For a diagram of the gyroscope and accelerometer signal paths, see Section 8 of the MPU-6000/MPU-6050 Product Specification document.

See also

MPU6050_RA_SMPLRT_DIV

Returns:

Current sample rate

inline void setRate(uint8_t rate)

Set gyroscope sample rate divider.

See also

getRate()

See also

MPU6050_RA_SMPLRT_DIV

Parameters:

rate – New sample rate divider

inline uint8_t getExternalFrameSync()

Get external FSYNC configuration. Configures the external Frame Synchronization (FSYNC) pin sampling. An external signal connected to the FSYNC pin can be sampled by configuring EXT_SYNC_SET. Signal changes to the FSYNC pin are latched so that short strobes may be captured. The latched FSYNC signal will be sampled at the Sampling Rate, as defined in register 25. After sampling, the latch will reset to the current FSYNC signal state.

The sampled value will be reported in place of the least significant bit in a sensor data register determined by the value of EXT_SYNC_SET according to the following table.

Returns:

FSYNC configuration value

inline void setExternalFrameSync(uint8_t sync)

Set external FSYNC configuration.

See also

MPU6050_RA_CONFIG

Parameters:

sync – New FSYNC configuration value

inline uint8_t getDLPFMode()

Get digital low-pass filter configuration. The DLPF_CFG parameter sets the digital low pass filter configuration. It also determines the internal sampling rate used by the device as shown in the table below.

Note: The accelerometer output rate is 1kHz. This means that for a Sample Rate greater than 1kHz, the same accelerometer sample may be output to the FIFO, DMP, and sensor registers more than once.

See also

MPU6050_RA_CONFIG

See also

MPU6050_CFG_DLPF_CFG_BIT

See also

MPU6050_CFG_DLPF_CFG_LENGTH

Returns:

DLFP configuration

inline void setDLPFMode(uint8_t mode)

Set digital low-pass filter configuration.

See also

getDLPFBandwidth()

See also

MPU6050_DLPF_BW_256

See also

MPU6050_RA_CONFIG

See also

MPU6050_CFG_DLPF_CFG_BIT

See also

MPU6050_CFG_DLPF_CFG_LENGTH

Parameters:

mode – New DLFP configuration setting

inline uint8_t getFullScaleGyroRange()

Get full-scale gyroscope range. The FS_SEL parameter allows setting the full-scale range of the gyro sensors, as described in the table below.

See also

MPU6050_GYRO_FS_250

See also

MPU6050_RA_GYRO_CONFIG

See also

MPU6050_GCONFIG_FS_SEL_BIT

See also

MPU6050_GCONFIG_FS_SEL_LENGTH

Returns:

Current full-scale gyroscope range setting

inline void setFullScaleGyroRange(uint8_t range)

Set full-scale gyroscope range.

See also

getFullScaleRange()

See also

MPU6050_GYRO_FS_250

See also

MPU6050_RA_GYRO_CONFIG

See also

MPU6050_GCONFIG_FS_SEL_BIT

See also

MPU6050_GCONFIG_FS_SEL_LENGTH

Parameters:

range – New full-scale gyroscope range value

uint8_t getAccelXSelfTestFactoryTrim()

Get self-test factory trim value for accelerometer X axis.

See also

MPU6050_RA_SELF_TEST_X

Returns:

factory trim value

uint8_t getAccelYSelfTestFactoryTrim()

Get self-test factory trim value for accelerometer Y axis.

See also

MPU6050_RA_SELF_TEST_Y

Returns:

factory trim value

uint8_t getAccelZSelfTestFactoryTrim()

Get self-test factory trim value for accelerometer Z axis.

See also

MPU6050_RA_SELF_TEST_Z

Returns:

factory trim value

uint8_t getGyroXSelfTestFactoryTrim()

Get self-test factory trim value for gyro X axis.

See also

MPU6050_RA_SELF_TEST_X

Returns:

factory trim value

uint8_t getGyroYSelfTestFactoryTrim()

Get self-test factory trim value for gyro Y axis.

See also

MPU6050_RA_SELF_TEST_Y

Returns:

factory trim value

uint8_t getGyroZSelfTestFactoryTrim()

Get self-test factory trim value for gyro Z axis.

See also

MPU6050_RA_SELF_TEST_Z

Returns:

factory trim value

inline bool getAccelXSelfTest()

Get self-test enabled setting for accelerometer X axis.

See also

MPU6050_RA_ACCEL_CONFIG

Returns:

Self-test enabled value

inline void setAccelXSelfTest(bool enabled)

Get self-test enabled setting for accelerometer X axis.

See also

MPU6050_RA_ACCEL_CONFIG

Parameters:

enabled – Self-test enabled value

inline bool getAccelYSelfTest()

Get self-test enabled value for accelerometer Y axis.

See also

MPU6050_RA_ACCEL_CONFIG

Returns:

Self-test enabled value

inline void setAccelYSelfTest(bool enabled)

Get self-test enabled value for accelerometer Y axis.

See also

MPU6050_RA_ACCEL_CONFIG

Parameters:

enabled – Self-test enabled value

inline bool getAccelZSelfTest()

Get self-test enabled value for accelerometer Z axis.

See also

MPU6050_RA_ACCEL_CONFIG

Returns:

Self-test enabled value

inline void setAccelZSelfTest(bool enabled)

Set self-test enabled value for accelerometer Z axis.

See also

MPU6050_RA_ACCEL_CONFIG

Parameters:

enabled – Self-test enabled value

inline uint8_t getFullScaleAccelRange()

Get full-scale accelerometer range. The FS_SEL parameter allows setting the full-scale range of the accelerometer sensors, as described in the table below.

See also

MPU6050_ACCEL_FS_2

See also

MPU6050_RA_ACCEL_CONFIG

See also

MPU6050_ACONFIG_AFS_SEL_BIT

See also

MPU6050_ACONFIG_AFS_SEL_LENGTH

Returns:

Current full-scale accelerometer range setting

inline void setFullScaleAccelRange(uint8_t range)

Set full-scale accelerometer range.

Parameters:

range – New full-scale accelerometer range setting

inline uint8_t getDHPFMode()

Get the high-pass filter configuration. The DHPF is a filter module in the path leading to motion detectors (Free Fall, Motion threshold, and Zero Motion). The high pass filter output is not available to the data registers (see Figure in Section 8 of the MPU-6000/ MPU-6050 Product Specification document).

The high pass filter has three modes:

See also

MPU6050_DHPF_RESET

See also

MPU6050_RA_ACCEL_CONFIG

Returns:

Current high-pass filter configuration

inline void setDHPFMode(uint8_t bandwidth)

Set the high-pass filter configuration.

See also

setDHPFMode()

See also

MPU6050_DHPF_RESET

See also

MPU6050_RA_ACCEL_CONFIG

Parameters:

bandwidth – New high-pass filter configuration

inline uint8_t getFreefallDetectionThreshold()

Get free-fall event acceleration threshold. This register configures the detection threshold for Free Fall event detection. The unit of FF_THR is 1LSB = 2mg. Free Fall is detected when the absolute value of the accelerometer measurements for the three axes are each less than the detection threshold. This condition increments the Free Fall duration counter (Register 30). The Free Fall interrupt is triggered when the Free Fall duration counter reaches the time specified in FF_DUR.

For more details on the Free Fall detection interrupt, see Section 8.2 of the MPU-6000/MPU-6050 Product Specification document as well as Registers 56 and 58 of this document.

See also

MPU6050_RA_FF_THR

Returns:

Current free-fall acceleration threshold value (LSB = 2mg)

inline void setFreefallDetectionThreshold(uint8_t threshold)

Get free-fall event acceleration threshold.

See also

MPU6050_RA_FF_THR

Parameters:

threshold – New free-fall acceleration threshold value (LSB = 2mg)

inline uint8_t getFreefallDetectionDuration()

Get free-fall event duration threshold. This register configures the duration counter threshold for Free Fall event detection. The duration counter ticks at 1kHz, therefore FF_DUR has a unit of 1 LSB = 1 ms.

The Free Fall duration counter increments while the absolute value of the accelerometer measurements are each less than the detection threshold (Register 29). The Free Fall interrupt is triggered when the Free Fall duration counter reaches the time specified in this register.

For more details on the Free Fall detection interrupt, see Section 8.2 of the MPU-6000/MPU-6050 Product Specification document as well as Registers 56 and 58 of this document.

See also

MPU6050_RA_FF_DUR

Returns:

Current free-fall duration threshold value (LSB = 1ms)

inline void setFreefallDetectionDuration(uint8_t duration)

Get free-fall event duration threshold.

See also

MPU6050_RA_FF_DUR

Parameters:

duration – New free-fall duration threshold value (LSB = 1ms)

inline uint8_t getMotionDetectionThreshold()

Get motion detection event acceleration threshold. This register configures the detection threshold for Motion interrupt generation. The unit of MOT_THR is 1LSB = 2mg. Motion is detected when the absolute value of any of the accelerometer measurements exceeds this Motion detection threshold. This condition increments the Motion detection duration counter (Register 32). The Motion detection interrupt is triggered when the Motion Detection counter reaches the time count specified in MOT_DUR (Register 32).

The Motion interrupt will indicate the axis and polarity of detected motion in MOT_DETECT_STATUS (Register 97).

For more details on the Motion detection interrupt, see Section 8.3 of the MPU-6000/MPU-6050 Product Specification document as well as Registers 56 and 58 of this document.

See also

MPU6050_RA_MOT_THR

Returns:

Current motion detection acceleration threshold value (LSB = 2mg)

inline void setMotionDetectionThreshold(uint8_t threshold)

Set motion detection event acceleration threshold.

See also

MPU6050_RA_MOT_THR

Parameters:

threshold – New motion detection acceleration threshold value (LSB = 2mg)

inline uint8_t getMotionDetectionDuration()

Get motion detection event duration threshold. This register configures the duration counter threshold for Motion interrupt generation. The duration counter ticks at 1 kHz, therefore MOT_DUR has a unit of 1LSB = 1ms. The Motion detection duration counter increments when the absolute value of any of the accelerometer measurements exceeds the Motion detection threshold (Register 31). The Motion detection interrupt is triggered when the Motion detection counter reaches the time count specified in this register.

For more details on the Motion detection interrupt, see Section 8.3 of the MPU-6000/MPU-6050 Product Specification document.

See also

MPU6050_RA_MOT_DUR

Returns:

Current motion detection duration threshold value (LSB = 1ms)

inline void setMotionDetectionDuration(uint8_t duration)

Set motion detection event duration threshold.

See also

MPU6050_RA_MOT_DUR

Parameters:

duration – New motion detection duration threshold value (LSB = 1ms)

inline uint8_t getZeroMotionDetectionThreshold()

Get zero motion detection event acceleration threshold. This register configures the detection threshold for Zero Motion interrupt generation. The unit of ZRMOT_THR is 1LSB = 2mg. Zero Motion is detected when the absolute value of the accelerometer measurements for the 3 axes are each less than the detection threshold. This condition increments the Zero Motion duration counter (Register 34). The Zero Motion interrupt is triggered when the Zero Motion duration counter reaches the time count specified in ZRMOT_DUR (Register 34).

Unlike Free Fall or Motion detection, Zero Motion detection triggers an interrupt both when Zero Motion is first detected and when Zero Motion is no longer detected.

When a zero motion event is detected, a Zero Motion Status will be indicated in the MOT_DETECT_STATUS register (Register 97). When a motion-to-zero-motion condition is detected, the status bit is set to 1. When a zero-motion-to- motion condition is detected, the status bit is set to 0.

For more details on the Zero Motion detection interrupt, see Section 8.4 of the MPU-6000/MPU-6050 Product Specification document as well as Registers 56 and 58 of this document.

See also

MPU6050_RA_ZRMOT_THR

Returns:

Current zero motion detection acceleration threshold value (LSB = 2mg)

inline void setZeroMotionDetectionThreshold(uint8_t threshold)

Set zero motion detection event acceleration threshold.

See also

MPU6050_RA_ZRMOT_THR

Parameters:

threshold – New zero motion detection acceleration threshold value (LSB = 2mg)

inline uint8_t getZeroMotionDetectionDuration()

Get zero motion detection event duration threshold. This register configures the duration counter threshold for Zero Motion interrupt generation. The duration counter ticks at 16 Hz, therefore ZRMOT_DUR has a unit of 1 LSB = 64 ms. The Zero Motion duration counter increments while the absolute value of the accelerometer measurements are each less than the detection threshold (Register 33). The Zero Motion interrupt is triggered when the Zero Motion duration counter reaches the time count specified in this register.

For more details on the Zero Motion detection interrupt, see Section 8.4 of the MPU-6000/MPU-6050 Product Specification document, as well as Registers 56 and 58 of this document.

See also

MPU6050_RA_ZRMOT_DUR

Returns:

Current zero motion detection duration threshold value (LSB = 64ms)

inline void setZeroMotionDetectionDuration(uint8_t duration)

Set zero motion detection event duration threshold.

See also

MPU6050_RA_ZRMOT_DUR

Parameters:

duration – New zero motion detection duration threshold value (LSB = 1ms)

inline bool getTempFIFOEnabled()

Get temperature FIFO enabled value. When set to 1, this bit enables TEMP_OUT_H and TEMP_OUT_L (Registers 65 and 66) to be written into the FIFO buffer.

See also

MPU6050_RA_FIFO_EN

Returns:

Current temperature FIFO enabled value

inline void setTempFIFOEnabled(bool enabled)

Set temperature FIFO enabled value.

See also

MPU6050_RA_FIFO_EN

Parameters:

enabled – New temperature FIFO enabled value

inline bool getXGyroFIFOEnabled()

Get gyroscope X-axis FIFO enabled value. When set to 1, this bit enables GYRO_XOUT_H and GYRO_XOUT_L (Registers 67 and 68) to be written into the FIFO buffer.

See also

MPU6050_RA_FIFO_EN

Returns:

Current gyroscope X-axis FIFO enabled value

inline void setXGyroFIFOEnabled(bool enabled)

Set gyroscope X-axis FIFO enabled value.

See also

MPU6050_RA_FIFO_EN

Parameters:

enabled – New gyroscope X-axis FIFO enabled value

inline bool getYGyroFIFOEnabled()

Get gyroscope Y-axis FIFO enabled value. When set to 1, this bit enables GYRO_YOUT_H and GYRO_YOUT_L (Registers 69 and 70) to be written into the FIFO buffer.

See also

MPU6050_RA_FIFO_EN

Returns:

Current gyroscope Y-axis FIFO enabled value

inline void setYGyroFIFOEnabled(bool enabled)

Set gyroscope Y-axis FIFO enabled value.

See also

MPU6050_RA_FIFO_EN

Parameters:

enabled – New gyroscope Y-axis FIFO enabled value

inline bool getZGyroFIFOEnabled()

Get gyroscope Z-axis FIFO enabled value. When set to 1, this bit enables GYRO_ZOUT_H and GYRO_ZOUT_L (Registers 71 and 72) to be written into the FIFO buffer.

See also

MPU6050_RA_FIFO_EN

Returns:

Current gyroscope Z-axis FIFO enabled value

inline void setZGyroFIFOEnabled(bool enabled)

Set gyroscope Z-axis FIFO enabled value.

See also

MPU6050_RA_FIFO_EN

Parameters:

enabled – New gyroscope Z-axis FIFO enabled value

inline bool getAccelFIFOEnabled()

Get accelerometer FIFO enabled value. When set to 1, this bit enables ACCEL_XOUT_H, ACCEL_XOUT_L, ACCEL_YOUT_H, ACCEL_YOUT_L, ACCEL_ZOUT_H, and ACCEL_ZOUT_L (Registers 59 to 64) to be written into the FIFO buffer.

See also

MPU6050_RA_FIFO_EN

Returns:

Current accelerometer FIFO enabled value

inline void setAccelFIFOEnabled(bool enabled)

Set accelerometer FIFO enabled value.

See also

MPU6050_RA_FIFO_EN

Parameters:

enabled – New accelerometer FIFO enabled value

inline bool getSlave2FIFOEnabled()

Get Slave 2 FIFO enabled value. When set to 1, this bit enables EXT_SENS_DATA registers (Registers 73 to 96) associated with Slave 2 to be written into the FIFO buffer.

See also

MPU6050_RA_FIFO_EN

Returns:

Current Slave 2 FIFO enabled value

inline void setSlave2FIFOEnabled(bool enabled)

Set Slave 2 FIFO enabled value.

See also

MPU6050_RA_FIFO_EN

Parameters:

enabled – New Slave 2 FIFO enabled value

inline bool getSlave1FIFOEnabled()

Get Slave 1 FIFO enabled value. When set to 1, this bit enables EXT_SENS_DATA registers (Registers 73 to 96) associated with Slave 1 to be written into the FIFO buffer.

See also

MPU6050_RA_FIFO_EN

Returns:

Current Slave 1 FIFO enabled value

inline void setSlave1FIFOEnabled(bool enabled)

Set Slave 1 FIFO enabled value.

See also

MPU6050_RA_FIFO_EN

Parameters:

enabled – New Slave 1 FIFO enabled value

inline bool getSlave0FIFOEnabled()

Get Slave 0 FIFO enabled value. When set to 1, this bit enables EXT_SENS_DATA registers (Registers 73 to 96) associated with Slave 0 to be written into the FIFO buffer.

See also

MPU6050_RA_FIFO_EN

Returns:

Current Slave 0 FIFO enabled value

inline void setSlave0FIFOEnabled(bool enabled)

Set Slave 0 FIFO enabled value.

See also

MPU6050_RA_FIFO_EN

Parameters:

enabled – New Slave 0 FIFO enabled value

inline bool getMultiMasterEnabled()

Get multi-master enabled value. Multi-master capability allows multiple I2C masters to operate on the same bus. In circuits where multi-master capability is required, set MULT_MST_EN to 1. This will increase current drawn by approximately 30uA.

In circuits where multi-master capability is required, the state of the I2C bus must always be monitored by each separate I2C Master. Before an I2C Master can assume arbitration of the bus, it must first confirm that no other I2C Master has arbitration of the bus. When MULT_MST_EN is set to 1, the MPU-60X0’s bus arbitration detection logic is turned on, enabling it to detect when the bus is available.

See also

MPU6050_RA_I2C_MST_CTRL

Returns:

Current multi-master enabled value

inline void setMultiMasterEnabled(bool enabled)

Set multi-master enabled value.

See also

MPU6050_RA_I2C_MST_CTRL

Parameters:

enabled – New multi-master enabled value

inline bool getWaitForExternalSensorEnabled()

Get wait-for-external-sensor-data enabled value. When the WAIT_FOR_ES bit is set to 1, the Data Ready interrupt will be delayed until External Sensor data from the Slave Devices are loaded into the EXT_SENS_DATA registers. This is used to ensure that both the internal sensor data (i.e. from gyro and accel) and external sensor data have been loaded to their respective data registers (i.e. the data is synced) when the Data Ready interrupt is triggered.

See also

MPU6050_RA_I2C_MST_CTRL

Returns:

Current wait-for-external-sensor-data enabled value

inline void setWaitForExternalSensorEnabled(bool enabled)

Set wait-for-external-sensor-data enabled value.

See also

MPU6050_RA_I2C_MST_CTRL

Parameters:

enabled – New wait-for-external-sensor-data enabled value

inline bool getSlave3FIFOEnabled()

Get Slave 3 FIFO enabled value. When set to 1, this bit enables EXT_SENS_DATA registers (Registers 73 to 96) associated with Slave 3 to be written into the FIFO buffer.

See also

MPU6050_RA_MST_CTRL

Returns:

Current Slave 3 FIFO enabled value

inline void setSlave3FIFOEnabled(bool enabled)

Set Slave 3 FIFO enabled value.

See also

MPU6050_RA_MST_CTRL

Parameters:

enabled – New Slave 3 FIFO enabled value

inline bool getSlaveReadWriteTransitionEnabled()

Get slave read/write transition enabled value. The I2C_MST_P_NSR bit configures the I2C Master’s transition from one slave read to the next slave read. If the bit equals 0, there will be a restart between reads. If the bit equals 1, there will be a stop followed by a start of the following read. When a write transaction follows a read transaction, the stop followed by a start of the successive write will be always used.

See also

MPU6050_RA_I2C_MST_CTRL

Returns:

Current slave read/write transition enabled value

inline void setSlaveReadWriteTransitionEnabled(bool enabled)

Set slave read/write transition enabled value.

See also

MPU6050_RA_I2C_MST_CTRL

Parameters:

enabled – New slave read/write transition enabled value

inline uint8_t getMasterClockSpeed()

Get I2C master clock speed. I2C_MST_CLK is a 4 bit unsigned value which configures a divider on the MPU-60X0 internal 8MHz clock. It sets the I2C master clock speed according to the following table:

See also

MPU6050_RA_I2C_MST_CTRL

Returns:

Current I2C master clock speed

inline void setMasterClockSpeed(uint8_t speed)

Set I2C master clock speed. @reparam speed Current I2C master clock speed

See also

MPU6050_RA_I2C_MST_CTRL

uint8_t getSlaveAddress(SlaveId slaveId)

Get the I2C address of the specified slave (0-3). Note that Bit 7 (MSB) controls read/write mode. If Bit 7 is set, it’s a read operation, and if it is cleared, then it’s a write operation. The remaining bits (6-0) are the 7-bit device address of the slave device.

In read mode, the result of the read is placed in the lowest available EXT_SENS_DATA register. For further information regarding the allocation of read results, please refer to the EXT_SENS_DATA register description (Registers 73 - 96).

The MPU-6050 supports a total of five slaves, but Slave 4 has unique characteristics, and so it has its own functions (getSlave4* and setSlave4*).

I2C data transactions are performed at the Sample Rate, as defined in Register 25. The user is responsible for ensuring that I2C data transactions to and from each enabled Slave can be completed within a single period of the Sample Rate.

The I2C slave access rate can be reduced relative to the Sample Rate. This reduced access rate is determined by I2C_MST_DLY (Register 52). Whether a slave’s access rate is reduced relative to the Sample Rate is determined by I2C_MST_DELAY_CTRL (Register 103).

The processing order for the slaves is fixed. The sequence followed for processing the slaves is Slave 0, Slave 1, Slave 2, Slave 3 and Slave 4. If a particular Slave is disabled it will be skipped.

Each slave can either be accessed at the sample rate or at a reduced sample rate. In a case where some slaves are accessed at the Sample Rate and some slaves are accessed at the reduced rate, the sequence of accessing the slaves (Slave 0 to Slave 4) is still followed. However, the reduced rate slaves will be skipped if their access rate dictates that they should not be accessed during that particular cycle. For further information regarding the reduced access rate, please refer to Register 52. Whether a slave is accessed at the Sample Rate or at the reduced rate is determined by the Delay Enable bits in Register 103.

See also

MPU6050_RA_I2C_SLV0_ADDR

Parameters:

slaveId – Slave ID (0-3)

Returns:

Current address for specified slave

void setSlaveAddress(SlaveId slaveId, uint8_t address)

Set the I2C address of the specified slave (0-3).

See also

MPU6050_RA_I2C_SLV0_ADDR

Parameters:
  • slaveId – Slave ID (0-3)

  • address – New address for specified slave

uint8_t getSlaveRegister(SlaveId slaveId)

Get the active internal register for the specified slave (0-3). Read/write operations for this slave will be done to whatever internal register address is stored in this MPU register.

The MPU-6050 supports a total of five slaves, but Slave 4 has unique characteristics, and so it has its own functions.

See also

MPU6050_RA_I2C_SLV0_REG

Parameters:

slaveId – Slave ID (0-3)

Returns:

Current active register for specified slave

void setSlaveRegister(SlaveId slaveId, uint8_t reg)

Set the active internal register for the specified slave (0-3).

See also

MPU6050_RA_I2C_SLV0_REG

Parameters:
  • slaveId – Slave ID (0-3)

  • reg – New active register for specified slave

bool getSlaveEnabled(SlaveId slaveId)

Get the enabled value for the specified slave (0-3). When set to 1, this bit enables Slave 0 for data transfer operations. When cleared to 0, this bit disables Slave 0 from data transfer operations.

See also

MPU6050_RA_I2C_SLV0_CTRL

Parameters:

slaveId – Slave ID (0-3)

Returns:

Current enabled value for specified slave

void setSlaveEnabled(SlaveId slaveId, bool enabled)

Set the enabled value for the specified slave (0-3).

See also

MPU6050_RA_I2C_SLV0_CTRL

Parameters:
  • slaveId – Slave ID (0-3)

  • enabled – New enabled value for specified slave

bool getSlaveWordByteSwap(SlaveId slaveId)

Get word pair byte-swapping enabled for the specified slave (0-3). When set to 1, this bit enables byte swapping. When byte swapping is enabled, the high and low bytes of a word pair are swapped. Please refer to I2C_SLV0_GRP for the pairing convention of the word pairs. When cleared to 0, bytes transferred to and from Slave 0 will be written to EXT_SENS_DATA registers in the order they were transferred.

See also

MPU6050_RA_I2C_SLV0_CTRL

Parameters:

slaveId – Slave ID (0-3)

Returns:

Current word pair byte-swapping enabled value for specified slave

void setSlaveWordByteSwap(SlaveId slaveId, bool enabled)

Set word pair byte-swapping enabled for the specified slave (0-3).

See also

MPU6050_RA_I2C_SLV0_CTRL

Parameters:
  • slaveId – Slave ID (0-3)

  • enabled – New word pair byte-swapping enabled value for specified slave

bool getSlaveWriteMode(SlaveId slaveId)

Get write mode for the specified slave (0-3). When set to 1, the transaction will read or write data only. When cleared to 0, the transaction will write a register address prior to reading or writing data. This should equal 0 when specifying the register address within the Slave device to/from which the ensuing data transaction will take place.

See also

MPU6050_RA_I2C_SLV0_CTRL

Parameters:

slaveId – Slave ID (0-3)

Returns:

Current write mode for specified slave (0 = register address + data, 1 = data only)

void setSlaveWriteMode(SlaveId slaveId, bool mode)

Set write mode for the specified slave (0-3).

See also

MPU6050_RA_I2C_SLV0_CTRL

Parameters:
  • slaveId – Slave ID (0-3)

  • mode – New write mode for specified slave (0 = register address + data, 1 = data only)

bool getSlaveWordGroupOffset(SlaveId slaveId)

Get word pair grouping order offset for the specified slave (0-3). This sets specifies the grouping order of word pairs received from registers. When cleared to 0, bytes from register addresses 0 and 1, 2 and 3, etc (even, then odd register addresses) are paired to form a word. When set to 1, bytes from register addresses are paired 1 and 2, 3 and 4, etc. (odd, then even register addresses) are paired to form a word.

See also

MPU6050_RA_I2C_SLV0_CTRL

Parameters:

slaveId – Slave ID (0-3)

Returns:

Current word pair grouping order offset for specified slave

void setSlaveWordGroupOffset(SlaveId slaveId, bool enabled)

Set word pair grouping order offset for the specified slave (0-3).

See also

MPU6050_RA_I2C_SLV0_CTRL

Parameters:
  • slaveId – Slave ID (0-3)

  • enabled – New word pair grouping order offset for specified slave

uint8_t getSlaveDataLength(SlaveId slaveId)

Get number of bytes to read for the specified slave (0-3). Specifies the number of bytes transferred to and from Slave 0. Clearing this bit to 0 is equivalent to disabling the register by writing 0 to I2C_SLV0_EN.

See also

MPU6050_RA_I2C_SLV0_CTRL

Parameters:

slaveId – Slave ID (0-3)

Returns:

Number of bytes to read for specified slave

void setSlaveDataLength(SlaveId slaveId, uint8_t length)

Set number of bytes to read for the specified slave (0-3).

See also

MPU6050_RA_I2C_SLV0_CTRL

Parameters:
  • slaveId – Slave ID (0-3)

  • length – Number of bytes to read for specified slave

inline uint8_t getSlave4Address()

Get the I2C address of Slave 4. Note that Bit 7 (MSB) controls read/write mode. If Bit 7 is set, it’s a read operation, and if it is cleared, then it’s a write operation. The remaining bits (6-0) are the 7-bit device address of the slave device.

See also

MPU6050_RA_I2C_SLV4_ADDR

Returns:

Current address for Slave 4

inline void setSlave4Address(uint8_t address)

Set the I2C address of Slave 4.

See also

MPU6050_RA_I2C_SLV4_ADDR

Parameters:

address – New address for Slave 4

inline uint8_t getSlave4Register()

Get the active internal register for the Slave 4. Read/write operations for this slave will be done to whatever internal register address is stored in this MPU register.

See also

MPU6050_RA_I2C_SLV4_REG

Returns:

Current active register for Slave 4

inline void setSlave4Register(uint8_t reg)

Set the active internal register for Slave 4.

See also

MPU6050_RA_I2C_SLV4_REG

Parameters:

reg – New active register for Slave 4

inline void setSlave4OutputByte(uint8_t data)

Set new byte to write to Slave 4. This register stores the data to be written into the Slave 4. If I2C_SLV4_RW is set 1 (set to read), this register has no effect.

See also

MPU6050_RA_I2C_SLV4_DO

Parameters:

data – New byte to write to Slave 4

inline bool getSlave4Enabled()

Get the enabled value for the Slave 4. When set to 1, this bit enables Slave 4 for data transfer operations. When cleared to 0, this bit disables Slave 4 from data transfer operations.

See also

MPU6050_RA_I2C_SLV4_CTRL

Returns:

Current enabled value for Slave 4

inline void setSlave4Enabled(bool enabled)

Set the enabled value for Slave 4.

See also

MPU6050_RA_I2C_SLV4_CTRL

Parameters:

enabled – New enabled value for Slave 4

inline bool getSlave4InterruptEnabled()

Get the enabled value for Slave 4 transaction interrupts. When set to 1, this bit enables the generation of an interrupt signal upon completion of a Slave 4 transaction. When cleared to 0, this bit disables the generation of an interrupt signal upon completion of a Slave 4 transaction. The interrupt status can be observed in Register 54.

See also

MPU6050_RA_I2C_SLV4_CTRL

Returns:

Current enabled value for Slave 4 transaction interrupts.

inline void setSlave4InterruptEnabled(bool enabled)

Set the enabled value for Slave 4 transaction interrupts.

See also

MPU6050_RA_I2C_SLV4_CTRL

Parameters:

enabled – New enabled value for Slave 4 transaction interrupts.

inline bool getSlave4WriteMode()

Get write mode for Slave 4. When set to 1, the transaction will read or write data only. When cleared to 0, the transaction will write a register address prior to reading or writing data. This should equal 0 when specifying the register address within the Slave device to/from which the ensuing data transaction will take place.

See also

MPU6050_RA_I2C_SLV4_CTRL

Returns:

Current write mode for Slave 4 (0 = register address + data, 1 = data only)

inline void setSlave4WriteMode(bool mode)

Set write mode for the Slave 4.

See also

MPU6050_RA_I2C_SLV4_CTRL

Parameters:

mode – New write mode for Slave 4 (0 = register address + data, 1 = data only)

inline uint8_t getSlave4MasterDelay()

Get Slave 4 master delay value. This configures the reduced access rate of I2C slaves relative to the Sample Rate. When a slave’s access rate is decreased relative to the Sample Rate, the slave is accessed every:

1 / (1 + I2C_MST_DLY) samples
This base Sample Rate in turn is determined by SMPLRT_DIV (register 25) and DLPF_CFG (register 26). Whether a slave’s access rate is reduced relative to the Sample Rate is determined by I2C_MST_DELAY_CTRL (register 103). For further information regarding the Sample Rate, please refer to register 25.

See also

MPU6050_RA_I2C_SLV4_CTRL

Returns:

Current Slave 4 master delay value

inline void setSlave4MasterDelay(uint8_t delay)

Set Slave 4 master delay value.

See also

MPU6050_RA_I2C_SLV4_CTRL

Parameters:

delay – New Slave 4 master delay value

inline uint8_t getSlate4InputByte()

Get last available byte read from Slave 4. This register stores the data read from Slave 4. This field is populated after a read transaction.

See also

MPU6050_RA_I2C_SLV4_DI

Returns:

Last available byte read from to Slave 4

inline bool getPassthroughStatus()

Get FSYNC interrupt status. This bit reflects the status of the FSYNC interrupt from an external device into the MPU-60X0. This is used as a way to pass an external interrupt through the MPU-60X0 to the host application processor. When set to 1, this bit will cause an interrupt if FSYNC_INT_EN is asserted in INT_PIN_CFG (Register 55).

See also

MPU6050_RA_I2C_MST_STATUS

Returns:

FSYNC interrupt status

inline bool getSlave4IsDone()

Get Slave 4 transaction done status. Automatically sets to 1 when a Slave 4 transaction has completed. This triggers an interrupt if the I2C_MST_INT_EN bit in the INT_ENABLE register (Register 56) is asserted and if the SLV_4_DONE_INT bit is asserted in the I2C_SLV4_CTRL register (Register 52).

See also

MPU6050_RA_I2C_MST_STATUS

Returns:

Slave 4 transaction done status

inline bool getLostArbitration()

Get master arbitration lost status. This bit automatically sets to 1 when the I2C Master has lost arbitration of the auxiliary I2C bus (an error condition). This triggers an interrupt if the I2C_MST_INT_EN bit in the INT_ENABLE register (Register 56) is asserted.

See also

MPU6050_RA_I2C_MST_STATUS

Returns:

Master arbitration lost status

inline bool getSlave4Nack()

Get Slave 4 NACK status. This bit automatically sets to 1 when the I2C Master receives a NACK in a transaction with Slave 4. This triggers an interrupt if the I2C_MST_INT_EN bit in the INT_ENABLE register (Register 56) is asserted.

See also

MPU6050_RA_I2C_MST_STATUS

Returns:

Slave 4 NACK interrupt status

inline bool getSlave3Nack()

Get Slave 3 NACK status. This bit automatically sets to 1 when the I2C Master receives a NACK in a transaction with Slave 3. This triggers an interrupt if the I2C_MST_INT_EN bit in the INT_ENABLE register (Register 56) is asserted.

See also

MPU6050_RA_I2C_MST_STATUS

Returns:

Slave 3 NACK interrupt status

inline bool getSlave2Nack()

Get Slave 2 NACK status. This bit automatically sets to 1 when the I2C Master receives a NACK in a transaction with Slave 2. This triggers an interrupt if the I2C_MST_INT_EN bit in the INT_ENABLE register (Register 56) is asserted.

See also

MPU6050_RA_I2C_MST_STATUS

Returns:

Slave 2 NACK interrupt status

inline bool getSlave1Nack()

Get Slave 1 NACK status. This bit automatically sets to 1 when the I2C Master receives a NACK in a transaction with Slave 1. This triggers an interrupt if the I2C_MST_INT_EN bit in the INT_ENABLE register (Register 56) is asserted.

See also

MPU6050_RA_I2C_MST_STATUS

Returns:

Slave 1 NACK interrupt status

inline bool getSlave0Nack()

Get Slave 0 NACK status. This bit automatically sets to 1 when the I2C Master receives a NACK in a transaction with Slave 0. This triggers an interrupt if the I2C_MST_INT_EN bit in the INT_ENABLE register (Register 56) is asserted.

See also

MPU6050_RA_I2C_MST_STATUS

Returns:

Slave 0 NACK interrupt status

inline bool getInterruptMode()

Get interrupt logic level mode. Will be set 0 for active-high, 1 for active-low.

See also

MPU6050_RA_INT_PIN_CFG

See also

MPU6050_INTCFG_INT_LEVEL_BIT

Returns:

Current interrupt mode (0=active-high, 1=active-low)

inline void setInterruptMode(bool mode)

Set interrupt logic level mode.

See also

MPU6050_RA_INT_PIN_CFG

See also

MPU6050_INTCFG_INT_LEVEL_BIT

Parameters:

mode – New interrupt mode (0=active-high, 1=active-low)

inline bool getInterruptDrive()

Get interrupt drive mode. Will be set 0 for push-pull, 1 for open-drain.

See also

MPU6050_RA_INT_PIN_CFG

See also

MPU6050_INTCFG_INT_OPEN_BIT

Returns:

Current interrupt drive mode (0=push-pull, 1=open-drain)

inline void setInterruptDrive(bool drive)

Set interrupt drive mode.

See also

MPU6050_RA_INT_PIN_CFG

See also

MPU6050_INTCFG_INT_OPEN_BIT

Parameters:

drive – New interrupt drive mode (0=push-pull, 1=open-drain)

inline bool getInterruptLatch()

Get interrupt latch mode. Will be set 0 for 50us-pulse, 1 for latch-until-int-cleared.

See also

MPU6050_RA_INT_PIN_CFG

See also

MPU6050_INTCFG_LATCH_INT_EN_BIT

Returns:

Current latch mode (0=50us-pulse, 1=latch-until-int-cleared)

inline void setInterruptLatch(bool latch)

Set interrupt latch mode.

See also

MPU6050_RA_INT_PIN_CFG

See also

MPU6050_INTCFG_LATCH_INT_EN_BIT

Parameters:

latch – New latch mode (0=50us-pulse, 1=latch-until-int-cleared)

inline bool getInterruptLatchClear()

Get interrupt latch clear mode. Will be set 0 for status-read-only, 1 for any-register-read.

See also

MPU6050_RA_INT_PIN_CFG

See also

MPU6050_INTCFG_INT_RD_CLEAR_BIT

Returns:

Current latch clear mode (0=status-read-only, 1=any-register-read)

inline void setInterruptLatchClear(bool clear)

Set interrupt latch clear mode.

See also

MPU6050_RA_INT_PIN_CFG

See also

MPU6050_INTCFG_INT_RD_CLEAR_BIT

Parameters:

clear – New latch clear mode (0=status-read-only, 1=any-register-read)

inline bool getFSyncInterruptLevel()

Get FSYNC interrupt logic level mode.

See also

getFSyncInterruptMode()

See also

MPU6050_RA_INT_PIN_CFG

See also

MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT

Returns:

Current FSYNC interrupt mode (0=active-high, 1=active-low)

inline void setFSyncInterruptLevel(bool level)

Set FSYNC interrupt logic level mode.

See also

getFSyncInterruptMode()

See also

MPU6050_RA_INT_PIN_CFG

See also

MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT

Parameters:

mode – New FSYNC interrupt mode (0=active-high, 1=active-low)

inline bool getFSyncInterruptEnabled()

Get FSYNC pin interrupt enabled setting. Will be set 0 for disabled, 1 for enabled.

See also

MPU6050_RA_INT_PIN_CFG

See also

MPU6050_INTCFG_FSYNC_INT_EN_BIT

Returns:

Current interrupt enabled setting

inline void setFSyncInterruptEnabled(bool enabled)

Set FSYNC pin interrupt enabled setting.

See also

MPU6050_RA_INT_PIN_CFG

See also

MPU6050_INTCFG_FSYNC_INT_EN_BIT

Parameters:

enabled – New FSYNC pin interrupt enabled setting

inline bool getI2CBypassEnabled()

Get I2C bypass enabled status. When this bit is equal to 1 and I2C_MST_EN (Register 106 bit[5]) is equal to 0, the host application processor will be able to directly access the auxiliary I2C bus of the MPU-60X0. When this bit is equal to 0, the host application processor will not be able to directly access the auxiliary I2C bus of the MPU-60X0 regardless of the state of I2C_MST_EN (Register 106 bit[5]).

See also

MPU6050_RA_INT_PIN_CFG

See also

MPU6050_INTCFG_I2C_BYPASS_EN_BIT

Returns:

Current I2C bypass enabled status

inline void setI2CBypassEnabled(bool enabled)

Set I2C bypass enabled status. When this bit is equal to 1 and I2C_MST_EN (Register 106 bit[5]) is equal to 0, the host application processor will be able to directly access the auxiliary I2C bus of the MPU-60X0. When this bit is equal to 0, the host application processor will not be able to directly access the auxiliary I2C bus of the MPU-60X0 regardless of the state of I2C_MST_EN (Register 106 bit[5]).

See also

MPU6050_RA_INT_PIN_CFG

See also

MPU6050_INTCFG_I2C_BYPASS_EN_BIT

Parameters:

enabled – New I2C bypass enabled status

inline bool getClockOutputEnabled()

Get reference clock output enabled status. When this bit is equal to 1, a reference clock output is provided at the CLKOUT pin. When this bit is equal to 0, the clock output is disabled. For further information regarding CLKOUT, please refer to the MPU-60X0 Product Specification document.

See also

MPU6050_RA_INT_PIN_CFG

See also

MPU6050_INTCFG_CLKOUT_EN_BIT

Returns:

Current reference clock output enabled status

inline void setClockOutputEnabled(bool enabled)

Set reference clock output enabled status. When this bit is equal to 1, a reference clock output is provided at the CLKOUT pin. When this bit is equal to 0, the clock output is disabled. For further information regarding CLKOUT, please refer to the MPU-60X0 Product Specification document.

See also

MPU6050_RA_INT_PIN_CFG

See also

MPU6050_INTCFG_CLKOUT_EN_BIT

Parameters:

enabled – New reference clock output enabled status

inline uint8_t getIntEnabled()

Get full interrupt enabled status. Full register byte for all interrupts, for quick reading. Each bit will be set 0 for disabled, 1 for enabled.

See also

MPU6050_RA_INT_ENABLE

See also

MPU6050_INTERRUPT_FF_BIT

Returns:

Current interrupt enabled status

inline void setIntEnabled(uint8_t enabled)

Set full interrupt enabled status. Full register byte for all interrupts, for quick reading. Each bit should be set 0 for disabled, 1 for enabled.

See also

MPU6050_RA_INT_ENABLE

See also

MPU6050_INTERRUPT_FF_BIT

Parameters:

enabled – New interrupt enabled status

inline bool getIntFreefallEnabled()

Get Free Fall interrupt enabled status. Will be set 0 for disabled, 1 for enabled.

See also

MPU6050_RA_INT_ENABLE

See also

MPU6050_INTERRUPT_FF_BIT

Returns:

Current interrupt enabled status

inline void setIntFreefallEnabled(bool enabled)

Set Free Fall interrupt enabled status.

See also

MPU6050_RA_INT_ENABLE

See also

MPU6050_INTERRUPT_FF_BIT

Parameters:

enabled – New interrupt enabled status

inline bool getIntMotionEnabled()

Get Motion Detection interrupt enabled status. Will be set 0 for disabled, 1 for enabled.

See also

MPU6050_RA_INT_ENABLE

See also

MPU6050_INTERRUPT_MOT_BIT

Returns:

Current interrupt enabled status

inline void setIntMotionEnabled(bool enabled)

Set Motion Detection interrupt enabled status.

See also

MPU6050_RA_INT_ENABLE

See also

MPU6050_INTERRUPT_MOT_BIT

Parameters:

enabled – New interrupt enabled status

inline bool getIntZeroMotionEnabled()

Get Zero Motion Detection interrupt enabled status. Will be set 0 for disabled, 1 for enabled.

See also

MPU6050_RA_INT_ENABLE

See also

MPU6050_INTERRUPT_ZMOT_BIT

Returns:

Current interrupt enabled status

inline void setIntZeroMotionEnabled(bool enabled)

Set Zero Motion Detection interrupt enabled status.

See also

MPU6050_RA_INT_ENABLE

See also

MPU6050_INTERRUPT_ZMOT_BIT

Parameters:

enabled – New interrupt enabled status

inline bool getIntFIFOBufferOverflowEnabled()

Get FIFO Buffer Overflow interrupt enabled status. Will be set 0 for disabled, 1 for enabled.

See also

MPU6050_RA_INT_ENABLE

See also

MPU6050_INTERRUPT_FIFO_OFLOW_BIT

Returns:

Current interrupt enabled status

inline void setIntFIFOBufferOverflowEnabled(bool enabled)

Set FIFO Buffer Overflow interrupt enabled status.

See also

MPU6050_RA_INT_ENABLE

See also

MPU6050_INTERRUPT_FIFO_OFLOW_BIT

Parameters:

enabled – New interrupt enabled status

inline bool getIntI2CMasterEnabled()

Get I2C Master interrupt enabled status. This enables any of the I2C Master interrupt sources to generate an interrupt. Will be set 0 for disabled, 1 for enabled.

See also

MPU6050_RA_INT_ENABLE

See also

MPU6050_INTERRUPT_I2C_MST_INT_BIT

Returns:

Current interrupt enabled status

inline void setIntI2CMasterEnabled(bool enabled)

Set I2C Master interrupt enabled status.

See also

MPU6050_RA_INT_ENABLE

See also

MPU6050_INTERRUPT_I2C_MST_INT_BIT

Parameters:

enabled – New interrupt enabled status

inline bool getIntDataReadyEnabled()

Get Data Ready interrupt enabled setting. This event occurs each time a write operation to all of the sensor registers has been completed. Will be set 0 for disabled, 1 for enabled.

See also

MPU6050_RA_INT_ENABLE

See also

MPU6050_INTERRUPT_DATA_RDY_BIT

Returns:

Current interrupt enabled status

inline void setIntDataReadyEnabled(bool enabled)

Set Data Ready interrupt enabled status.

See also

MPU6050_RA_INT_CFG

See also

MPU6050_INTERRUPT_DATA_RDY_BIT

Parameters:

enabled – New interrupt enabled status

inline uint8_t getIntStatus()

Get full set of interrupt status bits. These bits clear to 0 after the register has been read. Very useful for getting multiple INT statuses, since each single bit read clears all of them because it has to read the whole byte.

See also

MPU6050_RA_INT_STATUS

Returns:

Current interrupt status

inline bool getIntFreefallStatus()

Get Free Fall interrupt status. This bit automatically sets to 1 when a Free Fall interrupt has been generated. The bit clears to 0 after the register has been read.

See also

MPU6050_RA_INT_STATUS

See also

MPU6050_INTERRUPT_FF_BIT

Returns:

Current interrupt status

inline bool getIntMotionStatus()

Get Motion Detection interrupt status. This bit automatically sets to 1 when a Motion Detection interrupt has been generated. The bit clears to 0 after the register has been read.

See also

MPU6050_RA_INT_STATUS

See also

MPU6050_INTERRUPT_MOT_BIT

Returns:

Current interrupt status

inline bool getIntZeroMotionStatus()

Get Zero Motion Detection interrupt status. This bit automatically sets to 1 when a Zero Motion Detection interrupt has been generated. The bit clears to 0 after the register has been read.

See also

MPU6050_RA_INT_STATUS

See also

MPU6050_INTERRUPT_ZMOT_BIT

Returns:

Current interrupt status

inline bool getIntFIFOBufferOverflowStatus()

Get FIFO Buffer Overflow interrupt status. This bit automatically sets to 1 when a Free Fall interrupt has been generated. The bit clears to 0 after the register has been read.

See also

MPU6050_RA_INT_STATUS

See also

MPU6050_INTERRUPT_FIFO_OFLOW_BIT

Returns:

Current interrupt status

inline bool getIntI2CMasterStatus()

Get I2C Master interrupt status. This bit automatically sets to 1 when an I2C Master interrupt has been generated. For a list of I2C Master interrupts, please refer to Register 54. The bit clears to 0 after the register has been read.

See also

MPU6050_RA_INT_STATUS

See also

MPU6050_INTERRUPT_I2C_MST_INT_BIT

Returns:

Current interrupt status

inline bool getIntDataReadyStatus()

Get Data Ready interrupt status. This bit automatically sets to 1 when a Data Ready interrupt has been generated. The bit clears to 0 after the register has been read.

See also

MPU6050_RA_INT_STATUS

See also

MPU6050_INTERRUPT_DATA_RDY_BIT

Returns:

Current interrupt status

Motion6 getMotion6()

Get raw 6-axis motion sensor readings (accel/gyro). Retrieves all currently available motion sensor values.

See also

getAngularRate()

See also

MPU6050_RA_ACCEL_XOUT_H

Returns:

container for 3-axis accelerometer and 3-axis gyroscope values

Motion3 getAcceleration()

Get 3-axis accelerometer readings. These registers store the most recent accelerometer measurements. Accelerometer measurements are written to these registers at the Sample Rate as defined in Register 25.

The accelerometer measurement registers, along with the temperature measurement registers, gyroscope measurement registers, and external sensor data registers, are composed of two sets of registers: an internal register set and a user-facing read register set.

The data within the accelerometer sensors’ internal register set is always updated at the Sample Rate. Meanwhile, the user-facing read register set duplicates the internal register set’s data values whenever the serial interface is idle. This guarantees that a burst read of sensor registers will read measurements from the same sampling instant. Note that if burst reads are not used, the user is responsible for ensuring a set of single byte reads correspond to a single sampling instant by checking the Data Ready interrupt.

Each 16-bit accelerometer measurement has a full scale defined in ACCEL_FS (Register 28). For each full scale setting, the accelerometers’ sensitivity per LSB in ACCEL_xOUT is shown in the table below:

See also

MPU6050_RA_GYRO_XOUT_H

Parameters:
  • x – 16-bit signed integer container for X-axis acceleration

  • y – 16-bit signed integer container for Y-axis acceleration

  • z – 16-bit signed integer container for Z-axis acceleration

inline int16_t getAccelerationX()

Get X-axis accelerometer reading.

See also

getMotion6()

See also

MPU6050_RA_ACCEL_XOUT_H

Returns:

X-axis acceleration measurement in 16-bit 2’s complement format

inline int16_t getAccelerationY()

Get Y-axis accelerometer reading.

See also

getMotion6()

See also

MPU6050_RA_ACCEL_YOUT_H

Returns:

Y-axis acceleration measurement in 16-bit 2’s complement format

inline int16_t getAccelerationZ()

Get Z-axis accelerometer reading.

See also

getMotion6()

See also

MPU6050_RA_ACCEL_ZOUT_H

Returns:

Z-axis acceleration measurement in 16-bit 2’s complement format

inline int16_t getTemperature()

Get current internal temperature.

See also

MPU6050_RA_TEMP_OUT_H

Returns:

Temperature reading in 16-bit 2’s complement format

Motion3 getAngularRate()

Get 3-axis gyroscope readings. These gyroscope measurement registers, along with the accelerometer measurement registers, temperature measurement registers, and external sensor data registers, are composed of two sets of registers: an internal register set and a user-facing read register set. The data within the gyroscope sensors’ internal register set is always updated at the Sample Rate. Meanwhile, the user-facing read register set duplicates the internal register set’s data values whenever the serial interface is idle. This guarantees that a burst read of sensor registers will read measurements from the same sampling instant. Note that if burst reads are not used, the user is responsible for ensuring a set of single byte reads correspond to a single sampling instant by checking the Data Ready interrupt.

Each 16-bit gyroscope measurement has a full scale defined in FS_SEL (Register 27). For each full scale setting, the gyroscopes’ sensitivity per LSB in GYRO_xOUT is shown in the table below:

See also

getMotion6()

See also

MPU6050_RA_GYRO_XOUT_H

Returns:

container for 3-axis gyro values

inline int16_t getAngularRateX()

Get X-axis gyroscope reading.

See also

getMotion6()

See also

MPU6050_RA_GYRO_XOUT_H

Returns:

X-axis rotation measurement in 16-bit 2’s complement format

inline int16_t getAngularRateY()

Get Y-axis gyroscope reading.

See also

getMotion6()

See also

MPU6050_RA_GYRO_YOUT_H

Returns:

Y-axis rotation measurement in 16-bit 2’s complement format

inline int16_t getAngularRateZ()

Get Z-axis gyroscope reading.

See also

getMotion6()

See also

MPU6050_RA_GYRO_ZOUT_H

Returns:

Z-axis rotation measurement in 16-bit 2’s complement format

inline uint8_t getExternalSensorByte(int position)

Read single byte from external sensor data register. These registers store data read from external sensors by the Slave 0, 1, 2, and 3 on the auxiliary I2C interface. Data read by Slave 4 is stored in I2C_SLV4_DI (Register 53).

External sensor data is written to these registers at the Sample Rate as defined in Register 25. This access rate can be reduced by using the Slave Delay Enable registers (Register 103).

External sensor data registers, along with the gyroscope measurement registers, accelerometer measurement registers, and temperature measurement registers, are composed of two sets of registers: an internal register set and a user-facing read register set.

The data within the external sensors’ internal register set is always updated at the Sample Rate (or the reduced access rate) whenever the serial interface is idle. This guarantees that a burst read of sensor registers will read measurements from the same sampling instant. Note that if burst reads are not used, the user is responsible for ensuring a set of single byte reads correspond to a single sampling instant by checking the Data Ready interrupt.

Data is placed in these external sensor data registers according to I2C_SLV0_CTRL, I2C_SLV1_CTRL, I2C_SLV2_CTRL, and I2C_SLV3_CTRL (Registers 39, 42, 45, and 48). When more than zero bytes are read (I2C_SLVx_LEN > 0) from an enabled slave (I2C_SLVx_EN = 1), the slave is read at the Sample Rate (as defined in Register 25) or delayed rate (if specified in Register 52 and 103). During each Sample cycle, slave reads are performed in order of Slave number. If all slaves are enabled with more than zero bytes to be read, the order will be Slave 0, followed by Slave 1, Slave 2, and Slave 3.

Each enabled slave will have EXT_SENS_DATA registers associated with it by number of bytes read (I2C_SLVx_LEN) in order of slave number, starting from EXT_SENS_DATA_00. Note that this means enabling or disabling a slave may change the higher numbered slaves’ associated registers. Furthermore, if fewer total bytes are being read from the external sensors as a result of such a change, then the data remaining in the registers which no longer have an associated slave device (i.e. high numbered registers) will remain in these previously allocated registers unless reset.

If the sum of the read lengths of all SLVx transactions exceed the number of available EXT_SENS_DATA registers, the excess bytes will be dropped. There are 24 EXT_SENS_DATA registers and hence the total read lengths between all the slaves cannot be greater than 24 or some bytes will be lost.

Note: Slave 4’s behavior is distinct from that of Slaves 0-3. For further information regarding the characteristics of Slave 4, please refer to Registers 49 to 53.

EXAMPLE: Suppose that Slave 0 is enabled with 4 bytes to be read (I2C_SLV0_EN = 1 and I2C_SLV0_LEN = 4) while Slave 1 is enabled with 2 bytes to be read so that I2C_SLV1_EN = 1 and I2C_SLV1_LEN = 2. In such a situation, EXT_SENS_DATA _00 through _03 will be associated with Slave 0, while EXT_SENS_DATA _04 and 05 will be associated with Slave 1. If Slave 2 is enabled as well, registers starting from EXT_SENS_DATA_06 will be allocated to Slave 2.

If Slave 2 is disabled while Slave 3 is enabled in this same situation, then registers starting from EXT_SENS_DATA_06 will be allocated to Slave 3 instead.

REGISTER ALLOCATION FOR DYNAMIC DISABLE VS. NORMAL DISABLE: If a slave is disabled at any time, the space initially allocated to the slave in the EXT_SENS_DATA register, will remain associated with that slave. This is to avoid dynamic adjustment of the register allocation.

The allocation of the EXT_SENS_DATA registers is recomputed only when (1) all slaves are disabled, or (2) the I2C_MST_RST bit is set (Register 106).

This above is also true if one of the slaves gets NACKed and stops functioning.

Parameters:

position – Starting position (0-23)

Returns:

Byte read from register

inline uint16_t getExternalSensorWord(int position)

Read word (2 bytes) from external sensor data registers.

Parameters:

position – Starting position (0-21)

Returns:

Word read from register

inline uint32_t getExternalSensorDWord(int position)

Read double word (4 bytes) from external sensor data registers.

Parameters:

position – Starting position (0-20)

Returns:

Double word read from registers

inline uint8_t getMotionStatus()

Get full motion detection status register content (all bits).

See also

MPU6050_RA_MOT_DETECT_STATUS

Returns:

Motion detection status byte

inline bool getXNegMotionDetected()

Get X-axis negative motion detection interrupt status.

See also

MPU6050_RA_MOT_DETECT_STATUS

See also

MPU6050_MOTION_MOT_XNEG_BIT

Returns:

Motion detection status

inline bool getXPosMotionDetected()

Get X-axis positive motion detection interrupt status.

See also

MPU6050_RA_MOT_DETECT_STATUS

See also

MPU6050_MOTION_MOT_XPOS_BIT

Returns:

Motion detection status

inline bool getYNegMotionDetected()

Get Y-axis negative motion detection interrupt status.

See also

MPU6050_RA_MOT_DETECT_STATUS

See also

MPU6050_MOTION_MOT_YNEG_BIT

Returns:

Motion detection status

inline bool getYPosMotionDetected()

Get Y-axis positive motion detection interrupt status.

See also

MPU6050_RA_MOT_DETECT_STATUS

See also

MPU6050_MOTION_MOT_YPOS_BIT

Returns:

Motion detection status

inline bool getZNegMotionDetected()

Get Z-axis negative motion detection interrupt status.

See also

MPU6050_RA_MOT_DETECT_STATUS

See also

MPU6050_MOTION_MOT_ZNEG_BIT

Returns:

Motion detection status

inline bool getZPosMotionDetected()

Get Z-axis positive motion detection interrupt status.

See also

MPU6050_RA_MOT_DETECT_STATUS

See also

MPU6050_MOTION_MOT_ZPOS_BIT

Returns:

Motion detection status

inline bool getZeroMotionDetected()

Get zero motion detection interrupt status.

See also

MPU6050_RA_MOT_DETECT_STATUS

See also

MPU6050_MOTION_MOT_ZRMOT_BIT

Returns:

Motion detection status

void setSlaveOutputByte(SlaveId slaveId, uint8_t data)

Write byte to Data Output container for specified slave. This register holds the output data written into Slave when Slave is set to write mode. For further information regarding Slave control, please refer to Registers 37 to 39 and immediately following.

See also

MPU6050_RA_I2C_SLV0_DO

Parameters:
  • slaveId – Slave ID (0-3)

  • data – Byte to write

inline bool getExternalShadowDelayEnabled()

Get external data shadow delay enabled status. This register is used to specify the timing of external sensor data shadowing. When DELAY_ES_SHADOW is set to 1, shadowing of external sensor data is delayed until all data has been received.

See also

MPU6050_RA_I2C_MST_DELAY_CTRL

See also

MPU6050_DELAYCTRL_DELAY_ES_SHADOW_BIT

Returns:

Current external data shadow delay enabled status.

inline void setExternalShadowDelayEnabled(bool enabled)

Set external data shadow delay enabled status.

See also

MPU6050_RA_I2C_MST_DELAY_CTRL

See also

MPU6050_DELAYCTRL_DELAY_ES_SHADOW_BIT

Parameters:

enabled – New external data shadow delay enabled status.

bool getSlaveDelayEnabled(SlaveId slaveId)

Get slave delay enabled status. When a particular slave delay is enabled, the rate of access for the that slave device is reduced. When a slave’s access rate is decreased relative to the Sample Rate, the slave is accessed every:

1 / (1 + I2C_MST_DLY) Samples
This base Sample Rate in turn is determined by SMPLRT_DIV (register * 25) and DLPF_CFG (register 26).

For further information regarding I2C_MST_DLY, please refer to register 52. For further information regarding the Sample Rate, please refer to register 25.

See also

MPU6050_RA_I2C_MST_DELAY_CTRL

See also

MPU6050_DELAYCTRL_I2C_SLV0_DLY_EN_BIT

Parameters:

slaveId – Slave ID (0-4)

Returns:

Current slave delay enabled status.

inline void setSlaveDelayEnabled(SlaveId slaveId, bool enabled)

Set slave delay enabled status.

See also

MPU6050_RA_I2C_MST_DELAY_CTRL

See also

MPU6050_DELAYCTRL_I2C_SLV0_DLY_EN_BIT

Parameters:
  • slaveId – Slave ID (0-4)

  • enabled – New slave delay enabled status.

inline void resetGyroscopePath()

Reset gyroscope signal path. The reset will revert the signal path analog to digital converters and filters to their power up configurations.

See also

MPU6050_RA_SIGNAL_PATH_RESET

See also

MPU6050_PATHRESET_GYRO_RESET_BIT

inline void resetAccelerometerPath()

Reset accelerometer signal path. The reset will revert the signal path analog to digital converters and filters to their power up configurations.

See also

MPU6050_RA_SIGNAL_PATH_RESET

See also

MPU6050_PATHRESET_ACCEL_RESET_BIT

inline void resetTemperaturePath()

Reset temperature sensor signal path. The reset will revert the signal path analog to digital converters and filters to their power up configurations.

See also

MPU6050_RA_SIGNAL_PATH_RESET

See also

MPU6050_PATHRESET_TEMP_RESET_BIT

inline uint8_t getAccelerometerPowerOnDelay()

Get accelerometer power-on delay. The accelerometer data path provides samples to the sensor registers, Motion detection, Zero Motion detection, and Free Fall detection modules. The signal path contains filters which must be flushed on wake-up with new samples before the detection modules begin operations. The default wake-up delay, of 4ms can be lengthened by up to 3ms. This additional delay is specified in ACCEL_ON_DELAY in units of 1 LSB = 1 ms. The user may select any value above zero unless instructed otherwise by InvenSense. Please refer to Section 8 of the MPU-6000/MPU-6050 Product Specification document for further information regarding the detection modules.

See also

MPU6050_RA_MOT_DETECT_CTRL

See also

MPU6050_DETECT_ACCEL_ON_DELAY_BIT

Returns:

Current accelerometer power-on delay

inline void setAccelerometerPowerOnDelay(uint8_t delay)

Set accelerometer power-on delay.

See also

MPU6050_RA_MOT_DETECT_CTRL

See also

MPU6050_DETECT_ACCEL_ON_DELAY_BIT

Parameters:

delay – New accelerometer power-on delay (0-3)

inline uint8_t getFreefallDetectionCounterDecrement()

Get Free Fall detection counter decrement configuration. Detection is registered by the Free Fall detection module after accelerometer measurements meet their respective threshold conditions over a specified number of samples. When the threshold conditions are met, the corresponding detection counter increments by 1. The user may control the rate at which the detection counter decrements when the threshold condition is not met by configuring FF_COUNT. The decrement rate can be set according to the following table:

When FF_COUNT is configured to 0 (reset), any non-qualifying sample will reset the counter to 0. For further information on Free Fall detection, please refer to Registers 29 to 32.

See also

MPU6050_RA_MOT_DETECT_CTRL

See also

MPU6050_DETECT_FF_COUNT_BIT

Returns:

Current decrement configuration

inline void setFreefallDetectionCounterDecrement(uint8_t decrement)

Set Free Fall detection counter decrement configuration.

See also

MPU6050_RA_MOT_DETECT_CTRL

See also

MPU6050_DETECT_FF_COUNT_BIT

Parameters:

decrement – New decrement configuration value

inline uint8_t getMotionDetectionCounterDecrement()

Get Motion detection counter decrement configuration. Detection is registered by the Motion detection module after accelerometer measurements meet their respective threshold conditions over a specified number of samples. When the threshold conditions are met, the corresponding detection counter increments by 1. The user may control the rate at which the detection counter decrements when the threshold condition is not met by configuring MOT_COUNT. The decrement rate can be set according to the following table:

When MOT_COUNT is configured to 0 (reset), any non-qualifying sample will reset the counter to 0. For further information on Motion detection, please refer to Registers 29 to 32.

inline void setMotionDetectionCounterDecrement(uint8_t decrement)

Set Motion detection counter decrement configuration.

See also

MPU6050_RA_MOT_DETECT_CTRL

See also

MPU6050_DETECT_MOT_COUNT_BIT

Parameters:

decrement – New decrement configuration value

inline bool getFIFOEnabled()

Get FIFO enabled status. When this bit is set to 0, the FIFO buffer is disabled. The FIFO buffer cannot be written to or read from while disabled. The FIFO buffer’s state does not change unless the MPU-60X0 is power cycled.

See also

MPU6050_RA_USER_CTRL

See also

MPU6050_USERCTRL_FIFO_EN_BIT

Returns:

Current FIFO enabled status

inline void setFIFOEnabled(bool enabled)

Set FIFO enabled status.

See also

getFIFOEnabled()

See also

MPU6050_RA_USER_CTRL

See also

MPU6050_USERCTRL_FIFO_EN_BIT

Parameters:

enabled – New FIFO enabled status

inline bool getI2CMasterModeEnabled()

Get I2C Master Mode enabled status. When this mode is enabled, the MPU-60X0 acts as the I2C Master to the external sensor slave devices on the auxiliary I2C bus. When this bit is cleared to 0, the auxiliary I2C bus lines (AUX_DA and AUX_CL) are logically driven by the primary I2C bus (SDA and SCL). This is a precondition to enabling Bypass Mode. For further information regarding Bypass Mode, please refer to Register 55.

See also

MPU6050_RA_USER_CTRL

See also

MPU6050_USERCTRL_I2C_MST_EN_BIT

Returns:

Current I2C Master Mode enabled status

inline void setI2CMasterModeEnabled(bool enabled)

Set I2C Master Mode enabled status.

See also

MPU6050_RA_USER_CTRL

See also

MPU6050_USERCTRL_I2C_MST_EN_BIT

Parameters:

enabled – New I2C Master Mode enabled status

inline void switchSPIEnabled(bool enabled)

Switch from I2C to SPI mode (MPU-6000 only) If this is set, the primary SPI interface will be enabled in place of the disabled primary I2C interface.

inline void resetFIFO()

Reset the FIFO. This bit resets the FIFO buffer when set to 1 while FIFO_EN equals 0. This bit automatically clears to 0 after the reset has been triggered.

See also

MPU6050_RA_USER_CTRL

See also

MPU6050_USERCTRL_FIFO_RESET_BIT

inline void resetI2CMaster()

Reset the I2C Master. This bit resets the I2C Master when set to 1 while I2C_MST_EN equals 0. This bit automatically clears to 0 after the reset has been triggered.

See also

MPU6050_RA_USER_CTRL

See also

MPU6050_USERCTRL_I2C_MST_RESET_BIT

inline void resetSensors()

Reset all sensor registers and signal paths. When set to 1, this bit resets the signal paths for all sensors (gyroscopes, accelerometers, and temperature sensor). This operation will also clear the sensor registers. This bit automatically clears to 0 after the reset has been triggered.

When resetting only the signal path (and not the sensor registers), please use Register 104, SIGNAL_PATH_RESET.

See also

MPU6050_RA_USER_CTRL

See also

MPU6050_USERCTRL_SIG_COND_RESET_BIT

inline void reset()

Trigger a full device reset. A small delay of ~50ms may be desirable after triggering a reset.

See also

MPU6050_RA_PWR_MGMT_1

See also

MPU6050_PWR1_DEVICE_RESET_BIT

inline bool getSleepEnabled()

Get sleep mode status. Setting the SLEEP bit in the register puts the device into very low power sleep mode. In this mode, only the serial interface and internal registers remain active, allowing for a very low standby current. Clearing this bit puts the device back into normal mode. To save power, the individual standby selections for each of the gyros should be used if any gyro axis is not used by the application.

See also

MPU6050_RA_PWR_MGMT_1

See also

MPU6050_PWR1_SLEEP_BIT

Returns:

Current sleep mode enabled status

inline void setSleepEnabled(bool enabled)

Set sleep mode status.

See also

MPU6050_RA_PWR_MGMT_1

See also

MPU6050_PWR1_SLEEP_BIT

Parameters:

enabled – New sleep mode enabled status

inline bool getWakeCycleEnabled()

Get wake cycle enabled status. When this bit is set to 1 and SLEEP is disabled, the MPU-60X0 will cycle between sleep mode and waking up to take a single sample of data from active sensors at a rate determined by LP_WAKE_CTRL (register 108).

See also

MPU6050_RA_PWR_MGMT_1

See also

MPU6050_PWR1_CYCLE_BIT

Returns:

Current sleep mode enabled status

inline void setWakeCycleEnabled(bool enabled)

Set wake cycle enabled status.

See also

MPU6050_RA_PWR_MGMT_1

See also

MPU6050_PWR1_CYCLE_BIT

Parameters:

enabled – New sleep mode enabled status

inline bool getTempSensorEnabled()

Get temperature sensor enabled status. Control the usage of the internal temperature sensor.

Note: this register stores the disabled value, but for consistency with the rest of the code, the function is named and used with standard true/false values to indicate whether the sensor is enabled or disabled, respectively.

See also

MPU6050_RA_PWR_MGMT_1

See also

MPU6050_PWR1_TEMP_DIS_BIT

Returns:

Current temperature sensor enabled status

inline void setTempSensorEnabled(bool enabled)

Set temperature sensor enabled status. Note: this register stores the disabled value, but for consistency with the rest of the code, the function is named and used with standard true/false values to indicate whether the sensor is enabled or disabled, respectively.

See also

MPU6050_RA_PWR_MGMT_1

See also

MPU6050_PWR1_TEMP_DIS_BIT

Parameters:

enabled – New temperature sensor enabled status

inline uint8_t getClockSource()

Get clock source setting.

See also

MPU6050_RA_PWR_MGMT_1

See also

MPU6050_PWR1_CLKSEL_BIT

See also

MPU6050_PWR1_CLKSEL_LENGTH

Returns:

Current clock source setting

inline void setClockSource(uint8_t source)

Set clock source setting. An internal 8MHz oscillator, gyroscope based clock, or external sources can be selected as the MPU-60X0 clock source. When the internal 8 MHz oscillator or an external source is chosen as the clock source, the MPU-60X0 can operate in low power modes with the gyroscopes disabled.

Upon power up, the MPU-60X0 clock source defaults to the internal oscillator. However, it is highly recommended that the device be configured to use one of the gyroscopes (or an external clock source) as the clock reference for improved stability. The clock source can be selected according to the following table:

See also

getClockSource()

See also

MPU6050_RA_PWR_MGMT_1

See also

MPU6050_PWR1_CLKSEL_BIT

See also

MPU6050_PWR1_CLKSEL_LENGTH

Parameters:

source – New clock source setting

inline uint8_t getWakeFrequency()

Get wake frequency in Accel-Only Low Power Mode. The MPU-60X0 can be put into Accerlerometer Only Low Power Mode by setting PWRSEL to 1 in the Power Management 1 register (Register 107). In this mode, the device will power off all devices except for the primary I2C interface, waking only the accelerometer at fixed intervals to take a single measurement. The frequency of wake-ups can be configured with LP_WAKE_CTRL as shown below:

For further information regarding the MPU-60X0’s power modes, please refer to Register 107.

See also

MPU6050_RA_PWR_MGMT_2

Returns:

Current wake frequency

inline void setWakeFrequency(uint8_t frequency)

Set wake frequency in Accel-Only Low Power Mode.

See also

MPU6050_RA_PWR_MGMT_2

Parameters:

frequency – New wake frequency

inline bool getStandbyXAccelEnabled()

Get X-axis accelerometer standby enabled status. If enabled, the X-axis will not gather or report data (or use power).

See also

MPU6050_RA_PWR_MGMT_2

See also

MPU6050_PWR2_STBY_XA_BIT

Returns:

Current X-axis standby enabled status

inline void setStandbyXAccelEnabled(bool enabled)

Set X-axis accelerometer standby enabled status.

See also

MPU6050_RA_PWR_MGMT_2

See also

MPU6050_PWR2_STBY_XA_BIT

Parameters:

New – X-axis standby enabled status

inline bool getStandbyYAccelEnabled()

Get Y-axis accelerometer standby enabled status. If enabled, the Y-axis will not gather or report data (or use power).

See also

MPU6050_RA_PWR_MGMT_2

See also

MPU6050_PWR2_STBY_YA_BIT

Returns:

Current Y-axis standby enabled status

inline void setStandbyYAccelEnabled(bool enabled)

Set Y-axis accelerometer standby enabled status.

See also

MPU6050_RA_PWR_MGMT_2

See also

MPU6050_PWR2_STBY_YA_BIT

Parameters:

New – Y-axis standby enabled status

inline bool getStandbyZAccelEnabled()

Get Z-axis accelerometer standby enabled status. If enabled, the Z-axis will not gather or report data (or use power).

See also

MPU6050_RA_PWR_MGMT_2

See also

MPU6050_PWR2_STBY_ZA_BIT

Returns:

Current Z-axis standby enabled status

inline void setStandbyZAccelEnabled(bool enabled)

Set Z-axis accelerometer standby enabled status.

See also

MPU6050_RA_PWR_MGMT_2

See also

MPU6050_PWR2_STBY_ZA_BIT

Parameters:

New – Z-axis standby enabled status

inline bool getStandbyXGyroEnabled()

Get X-axis gyroscope standby enabled status. If enabled, the X-axis will not gather or report data (or use power).

See also

MPU6050_RA_PWR_MGMT_2

See also

MPU6050_PWR2_STBY_XG_BIT

Returns:

Current X-axis standby enabled status

inline void setStandbyXGyroEnabled(bool enabled)

Set X-axis gyroscope standby enabled status.

See also

MPU6050_RA_PWR_MGMT_2

See also

MPU6050_PWR2_STBY_XG_BIT

Parameters:

New – X-axis standby enabled status

inline bool getStandbyYGyroEnabled()

Get Y-axis gyroscope standby enabled status. If enabled, the Y-axis will not gather or report data (or use power).

See also

MPU6050_RA_PWR_MGMT_2

See also

MPU6050_PWR2_STBY_YG_BIT

Returns:

Current Y-axis standby enabled status

inline void setStandbyYGyroEnabled(bool enabled)

Set Y-axis gyroscope standby enabled status.

See also

MPU6050_RA_PWR_MGMT_2

See also

MPU6050_PWR2_STBY_YG_BIT

Parameters:

New – Y-axis standby enabled status

inline bool getStandbyZGyroEnabled()

Get Z-axis gyroscope standby enabled status. If enabled, the Z-axis will not gather or report data (or use power).

See also

MPU6050_RA_PWR_MGMT_2

See also

MPU6050_PWR2_STBY_ZG_BIT

Returns:

Current Z-axis standby enabled status

inline void setStandbyZGyroEnabled(bool enabled)

Set Z-axis gyroscope standby enabled status.

See also

MPU6050_RA_PWR_MGMT_2

See also

MPU6050_PWR2_STBY_ZG_BIT

Parameters:

New – Z-axis standby enabled status

inline uint16_t getFIFOCount()

Get current FIFO buffer size. This value indicates the number of bytes stored in the FIFO buffer. This number is in turn the number of bytes that can be read from the FIFO buffer and it is directly proportional to the number of samples available given the set of sensor data bound to be stored in the FIFO (register 35 and 36).

Returns:

Current FIFO buffer size

inline uint8_t getFIFOByte()

Get byte from FIFO buffer. This register is used to read and write data from the FIFO buffer. Data is written to the FIFO in order of register number (from lowest to highest). If all the FIFO enable flags (see below) are enabled and all External Sensor Data registers (Registers 73 to 96) are associated with a Slave device, the contents of registers 59 through 96 will be written in order at the Sample Rate.

The contents of the sensor data registers (Registers 59 to 96) are written into the FIFO buffer when their corresponding FIFO enable flags are set to 1 in FIFO_EN (Register 35). An additional flag for the sensor data registers associated with I2C Slave 3 can be found in I2C_MST_CTRL (Register 36).

If the FIFO buffer has overflowed, the status bit FIFO_OFLOW_INT is automatically set to 1. This bit is located in INT_STATUS (Register 58). When the FIFO buffer has overflowed, the oldest data will be lost and new data will be written to the FIFO.

If the FIFO buffer is empty, reading this register will return the last byte that was previously read from the FIFO until new data is available. The user should check FIFO_COUNT to ensure that the FIFO buffer is not read when empty.

Returns:

Byte from FIFO buffer

inline void setFIFOByte(uint8_t data)

Write byte to FIFO buffer.

See also

getFIFOByte()

See also

MPU6050_RA_FIFO_R_W

inline uint8_t getDeviceID()

Get Device ID. This register is used to verify the identity of the device (0b110100, 0x34).

See also

MPU6050_RA_WHO_AM_I

See also

MPU6050_WHO_AM_I_BIT

See also

MPU6050_WHO_AM_I_LENGTH

Returns:

Device ID (6 bits only! should be 0x34)

inline void setDeviceID(uint8_t id)

Set Device ID. Write a new ID into the WHO_AM_I register (no idea why this should ever be necessary though).

See also

getDeviceID()

See also

MPU6050_RA_WHO_AM_I

See also

MPU6050_WHO_AM_I_BIT

See also

MPU6050_WHO_AM_I_LENGTH

Parameters:

id – New device ID to set.

struct Motion3
struct Motion6
References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Mirf for NRF24L01

Arduino port of Mirf for the NRF24L01 modules

References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

ModbusMaster RTU Library

This library handles Modbus master RTU communication. The library supports callbacks for pre- and post-transmission, receive and transmit logging.

MB_RESPONSE_TIMEOUT

The patch provides changeable response timeout using MB_RESPONSE_TIMEOUT (in milliseconds).

The original author of the library is 4-20ma: https://github.com/4-20ma/ModbusMaster/

The one included in Sming is nomis’ fork: https://github.com/nomis/ModbusMaster (see branch fixes-2.0.1) that isn’t yet merged to the original repository.

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: ModbusMaster
ModbusMaster

` .. image:: https://img.shields.io/github/release/4-20ma/ModbusMaster.svg?maxAge=3600

` .. image:: https://img.shields.io/travis/4-20ma/ModbusMaster.svg?maxAge=3600

` .. image:: https://img.shields.io/github/license/4-20ma/ModbusMaster.svg?maxAge=3600

` .. image:: https://img.shields.io/badge/%E2%9D%A4-code%20of%20conduct-blue.svg?maxAge=3600

<CODE_OF_CONDUCT.md>`_

Overview

This is an Arduino library for communicating with Modbus slaves over RS232/485 (via RTU protocol).

Features

The following Modbus functions are available:

Discrete Coils/Flags

  • 0x01 - Read Coils

  • 0x02 - Read Discrete Inputs

  • 0x05 - Write Single Coil

  • 0x0F - Write Multiple Coils

Registers

  • 0x03 - Read Holding Registers

  • 0x04 - Read Input Registers

  • 0x06 - Write Single Register

  • 0x10 - Write Multiple Registers

  • 0x16 - Mask Write Register

  • 0x17 - Read Write Multiple Registers

Both full-duplex and half-duplex RS232/485 transceivers are supported. Callback functions are provided to toggle Data Enable (DE) and Receiver Enable (/RE) pins.

Installation
Library Manager

Install the library into your Arduino IDE using the Library Manager (available from IDE version 1.6.2). Open the IDE and click Sketch > Include Library > Manage Libraries&hellip;

Scroll or search for ModbusMaster, then select the version of the library you want to install. Quit/re-launch the IDE to refresh the list; new versions are automatically added to the list, once released on GitHub.

Refer to Arduino Tutorials > Libraries Using the Library Manager.

Zip Library

Refer to Arduino Tutorials > Libraries Importing a .zip Library.

Manual

Refer to Arduino Tutorials > Libraries Manual Installation.

Hardware

This library has been tested with an Arduino Duemilanove, PHOENIX CONTACT nanoLine controller, connected via RS485 using a Maxim MAX488EPA transceiver.

Caveats

Conforms to Arduino IDE 1.5 Library Specification v2.1 which requires Arduino IDE >= 1.5.

Arduinos prior to the Mega have one serial port which must be connected to USB (FTDI) for uploading sketches and to the RS232/485 device/network for running sketches. You will need to disconnect pin 0 (RX) while uploading sketches. After a successful upload, you can reconnect pin 0.

Support

Please submit an issue for all questions, bug reports, and feature requests. Email requests will be politely redirected to the issue tracker so others may contribute to the discussion and requestors get a more timely response.

Example

The library contains a few sketches that demonstrate use of the ModbusMaster library. You can find these in the examples folder.

/*

  Basic.pde - example using ModbusMaster library

  Library:: ModbusMaster
  Author:: Doc Walker <4-20ma@wvfans.net>

  Copyright:: 2009-2016 Doc Walker

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

#include <ModbusMaster.h>


// instantiate ModbusMaster object
ModbusMaster node;


void setup()
{
  // use Serial (port 0); initialize Modbus communication baud rate
  Serial.begin(19200);

  // communicate with Modbus slave ID 2 over Serial (port 0)
  node.begin(2, Serial);
}


void loop()
{
  static uint32_t i;
  uint8_t j, result;
  uint16_t data[6];

  i++;

  // set word 0 of TX buffer to least-significant word of counter (bits 15..0)
  node.setTransmitBuffer(0, lowWord(i));

  // set word 1 of TX buffer to most-significant word of counter (bits 31..16)
  node.setTransmitBuffer(1, highWord(i));

  // slave: write TX buffer to (2) 16-bit registers starting at register 0
  result = node.writeMultipleRegisters(0, 2);

  // slave: read (6) 16-bit registers starting at register 2 to RX buffer
  result = node.readHoldingRegisters(2, 6);

  // do something with data if read is successful
  if (result == node.ku8MBSuccess)
  {
    for (j = 0; j < 6; j++)
    {
      data[j] = node.getResponseBuffer(j);
    }
  }
}

Project inspired by `Arduino Modbus Master <http://sites.google.com/site/jpmzometa/arduino-mbrt/arduino-modbus-master>`_.

License & Authors
Copyright:: 2009-2016 Doc Walker

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Multipart Parser

Component to manage processing of multipart form data according to RFC7578, mostly used to support file uploads via HTTP/POST.

Usage

While setting up your web server, register the body parser provided by the library:

#include <MultipartParser.h>

HttpServer server;
...

server.setBodyParser(MIME_FORM_MULTIPART, formMultipartParser);

Now add a HttpMultipartResource for the path to receive the multipart data:

void fileUploadMapper(HttpFiles& files)
{
    files["file"] = <writable_stream_to_process_file>;
}

int onUpload(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response)
{
    // `file` points to the stream from `fileUploadMapper`.
    auto* file = request.files["file"];

    // TODO: use methods of `file` to check if data was processed successfully

    // TODO: setup HTTP response

    return 0;
}

...

server.paths.set("/upload", new HttpMultipartResource(fileUploadMapper, onUpload));

See HttpServer Firmware Upload for further details.

Upgrade Notes

The functionality provided by this lirbary was previously controlled by the config option ENABLE_HTTP_SERVER_MULTIPART.

To upgrade, you have to replace:

ENABLE_HTTP_SERVER_MULTIPART := 1

by:

ARDUINO_LIBRARIES += MultipartParser

in your component.mk. In addition, body parser registration must now be done explicitly by application (see above).

API Documentation
MultipartParser API
size_t formMultipartParser(HttpRequest &request, const char *at, int length)

Body parser for content-type form-data/multipart

Must be added to the web server’s list of body parsers explicitly:

#include <MultipartParser.h>
...
HttpServer server;
...
server.setBodyParser(MIME_FORM_MULTIPART, formMultipartParser);

class HttpMultipartResource : public HttpResource

HttpResource that allows handling of HTTP file upload.

Public Functions

inline HttpMultipartResource(const HttpFilesMapper &mapper, HttpResourceDelegate complete)

Create and configure a HttpResource for handling file upload.

On a normal computer the file uploads are usually using temporary space on the hard disk or in memory to store the incoming data. On an embedded device that is a luxury that we can hardly afford. Therefore we should define a map that specifies explicitly where every form field will be stored. If a field is not specified then its content will be discarded.

Parameters:
  • mapper – callback that provides information where the desired upload fields will be stored.

  • complete – callback that will be called after the request has completed.

virtual int setFileMap(HttpServerConnection &connection, HttpRequest &request, HttpResponse &response)

Callback implementation for HttpResource::onHeadersComplete. Not to be used by application code.

virtual void shutdown(HttpServerConnection &connection) override

Takes care to cleanup the connection.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: multipart-parser
Multipart form data parser
Features
  • No dependencies

  • Works with chunks of a data - no need to buffer the whole request

  • Almost no internal buffering. Buffer size doesn’t exceed the size of the boundary (~60-70 bytes)

Tested as part of Cosmonaut HTTP server.

Implementation based on node-formidable by Felix Geisendörfer.

Inspired by http-parser by Ryan Dahl.

Usage (C)

This parser library works with several callbacks, which the user may set up at application initialization time.

multipart_parser_settings callbacks;

memset(&callbacks, 0, sizeof(multipart_parser_settings));

callbacks.on_header_field = read_header_name;
callbacks.on_header_value = read_header_value;

These functions must match the signatures defined in the multipart-parser header file. For this simple example, we’ll just use two of the available callbacks to print all headers the library finds in multipart messages.

Returning a value other than 0 from the callbacks will abort message processing.

int read_header_name(multipart_parser* p, const char *at, size_t length)
{
   printf("%.*s: ", length, at);
   return 0;
}

int read_header_value(multipart_parser* p, const char *at, size_t length)
{
   printf("%.*s\n", length, at);
   return 0;
}

When a message arrives, callers must parse the multipart boundary from the Content-Type header (see the RFC for more information and examples), and then execute the parser.

multipart_parser* parser = multipart_parser_init(boundary, &callbacks);
multipart_parser_execute(parser, body, length);
multipart_parser_free(parser);
Usage (C++)

In C++, when the callbacks are static member functions it may be helpful to pass the instantiated multipart consumer along as context. The following (abbreviated) class called MultipartConsumer shows how to pass this to callback functions in order to access non-static member data.

class MultipartConsumer
{
public:
    MultipartConsumer(const std::string& boundary)
    {
        memset(&m_callbacks, 0, sizeof(multipart_parser_settings));
        m_callbacks.on_header_field = ReadHeaderName;
        m_callbacks.on_header_value = ReadHeaderValue;

        m_parser = multipart_parser_init(boundary.c_str(), &m_callbacks);
        multipart_parser_set_data(m_parser, this);
    }

    ~MultipartConsumer()
    {
        multipart_parser_free(m_parser);
    }

    int CountHeaders(const std::string& body)
    {
        multipart_parser_execute(m_parser, body.c_str(), body.size());
        return m_headers;
    }

private:
    static int ReadHeaderName(multipart_parser* p, const char *at, size_t length)
    {
        MultipartConsumer* me = (MultipartConsumer*)multipart_parser_get_data(p);
        me->m_headers++;
    }

    multipart_parser* m_parser;
    multipart_parser_settings m_callbacks;
    int m_headers;
};
Contributors

© 2012 Igor Afonov

ESP32 NimBLE

Introduction

NimBLE is a completely open source Bluetooth Low Energy (BLE) stack produced by Apache. It is more suited to resource constrained devices than bluedroid and has now been ported to the ESP32 by Espressif.

Using
  1. Add COMPONENT_DEPENDS += NimBLE to your application component.mk file.

  2. Add these lines to your application:

    #include <NimBLEDevice.h>
    
    void init()
    {
            // ...
    
            BLEDevice::init("");
    }
    
References
Used by
SoC support
  • esp32

  • esp32c3

  • esp32s3

Submodule: esp-nimble-cpp

`Latest release .. image:: https://img.shields.io/github/release/h2zero/esp-nimble-cpp.svg?style=plastic

https://img.shields.io/github/release-date/h2zero/esp-nimble-cpp.svg?style=plastic:target:https://img.shields.io/github/release-date/h2zero/esp-nimble-cpp.svg?style=plastic:alt:ReleaseDate<https://github.com/h2zero/esp-nimble-cpp/releases/latest/>`_

Need help? Have questions or suggestions? Join the .. image:: https://badges.gitter.im/NimBLE-Arduino/community.svg



esp-nimble-cpp

NimBLE CPP library for use with ESP32 that attempts to maintain compatibility with the nkolban cpp_uitls BLE API.

**An Arduino version of this library, including NimBLE, can be found here.**

This library significantly reduces resource usage and improves performance for ESP32 BLE applications as compared
with the bluedroid based library. The goal is to maintain, as much as reasonable, compatibility with the original
library but refactored to use the NimBLE stack. In addition, this library will be more actively developed and maintained
to provide improved capabilites and stability over the original.

Testing shows a nearly 50% reduction in flash use and approx. 100kB less ram consumed vs the original!
Your results may vary

What is NimBLE?

NimBLE is a completely open source Bluetooth Low Energy stack produced by Apache.
It is more suited to resource constrained devices than bluedroid and has now been ported to the ESP32 by Espressif.

Installation
ESP-IDF v4.0+

Download as .zip and extract or clone into the components folder in your esp-idf project.

Run menuconfig, go to Component config->Bluetooth enable Bluetooth and in Bluetooth host NimBLE.
Configure settings in NimBLE Options.
#include "NimBLEDevice.h" in main.cpp.
Call NimBLEDevice::init(""); in app_main.

ESP-IDF v3.2 & v3.3

The NimBLE component does not come with these versions of IDF (now included in 3.3.2 and above).
A backport that works in these versions has been created and is available here.
Download or clone that repo into your project/components folder and run menuconfig. Configure settings in main menu -> NimBLE Options.

#include "NimBLEDevice.h" in main.cpp.
Call NimBLEDevice::init(""); in app_main.

Using

This library is intended to be compatible with the original ESP32 BLE functions and types with minor changes.

If you have not used the original Bluedroid library please refer to the New user guide.

If you are familiar with the original library, see: The migration guide for details about breaking changes and migration.

Also see Improvements_and_updates for information about non-breaking changes.

Full API documentation and class list can be found here.

When using this library along with Arduino and compiling with CMake you must add add_compile_definitions(ARDUINO_ARCH_ESP32=1)
in your project/CMakeLists.txt after the line include($ENV{IDF_PATH}/tools/cmake/project.cmake) to prevent Arduino from releasing BLE memory.

Acknowledgments

OneWire for Arduino

https://github.com/PaulStoffregen/OneWire.git

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Over-The-Air(OTA) Upgrader

Introduction

This architecture-agnostic component adds support for Over-The-Air upgrades.

Usage
  1. Add COMPONENT_DEPENDS += Ota to your application component.mk file.

  2. Add these lines to your application:

    #include <Ota/Manager.h>
    

After that you will have access to a global OtaManager instance that can be used to manage your OTA upgrade process.

  1. You can use OtaManager to get information about the bootable partitions and update them. The code below will display the current bootable and running partition:

    void init()
    {
    
        // ...
        auto partition = OtaManager.getRunningPartition();
    
        Serial.printf("\r\nCurrently running %s @ 0x%08x.\r\n", partition.name().c_str(), partition.address());
    
    }
    
  2. If needed you can also create your own instance of the of OtaUpgrader as shown below:

    // Call when IP address has been obtained
    void onIp(IpAddress ip, IpAddress mask, IpAddress gateway)
    {
       // ...
    
       OtaUpgrader ota;
    
       auto part = ota.getNextBootPartition();
    
       ota.begin(part);
    
       // ... write all the data to the partition
    
       ota.end();
    
       // ...
    }
    

See the Basic Ota sample application.

API Documentation
namespace Ota
class IdfUpgrader : public Ota::UpgraderBase
#include <IdfUpgrader.h>

ESP32 OTA Upgrader implementation.

Public Functions

virtual bool begin(Partition partition, size_t size = 0) override

Prepares a partition for an upgrade. The preparation is bootloader and architecture dependent.

Parameters:
  • partition

  • size

Return values:

bool

virtual size_t write(const uint8_t *buffer, size_t size) override

Writes chunk of data to the partition set in begin().

Parameters:
  • buffer

  • size

Return values:

size_t – actually written bytes

inline virtual bool end() override

Finalizes the partition upgrade.

inline virtual bool abort() override

Aborts a partition upgrade.

inline virtual bool setBootPartition(Partition partition, bool save = true) override

Sets the default partition from where the application will be booted on next restart.

Parameters:
  • partition

  • save – if true the change is persisted on the flash, otherwise it will be valid only for the next boot

Return values:

bool

inline virtual Partition getBootPartition() override

Gets information about the partition that is set as the default one to boot.

Note

The returned partition can be different than the current running partition.

Return values:

partition

inline virtual Partition getRunningPartition() override

Gets information about the partition from which the current application is running.

Note

The returned partition can be different than the default boot partition.

Return values:

partition

inline virtual Partition getNextBootPartition(Partition startFrom = {}) override

Gets the next bootable partition that can be used after successful OTA upgrade.

Parameters:

startFrom – - optional

Return values:

partition

class RbootUpgrader : public Ota::UpgraderBase
#include <RbootUpgrader.h>

ESP8266 rBoot OTA Upgrader implementation.

Public Functions

virtual bool begin(Partition partition, size_t size = 0) override

Prepare the partition for.

virtual size_t write(const uint8_t *buffer, size_t size) override

Writes chunk of data to the partition set in begin().

Parameters:
  • buffer

  • size

Return values:

size_t – actually written bytes

inline virtual bool end() override

Finalizes the partition upgrade.

virtual bool setBootPartition(Partition partition, bool save = true) override

Sets the default partition from where the application will be booted on next restart.

Parameters:
  • partition

  • save – if true the change is persisted on the flash, otherwise it will be valid only for the next boot

Return values:

bool

inline virtual Partition getBootPartition() override

Gets information about the partition that is set as the default one to boot.

Note

The returned partition can be different than the current running partition.

Return values:

partition

virtual Partition getRunningPartition() override

Gets information about the partition from which the current application is running.

Note

The returned partition can be different than the default boot partition.

Return values:

partition

inline virtual Partition getNextBootPartition(Partition startFrom = {}) override

Gets the next bootable partition that can be used after successful OTA upgrade.

Parameters:

startFrom – - optional

Return values:

partition

class UpgradeOutputStream : public ReadWriteStream
#include <UpgradeOutputStream.h>

Write-only stream type used during firmware upgrade.

Public Functions

inline UpgradeOutputStream(Partition partition, size_t maxLength = 0)

Construct a stream for the given partition.

Parameters:

partition

virtual size_t write(const uint8_t *data, size_t size) override

Write chars to stream.

Note

Although this is defined in the Print class, ReadWriteStream uses this as the core output method so descendants are required to implement it

Parameters:
  • buffer – Pointer to buffer to write to the stream

  • size – Quantity of chars to write

Return values:

size_t – Quantity of chars written to stream

inline virtual StreamType getStreamType() const override

Get the stream type.

Return values:

StreamType – The stream type.

inline virtual uint16_t readMemoryBlock(char *data, int bufSize) override

Read a block of memory.

Todo:

Should IDataSourceStream::readMemoryBlock return same data type as its bufSize param?

Parameters:
  • data – Pointer to the data to be read

  • bufSize – Quantity of chars to read

Return values:

uint16_t – Quantity of chars read

inline virtual bool seek(int len) override

Move read cursor.

Parameters:

len – Relative cursor adjustment

Return values:

bool – True on success.

inline virtual int available() override

Return the total length of the stream.

Return values:

int – -1 is returned when the size cannot be determined

inline virtual bool isFinished() override

Check if all data has been read.

Return values:

bool – True on success.

class UpgraderBase
#include <UpgraderBase.h>

Subclassed by Ota::IdfUpgrader, Ota::RbootUpgrader

Public Functions

virtual bool begin(Partition partition, size_t size = 0) = 0

Prepares a partition for an upgrade. The preparation is bootloader and architecture dependent.

Parameters:
  • partition

  • size

Return values:

bool

virtual size_t write(const uint8_t *buffer, size_t size) = 0

Writes chunk of data to the partition set in begin().

Parameters:
  • buffer

  • size

Return values:

size_t – actually written bytes

virtual bool end() = 0

Finalizes the partition upgrade.

inline virtual bool abort()

Aborts a partition upgrade.

virtual bool setBootPartition(Partition partition, bool save = true) = 0

Sets the default partition from where the application will be booted on next restart.

Parameters:
  • partition

  • save – if true the change is persisted on the flash, otherwise it will be valid only for the next boot

Return values:

bool

virtual Partition getBootPartition() = 0

Gets information about the partition that is set as the default one to boot.

Note

The returned partition can be different than the current running partition.

Return values:

partition

virtual Partition getRunningPartition() = 0

Gets information about the partition from which the current application is running.

Note

The returned partition can be different than the default boot partition.

Return values:

partition

virtual Partition getNextBootPartition(Partition startFrom = {}) = 0

Gets the next bootable partition that can be used after successful OTA upgrade.

Parameters:

startFrom – - optional

Return values:

partition

inline Storage::Iterator getBootPartitions()

Gets information about all bootable partitions.

Return values:

Storage::Iterator

inline uint8_t getSlot(Partition partition)

Gets slot number for a partition.

Parameters:

partition

Return values:

uint8_t – slot number

namespace Network

Variables

constexpr uint8_t NO_ROM_SWITCH = {0xff}

Magic value for ROM slot indicating slot won’t change after successful OTA.

class HttpUpgrader : protected HttpClient
#include <HttpUpgrader.h>

Public Functions

inline bool addItem(const String &firmwareFileUrl, Partition partition, ReadWriteStream *stream = nullptr)

Add an item to update.

Parameters:
  • firmwareFileUrl

  • partition – Target partition to write

  • stream

Return values:

bool

inline void switchToRom(uint8_t romSlot)

On completion, switch to the given ROM slot.

Parameters:

romSlot – specify NO_ROM_SWITCH (the default) to cancel any previously set switch

inline void setBaseRequest(HttpRequest *request)

Sets the base request that can be used to pass.

   - default request parameters, like request headers...
   - default SSL options
   - default SSL fingeprints
   - default SSL client certificates

Parameters:

request

inline const ItemList &getItems() const

Allow read access to item list.

struct Item
#include <HttpUpgrader.h>
class ItemList : public Vector<Item>
#include <HttpUpgrader.h>
References
Used by
Environment Variables
SoC support
  • esp8266

  • host

Over-The-Air(OTA) Network Upgrader

Introduction

This architecture-agnostic component adds support for Over-The-Air upgrades.

Usage
  1. Add COMPONENT_DEPENDS += Ota to your application componenent.mk file.

  2. Add these lines to your application:

    #include <Ota/Manager.h>
    

After that you will have access to a global OtaManager instance that can be used to manage your OTA upgrade process.

  1. You can use OtaManager to get information about the bootable partitions and update them. The code below will display the current bootable and running partition:

    void init()
    {
    
        // ...
        auto partition = OtaManager.getRunningPartition();
    
        Serial.printf("\r\nCurrently running %s @ 0x%08x.\r\n", partition.name().c_str(), partition.address());
    
    }
    
  2. If needed you can also create your own instance of the of OtaUpgrader as shown below:

    // Call when IP address has been obtained
    void onIp(IpAddress ip, IpAddress mask, IpAddress gateway)
    {
       // ...
    
       OtaUpgrader ota;
    
       auto partition = ota.getNextBootPartition();
    
       ota.begin(partition);
    
       // ... write all the data to the partition
    
       ota.end();
    
       // ...
    }
    

See the Basic Ota sample application.

API Documentation
namespace Network

Variables

constexpr uint8_t NO_ROM_SWITCH = {0xff}

Magic value for ROM slot indicating slot won’t change after successful OTA.

class HttpUpgrader : protected HttpClient
#include <HttpUpgrader.h>

Public Functions

inline bool addItem(const String &firmwareFileUrl, Partition partition, ReadWriteStream *stream = nullptr)

Add an item to update.

Parameters:
  • firmwareFileUrl

  • partition – Target partition to write

  • stream

Return values:

bool

inline void switchToRom(uint8_t romSlot)

On completion, switch to the given ROM slot.

Parameters:

romSlot – specify NO_ROM_SWITCH (the default) to cancel any previously set switch

inline void setBaseRequest(HttpRequest *request)

Sets the base request that can be used to pass.

   - default request parameters, like request headers...
   - default SSL options
   - default SSL fingeprints
   - default SSL client certificates

Parameters:

request

inline const ItemList &getItems() const

Allow read access to item list.

struct Item
#include <HttpUpgrader.h>
class ItemList : public Vector<Item>
#include <HttpUpgrader.h>
References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

Over-the-Air Firmware Upgrade

This library offers a writable stream that decodes and applies [Over-the-Air] firmware upgrade files, as well as a small python utility to generate those upgrade files as part of Sming’s build process. It may be combined with any transport mechanism that is compatible with Sming’s stream classes. Check out the HttpServer Firmware Upload example, which demonstrates how the integrated HTTP server can be used to provide a web form for uploading new firmware images from the browser.

Prerequisites

Every in-system firmware upgrade mechanism for ESP-based devices requires partitioning the flash into two slots: One slot holds the currently running firmware, while the other slot receives the upgrade. As a consequence only half of the available flash memory can be used for the firmware. (Actually a bit less because a few sectors are reserved for the bootloader and various parameter blobs.)

In most cases, it is sufficient to set RBOOT_ROM1_ADDR to the offset address of the second slot. See the rBoot documentation for further options and considerations. If your partitioning choice results in two ROM images being created, they are transparently combined such that there is always a single OTA upgrade file. During the upgrade, the OTA code will automatically select the right image and ignore the one for the other slot.

Attention

Make sure that the ROM slots do not overlap with each other or with areas of the flash allocated to other purposes (file system, RF calibration parameters, etc.). Sming will not detect a misconfigured flash layout.

Security features leverage libsodium, which is automatically pulled in as a dependency when signing and/or encryption is enabled. You also have to install libsodium bindings for python on your development computer, either using python -m pip install PyNaCl (recommended for Windows users) or, if your are on Linux, preferably via your distribution’s package manager (search for a package named ‘python-nacl’).

Usage
The OtaUpgradeStream class

The library provides the class OtaUpgradeStream (actually, an alias for either OtaUpgrade::BasicStream or OtaUpgrade::EncryptedStream, depending on ENABLE_OTA_ENCRYPTION.), which derives from ReadWriteStream, but, despite its base class, is only a writable stream.

At construction time, the address and size of the slot to receive the new firmware is automatically determined from the rBoot configuration. No further setup is required. Just feed the OTA upgrade file into the OtaUpgradeStream::write method in arbitrarily sized chunks. The flash memory is updated on the fly as data arrives and upon successful validation, the updated slot is activated in the rRoot configuration.

Once the file is complete, call OtaUpgradeStream::hasError to check for any errors that might have occurred during the upgrade process. The actual error, if any, is stored in the public member OtaUpgradeStream::errorCode and can be converted to an error message using OtaUpgradeStream::errorToString. In addition, you may also examine the return value of the OtaUpgradeStream::write method, which will be equal to the given chunk size, unless there is an error with the file or the upgrade process.

Building

The library is fully integrated into the Sming build process. Just run:

make

and find the OTA upgrade file in out/<arch>/<config>/firmware/firmware.ota. If security features are enabled but no secret key file does exist yet, a new one is generated during the first build. You may change it later by modifying OTA_KEY or using the Key/Settings rollover process.

Now install the OTA-enabled firmware once via USB/Serial cable and you are all set to do future upgrades wirelessly over your chosen communication channel.

A convenience target:

make ota-upload OTA_UPGRADE_URL=http://<your-ip>/upgrade

is provided for the not too uncommon use case of uploading the OTA file as a HTTP/POST request (but obviously is of no value for other transport mechanisms). The URL is cached and can be omitted from subsequent invocations.

Configuration and Security features
ENABLE_OTA_SIGNING

Default: 1 (enabled)

If set to 1 (highly recommended), OTA upgrade files are protected against unauthorized modification by a digital signature. This is implemented using libsodium’s crypto_verify_… API, which encapsulates a public key algorithm: A secret (or ‘private’) signing key never leaves the development computer, while a non-secret (‘public’) verification key is embedded into the firmware. Public key algorithms cannot be broken even if an attacker gains physical access to one of your devices and extracts the verification key from flash memory, because only someone in possession of the secret signing key (see OTA_KEY) is able to create upgrade files with a valid signature.

Note

You may disable signing in order to save some program memory if your communication channel already establishes a comparable level of trust, e.g. TLS with a pinned certificate.

OTA_ENABLE_ENCRYPTION

Default: 0 (disabled)

Set to 1 to enable encryption of the upgrade file using libsodium’s crypto_secretstream_… API, in order to protect confidential data embedded in your firmware (WiFi credentials, server certificates, etc.).

It is generally unnecessary to sign encrypted upgrade files, as encryption is also authenticating, i.e. only someone in possession of the secret encryption key can generate upgrade files that decrypt successfully. There is, however, one catch: Unlike signing, encryption can be broken if an attacker is able to extract the decryption key (which is identical to the encryption key) from flash memory, in which case all current and future files encrypted with the same key are compromised. Moreover, the attacker will be able to generate new valid upgrade files modified to his or her agenda. Hence, you should only ever rely on encryption if it is impossible for an attacker to gain physical access to your device(s). But otherwise, you shouldn’t have stored confidential data on such device(s) in the first place. Conversely, you should not encrypt upgrade files that do not contain confidential data, to avoid the risk of accidentally exposing a key you might want to reuse later. For this reason, encryption is disabled by default.

Note: To mitigate a catastrophic security breach when the encryption key is revealed involuntarily, encryption and signing can be enabled at the same time. This way, an attacker (who probably has access to your WiFi by now) will at least be unable to take over more devices wirelessly. But keep in mind: it is still not a good idea to store confidential data on an unsecured device.

Note also that the described weakness is not a property of the selected encryption algorithm, but a rather general one. It can only be overcome by encrypting the communication channel instead of the upgrade file, e.g. with TLS, which uses a key exchange protocol to negotiate a temporary encryption key that is never written to flash memory. But even then, it is still unwise to embed confidential data into the firmware of a device that is physically accessible to an attacker - now you have been warned!

OTA_KEY

Path to the secret encryption/signing key. The default is ota.key in the root directory of your project. If the key file does not exist, it will be generated during the first build. It can also be (re-)generated manually using the following command (usually as part of a Key/Settings rollover process):

make ota-genkey

The key file must be kept secret for obvious reasons. In particular, set up your .gitignore (or equivalent VCS mechanism) carefully to avoid accidentally pushing the key file to a public repository.

By pointing OTA_KEY to a shared location, the same key file can be used for multiple projects, even if their security settings differ, since the key file format is independent of the security settings. (In fact, it is just a string of random numbers, from which the actual algorithm keys are derived.)

ENABLE_OTA_DOWNGRADE

Default: 0 (disabled)

By default, OtaUpgradeStream refuses to downgrade to an older firmware version, in order to prevent an attacker from restoring already patched security vulnerabilities. This is implemented by comparing timestamps embedded in the firmware and the upgrade file. To disable downgrade protection, set ENABLE_OTA_DOWNGRADE to 1.

Downgrade protection must be combined with encryption or signing to be effective. A warning is issued by the build system otherwise.

OTA_UPLOAD_URL

URL used by the make ota-upload command.

OTA_UPLOAD_NAME

Field name for the upgrade file in the HTTP/POST request issued by make ota-upload, corresponding to the name attribute of the HTML input element:

<input type="file" name="firmware" />

The default is “firmware”.

Key/Settings rollover process

There might be occasions where you want to change the encryption/signing key and or other OTA security settings (e.g. switch from signing to encryption or vice versa). While you could always install the new settings via USB/serial cable, you can also follow the steps below to achieve the same goal wirelessly:

  1. Before modifying any security-related settings, start the rollover process by issuing:

    make ota-rollover
    
  2. Now modify security settings as desired, e.g. generate a new key using make ota-genkey.

  3. Run make to build a rollover upgrade file. The firmware image(s) contained in this file use the new security settings, while the upgrade file itself is created with the old settings (saved by the command in step 1) and thus is still compatible with the firmware currently running on your device(s).

  4. Upgrade wirelessly using the rollover file created in step 3. The new security settings are now installed.

  5. Finalize the rollover process using the command:

    make ota-rollover-done
    

    This will delete temporary files created by step 1.

OTA upgrade file format
Basic file format

The following layout is used for unencrypted upgrade files, as well as for the data inside the encrypted container (see next paragraph). All fields are stored in little-endian byte order.

Field size (bytes)

Field description

4

Magic number for file format identification:
0xf01af02a for signed images
0xf01af020 for images without signature

8

OTA upgrade file timestamp in milliseconds since 1900/01/01 (used for downgrade protection)

1

Number of ROM images (1 or 2)

3

reserved, always zero

variable

ROM images, see below

64 (signed)
16 (otherwise)
With signature: Digital signature over the whole file up to this point.
Otherwise: MD5 HASH over the whole file up to this point. This is not a security measure but merely protects the integrity of the file. MD5 was selected, because it already available in the ESP8266’s on-chip ROM.

Each ROM image has the following format:

Field size (bytes)

Field description

4

Start address in flash memory (i.e. RBOOT_ROM0_ADDR for first ROM)

4

Size of ROM in bytes

variable (see previous field)

ROM image content

More content may be added in a future version (e.g. SPIFFS images, bootloader image, RF calibration data blob). The reserved bytes in the file header are intended to announce such additional content.

Encryption Container format

Encrypted files are stored in chunks suitable for consumption by libsodium’s crypto_secretstream_… API.

The first chunk is always 24 bytes and is fed into crypto_secretstream_pull_init to initialize the decryption algorithm.

Subsequent chunks are composed of:

  • A 2 byte header indicating the length of the chunk minus 1. The default chunk size used by otatool.py is 2 kB.

  • The data of the chunk, which is fed into crypto_secretstream_pull.

For further information on the data stored in the header and the chunks, refer to libsodium’s documentation and/or source code.

API Documentation
OTA Upgrade Stream classes
typedef OtaUpgrade::BasicStream OtaUpgradeStream

Alias for either OtaUpgrade::BasicStream or OtaUpgrade::EncryptedStream, depending on encryption settings.

Application code should use this alias to avoid source code modifications when changing OTA upgrade security settings.

class BasicStream : public ReadWriteStream

A write-only stream to parse and apply firmware unencrypted upgrade files generated by otatool.py.

The class fully automates the firmware upgrade process without any manual configuration. At construction time, the rBoot configuration is read to determine the unused ROM slot which should receive the upgrade. Just feed the upgrade file content into the write() method in arbitrarily sized chunks. The relevant portion(s) of the Flash memory (currently only the application rom) are updated on the fly as data arrives. When the file is complete and signature validation (if enabled) was successful, the updated slot is activated in the rBoot configuration. Call hasError() and/or check the public errorCode member to determine if everything went smoothly.

For further information on configuration options and the file format, refer to the library’s documentation.

See also

EncryptedStream for encryption support.

Subclassed by OtaUpgrade::EncryptedStream

class EncryptedStream : public OtaUpgrade::BasicStream

Encryption wrapper for BasicStream.

The class processes encrypted firmware upgrade files created by otatool.py. A buffer is allocated dynamically to fit the largest chunk of the encryption container (2kB unless otatool.py was modified). The actual processing of the decrypted data is deferred to BasicStream.

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

OTA Firmware Upgrade via MQTT

Introduction

This library allows Sming applications to upgrade their firmware Over-The-Air (OTA) using the MQTT protocol. MTQTT has less overhead compared to HTTP and can be used for faster delivery of application updates.

Using
  1. Add COMPONENT_DEPENDS += OtaUpgradeMqtt to your application componenent.mk file.

  2. Add these lines to your application:

    #include <OtaUpgrade/Mqtt/RbootPayloadParser.h>
    
    #if ENABLE_OTA_ADVANCED
    #include <OtaUpgrade/Mqtt/AdvancedPayloadParser.h>
    #endif
    
    MqttClient mqtt;
    
    // Call when IP address has been obtained
    void onIp(IpAddress ip, IpAddress mask, IpAddress gateway)
    {
       // ...
    
       mqtt.connect(Url(MQTT_URL), "sming");
    
    #if ENABLE_OTA_ADVANCED
        /*
         * The advanced parser suppors all firmware upgrades supported by the `OtaUpgrade` library.
         * `OtaUpgrade` library provides firmware signing, firmware encryption and so on.
         */
        auto parser = new OtaUpgrade::Mqtt::AdvancedPayloadParser(APP_VERSION_PATCH);
    #else
        /*
         * The command below uses class that stores the firmware directly
         * using RbootOutputStream on a location provided by us
         */
        auto parser = new OtaUpgrade::Mqtt::RbootPayloadParser(part, APP_VERSION_PATCH);
     #endif
    
          mqtt.setPayloadParser([parser]
          (MqttPayloadParserState& state, mqtt_message_t* message, const char* buffer, int length) -> int
          {
             return parser->parse(state, message, buffer, length);
          });
    
          String updateTopic = "a/test/u/4.3";
          mqtt.subscribe(updateTopic);
    
       // ...
    }
    

See the OTA over MQTT sample application.

Versioning Principles

To simplify the OTA process we strongly recommend the following versioning principles for your application:

  1. Use semantic versioning. If your current application version is 4.3.1 then 4 is the major, 3 is the minor and 1 is the patch version number.

  2. Every application firmware knows its version.

  3. An application with the same major and minor version should be compatible for update no matter what the patch number is. If the new firmware is not compatible then a new minor or major version should be used.

Theory Of Operation
  1. On a period of time the application connects to check if there is a new version of the firmware. In your application this period has to be carefully selected so that OTA updates occur when the device has enough resources: memory, space on flash, power and time to complete such an update. Also there should be no critical task running at the moment. Depending on the size of the new firmware and the speed of the connection an update can take 10 to 20 seconds.

  2. The application connects via MQTT to a remote server and subscribes to a special topic. The topic is based on the application id and its current version. If the current application id is test and version is 4.3.1 then the topic that will be used for OTA is a/test/u/4.3.

  3. If there is a need to support both stable and unstable/nightly builds then the topic name can have s or u suffix. For example all stable versions should be published and downloaded from the topic a/test/u/4.3/s. For the unstable ones we can use the topic a/test/u/4.3/u. If an application is interested in both stable and unstable versions then it can subscribe using the following pattern a/test/u/4.3/+.

  4. The application is waiting for new firmware. When the application is on battery than it makes sense to wait for a limited time and if there is no message coming back to disconnect.

Firmware packaging

The firmware update must come as one MQTT message. The MQTT protocol allows messages with a maximum size of 268435455 bytes approx 260MB. This should be perfectly enough for a device that has maximum 1MB available for an application ROM.

One MQTT message contains:

  • patch version of the firmware

  • followed by the firmware data itself

Based on the ENABLE_OTA_VARINT_VERSION the patch version can be encoded either using one byte or a varint. Based on ENABLE_OTA_ADVANCED the firmware data can be either without any encoding or be signed and encrypted.

To simplify the packaging this library comes with a tool called deployer. To create a package type the following from your application:

make ota-pack OTA_PATCH_VERSION=127

Replace 127 with the desired patch version. If the option OTA_PATCH_VERSION is omitted from the command line then the patch version will be generated automatically and it will contain the current unix timestamp.

Once a package is created it can be deployed to the firmware MQTT server using the command below:

make ota-deploy MQTT_FIRMWARE_URL=mqtt://relser:relpassword@attachix.com/a/test/u/4.3

The MQTT_FIRMWARE_URL above specifies that

  • protocol is: mqtt without SSL. Allowed values here are mqtt and mqtts. The latter uses SSL.

  • user is: relser

  • password is: relpassword

  • host is: attachix.com

  • path is: /a/test/u/4.3. The path without leading and ending slashes is used to generate the topic name a/test/u/4.3.

Make sure to replace the MQTT_FIRMWARE_URL value with your MQTT server credentials, host and topic.

Security

For additional security a standard SSL/TLS can be used

  1. The communication should be secured using standard SSL.

  2. To prove that the server is the correct one: The MQTT clients should pin the public key fingerprint on the server. OR have a list of public key fingerprints that are allowed.

  3. To prove that the clients are allowed to connect: Every MQTT client should also have a client certificate that is signed by the server.

Configuration
ENABLE_OTA_VARINT_VERSION

Default: 1 (enabled)

If set to 1 the OTA upgrade mechanism and application will use a varint encoding for the patch version. Thus allowing unlimited number of patch versions. Useful for enumerating unstable/nightly releases. A bit more difficult to read and write but allows for unlimited versions.

If set to 0 the OTA upgrade mechanism and application will use one byte for the patch version which will limit it to 256 possible patch versions. Useful for enumerating stable releases. Easier to write and read but limited to 256 versions only.

ENABLE_OTA_ADVANCED

Default: 0 (disabled)

If set to 1 the library will work with OtaUpgradeStream which supports signature and encryption of the firmware data itself. See Over-the-Air Firmware Upgrade for details. In the application the AdvancedPayloadParser can be used to do the MQTT message handling.

API
API Documentation
namespace Mqtt

Typedefs

using RbootPayloadParser = StandardPayloadParser

Deprecated:

Use StandardPayloadParser

Variables

constexpr int8_t VERSION_NOT_READY = {-1}
constexpr int8_t ERROR_INVALID_MQTT_MESSAGE = {-1}
constexpr int8_t ERROR_INVALID_PATCH_VERSION = {-2}
constexpr int8_t ERROR_UNKNOWN_REASON = {-10}
class AdvancedPayloadParser : public OtaUpgrade::Mqtt::PayloadParser
#include <AdvancedPayloadParser.h>

This parser allows the processing of firmware data that can be encrypted or have signature

Public Functions

virtual bool switchRom(const UpdateState &updateState) override

This method is responsible for switching the rom. This method is NOT restarting the system. It will happen lated in the parse method.

Return values:

true – if the switch was successful

virtual ReadWriteStream *getStorageStream(size_t storageSize) override

Creates new stream to store the firmware update.

Parameters:

size_t – storageSize the requested storage size

Return values:

ReadWriteStream*

inline PayloadParser(size_t currentPatchVersion, size_t allowedVersionBytes = 24)
Parameters:
  • currentPatchVersion

  • allowedVersionBytes – - maximum allowed bytes to be used for describing a patch version

class PayloadParser
#include <PayloadParser.h>

Subclassed by OtaUpgrade::Mqtt::AdvancedPayloadParser, OtaUpgrade::Mqtt::StandardPayloadParser

Public Functions

inline PayloadParser(size_t currentPatchVersion, size_t allowedVersionBytes = 24)
Parameters:
  • currentPatchVersion

  • allowedVersionBytes – - maximum allowed bytes to be used for describing a patch version

virtual bool switchRom(const UpdateState &updateState) = 0

This method is responsible for switching the rom. This method is NOT restarting the system. It will happen lated in the parse method.

Return values:

true – if the switch was successful

virtual ReadWriteStream *getStorageStream(size_t storageSize) = 0

Creates new stream to store the firmware update.

Parameters:

size_t – storageSize the requested storage size

Return values:

ReadWriteStream*

int parse(MqttPayloadParserState &state, mqtt_message_t *message, const char *buffer, int length)

This method takes care to read the incoming MQTT message and pass it to the stream that is responsoble for storing the data.

Return values:

int – 0 when everything is ok <0 when an error has occurred

struct UpdateState
#include <PayloadParser.h>
class StandardPayloadParser : public OtaUpgrade::Mqtt::PayloadParser
#include <StandardPayloadParser.h>

This parser allows the processing of firmware data that is directly stored to the flash memory using RbootOutputStream.

Public Functions

virtual bool switchRom(const UpdateState &updateState) override

This method is responsible for switching the rom. This method is NOT restarting the system. It will happen lated in the parse method.

Return values:

true – if the switch was successful

virtual ReadWriteStream *getStorageStream(size_t storageSize) override

Creates new stream to store the firmware update.

Parameters:

size_t – storageSize the requested storage size

Return values:

ReadWriteStream*

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

rc-switch

Build Status

Use your Arduino or Raspberry Pi to operate remote radio controlled devices

Download

https://github.com/sui77/rc-switch/releases/latest

Wiki

https://github.com/sui77/rc-switch/wiki

Info
Send RC codes

Use your Arduino or Raspberry Pi to operate remote radio controlled devices. This will most likely work with all popular low cost power outlet sockets. If yours doesn’t work, you might need to adjust the pulse length.

All you need is a Arduino or Raspberry Pi, a 315/433MHz AM transmitter and one or more devices with one of the supported chipsets:

  • SC5262 / SC5272

  • HX2262 / HX2272

  • PT2262 / PT2272

  • EV1527 / RT1527 / FP1527 / HS1527

  • Intertechno outlets

Receive and decode RC codes

Find out what codes your remote is sending. Use your remote to control your Arduino.

All you need is an Arduino, a 315/433MHz AM receiver (although there is no instruction yet, yes it is possible to hack an existing device) and a remote hand set.

For the Raspberry Pi, clone the https://github.com/ninjablocks/433Utils project to compile a sniffer tool and transmission commands.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Arduino driver for nRF24L01 2.4GHz Wireless Transceiver

Design Goals: This library is designed to be…

  • Maximally compliant with the intended operation of the chip

  • Easy for beginners to use

  • Consumed with a public interface that’s similar to other Arduino standard libraries

  • Built against the standard SPI library.

Please refer to:

This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or the SPI hardware will go into ‘slave’ mode.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

RapidXML

This is a port of https://github.com/dwd/rapidxml with minimal patch, and additional support code for Sming.

API Documentation
namespace XML

Unnamed Group

bool deserialize(Document &doc, char *content)

De-serialise XML text into a document.

Note

Document maintains references to content so MUST stay in scope for lifetime of doc, or until doc.clear() is called

Return values:

bool – false on parse error

static inline bool deserialize(Document &doc, String &content)
bool deserialize(Document &doc, const FlashString &content)

Unnamed Group

size_t serialize(const Node &node, String &buffer, bool pretty = false)

Serialize XML text and append to string buffer.

Return values:

size_t – number of characters written

String serialize(const Node &node, bool pretty = false)
size_t serialize(const Node &node, Print &out, bool pretty = false)
static inline size_t serialize(const Node &node, Print *out, bool pretty = false)

Unnamed Group

Node *appendNode(Node *parent, const char *name, const char *value = nullptr, size_t name_size = 0, size_t value_size = 0)

Append a child element with optional value.

Note

If provide, sizes do not include nul-terminator Node MUST already be added to the document tree

Parameters:
  • parent – Node to append child to

  • name – Name of child node (copy taken)

  • value – Optional node value (copy taken)

Return values:

Node* – Child node

static inline Node *appendNode(Node *parent, const String &name, const String &value = nullptr)
Node *appendNode(Node *parent, const String &name, const FlashString &value)
template<typename TString, typename TValue>
Node *appendNode(Node *parent, const TString &name, const TValue &value)

Unnamed Group

Attribute *appendAttribute(Node *node, const char *name, const char *value, size_t name_size = 0, size_t value_size = 0)

Append an attribute.

Parameters:
  • parent – Node to append child to

  • name – Name of attribute (copy taken)

  • value – Value string (copy taken)

  • Attribute* – New attribute

static inline Attribute *appendAttribute(Node *node, const String &name, const String &value)
template<typename TString, typename TValue>
Attribute *appendAttribute(Node *node, const TString &name, const TValue &value)

Gets child node by relative path

Leading/trailing separators are not permitted.

param doc:

param path:

Node path using slash as separator

param ns:

Optional namespace

retval Node*:

nullptr if none is found

Node *getNode(Node *node, const char *path, const char *ns, size_t ns_len = 0)
inline Node *getNode(Node *node, const String &path, const String &ns = nullptr)

Gets node from a document by path

Leading separator is important: if present, search starts at root node. If omitted, first element must match the root node name.

param doc:

param path:

Node path using slash as separator

param ns:

Optional namespace

retval Node*:

nullptr if none is found

Trailing separator is not allowed.

Example 1: /s:Body/u:GetContentResponse Will search for s:Body node that is a child of the root node. The node u:GetContentResponse should be child of the s:Body node

Example 2: s:Envelope/s:Body/u:GetContentResponse Will search for s:Body node that is a child of the root node named s:Envelope. The node u:GetContentResponse should be child of the s:Body node

Node *getNode(const Document &doc, const char *path, const char *ns = nullptr, size_t ns_len = 0)
inline Node *getNode(const Document &doc, const String &path, const String &ns = nullptr)

Get node value

param node:

param name:

param name_size:

retval String:

invalid if node or name not found

String getValue(const Node *node, const char *name, size_t name_size, const char *ns = nullptr, size_t ns_size = 0)
inline String getValue(const Node *node, const char *name, const char *ns = nullptr)
inline String getValue(const Node *node, const String &name, const String &ns = nullptr)

Get attribute value

param node:

param name:

param name_size:

retval String:

invalid if node or name not found

String getAttribute(const Node *node, const char *name, size_t name_size)
inline String getAttribute(const Node *node, const char *name)
inline String getAttribute(const Node *node, const String &name)

Typedefs

using Document = rapidxml::xml_document<char>
using Node = rapidxml::xml_node<char>
using NodeType = rapidxml::node_type
using Attribute = rapidxml::xml_attribute<char>

Functions

Node *insertDeclaration(Document &doc)

Add a declaration to the document if there isn’t one already.

Note

By default, declarations are included when parsed during de-serialisation. If you need one in the output serialisation, call this function. (We’re talking about the “<?xml version=”1.0” ?>” at the start)

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Embedded RingBufCPP

This is a simple ring (FIFO) buffer queuing library for embedded platforms, such as Arduino’s. This library is based on a previous one I wrote, only now in C++ instead of C. It uses C++’s templating, so no more weird pointer casting, and deep copies of objects are supported. It has concurrency protection built in, so feel free to do operations on the buffer inside of ISR’s. All memory is statically allocated at compile time, so no heap memory is used. It can buffer any fixed size object (ints, floats, structs, objects, etc…).

FAQ’s
I only have a C compiler for my platform
No worries, try the vanilla C version of the library.
Use Cases

A ring buffer is used when passing asynchronous io between two threads. In the case of the Arduino, it is very useful for buffering data in an interrupt routine that is later processed in your void loop().

Supported Platforms

The library currently supports:

  • AVR

  • ESP8266

  • Any other platform (just implement the RB_ATOMIC_START and RB_ATOMIC_END macros)

Install

This library is now available in the Arduino Library Manager, directly in the IDE. Go to Sketch > Include Library > Manage Libraries and search for RingBufCPP. Then #include <RingBufCPP.h> in your sketch.

To manually install this library, download this file as a zip, and extract the resulting folder into your Arduino Libraries folder. Installing an Arduino Library.

Examples

Look at the examples folder for several examples.

Contributing

If you find this Arduino library helpful, click the Star button, and you will make my day.

Feel free to improve this library. Fork it, make your changes, then submit a pull request!

API
Constructor
RingBufCPP<typename Type, size_t MaxElements>();

Creates a new RingBuf object that can buffer up to MaxElements of type Type.

Methods
add()
bool add(const Type &obj, bool overwrite=false)

Append an element to the buffer. If there is already MaxElements in the buffer, the oldest element will either be overwritten (when overwrite is true) or this add will have no effect (when overwrite is false). Return false on a full buffer.

peek()
Type *peek(uint16_t num);

Peek at the num’th element in the buffer. Returns a pointer to the location of the num’th element. If num is out of bounds or the num’th element is empty, a NULL pointer is returned. Note that this gives you direct memory access to the location of the num’th element in the buffer, allowing you to directly edit elements in the buffer. Note that while all of RingBuf’s public methods are atomic (including this one), directly using the pointer returned from this method is not safe. If there is a possibility an interrupt could fire and remove/modify the item pointed to by the returned pointer, disable interrupts first with noInterrupts(), do whatever you need to do with the pointer, then you can re-enable interrupts by calling interrupts().

pull()
bool pull(Type *dest);

Pull the first element out of the buffer. The first element is copied into the location pointed to by dest. Returns false if the buffer is empty, otherwise returns true on success.

numElements()
size_t numElements();

Returns number of elements in the buffer.

isFull()
bool isFull();

Returns true if buffer is full, otherwise false.

isEmpty()
bool isEmpty();

Returns true if buffer is empty, false otherwise.

License

This library is open-source, and licensed under the MIT license. Do whatever you like with it, but contributions are appreciated.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

RingTone

This library provides support for parsing and playing tunes in RTTTL format.

RTTTL conversion code based on https://github.com/end2endzone/NonBlockingRTTTL.

The parser is stream-based and allows random seeking where the stream supports it.

An RTTTL writer is also included to assist with editing/creation of RTTTL files.

API Documentation
namespace RingTone

Enums

enum class Note

Note numbers, defined here for convenience.

Values:

enumerator MUTE
enumerator C
enumerator C_Sharp
enumerator D_Flat
enumerator D
enumerator D_Sharp
enumerator E_Flat
enumerator E
enumerator F
enumerator F_Sharp
enumerator G
enumerator G_Sharp
enumerator A_Flat
enumerator A
enumerator A_Sharp
enumerator B_Flat
enumerator B

Functions

template<unsigned octave, unsigned note>
static constexpr unsigned calculateFrequency()
unsigned charToNoteValue(char c)

Get the corresponding note number for a letter.

Note

To sharpen a note, add 1

Parameters:

c

Return values:

unsigned – Notes start at 1, 0 indicates error or pause/mute

unsigned getNoteFrequency(unsigned octave, unsigned note)

Convert a scale/note combination into frequency.

Parameters:
  • octave

  • note

Return values:

unsigned – Frequency, 0 if out of range

unsigned getClosestNote(unsigned frequency, unsigned &octave)

Convert a frequency into a scale/note combination into frequency.

Parameters:
  • frequency

  • octave – Octave for the note

Return values:

unsigned – The note number, 0 if out of range

const char *getNoteName(unsigned noteValue)

Get text for a given note number.

Variables

static unsigned noteFrequencyA4 = 440

Reference note frequency.

static constexpr float frequencyRoot = pow(2, 1.0 / 12)
struct NoteDef
#include <RingTone.h>
class Parser
#include <RingTone.h>

Base parser class.

Subclassed by RingTone::RtttlParser

Public Functions

virtual bool readNextNote(NoteDef &note) = 0

Fetch the next note for this tune.

Return values:

bool – true on success, false if no more notes available

class Player
#include <RingTonePlayer.h>

Base class to support playback of tunes.

Note

This class doesn’t produce any sound. To do this provide set the onPrepareNote and onPlayNote callbacks, or override the prepareNote and playNote methods in an inherited class.

Public Functions

void begin(RingTone::Parser *parser)

Initialise player.

Note

We don’t own the parser, just take a reference

Parameters:

parser – The source of ringtone data

void end()

Stop the player and un-reference parser.

bool start(unsigned delayMs = 0)

Start or continue playing the tune.

void stop()

Stop/pause playing the tune.

inline void resetPlayTime()

Stop playback and reset play time.

inline bool isStarted()

Determine if a tune is being played.

inline unsigned getSpeed() const

Get playback speed factor.

Return values:

unsigned – 100 = normal speed, 50 = half speed, 200 = double speed, etc.

inline unsigned setSpeed(unsigned speed)

Set playback speed factor.

Return values:

unsigned – New speed factor

inline unsigned adjustSpeed(int adjust)

Make a relative adjustment to playback speed.

Return values:

unsigned – New speed factor

struct RtttlHeader
#include <RtttlParser.h>
struct RtttlParserState
#include <RtttlParser.h>

Subclassed by RingTone::RtttlParser

class RtttlParser : public RingTone::Parser, private RingTone::RtttlParserState
#include <RtttlParser.h>

Class to parse RTTTL files RTTTL (RingTone Text Transfer Language) format.

Public Functions

bool begin(IDataSourceStream *source)

Initialise the parser with the given stream.

void end()

Release the source stream.

bool nextTune()

Locate next tune and read header.

bool seekTune(unsigned index)

Find a tune by index, starting at #0.

inline unsigned getIndex()

Get the current tune index.

unsigned getCount()

Get the number of tunes in this file.

inline const String &getTitle()

Get the current tune title.

inline String getCaption()

Get a display caption for the current tune.

inline bool rewind()

Rewind to start of tune.

virtual bool readNextNote(RingTone::NoteDef &note)

Fetch the next note for this tune.

Parameters:

note

Return values:

bool – true on success, false if no more notes available

class RtttlWriter
#include <RtttlWriter.h>
class RtttlJsonListStream : public IDataSourceStream

A forward-only stream for listing contents of a tune file.

Note

Tune files can be large so we only output one tune title at a time

Public Functions

inline RtttlJsonListStream(const String &name, RingTone::RtttlParser *parser)

Construct a list stream.

Parameters:
  • name – Identifies this stream, will have .json appended

  • parser – Pre-initialised parser to obtain tunes from

inline virtual bool isValid() const override

Determine if the stream object contains valid data.

Note

Where inherited classes are initialised by constructor this method indicates whether that was successful or not (e.g. FileStream)

Return values:

bool – true if valid, false if invalid

virtual uint16_t readMemoryBlock(char *data, int bufSize) override

Read a block of memory.

Todo:

Should IDataSourceStream::readMemoryBlock return same data type as its bufSize param?

Parameters:
  • data – Pointer to the data to be read

  • bufSize – Quantity of chars to read

Return values:

uint16_t – Quantity of chars read

virtual bool seek(int len) override

Move read cursor.

Parameters:

len – Relative cursor adjustment

Return values:

bool – True on success.

inline virtual bool isFinished() override

Check if all data has been read.

Return values:

bool – True on success.

virtual String getName() const override

Returns name of the resource.

Note

Commonly used to obtain name of file

Return values:

String

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

SD Card

Low-level support code for accessing SD Cards using FATFS.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

SI7020/SI7021 Environmental Sensors

Arduino library for SI7020 and SI7021 environmental sensors

Examples:

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

SPI Library

Provides support for both hardware and software-base SPI master devices.

Software SPI

Software SPI is likely to be of use only for ESP8266, but is enabled for all architectures.

The ESP8266 can manage a minimum of about 70ns between bit edges, with a maximum of about 1.8 MBit/s at normal CPU clock frequency or 2.5 MHz if CPU clock set to fast.

Build variables
SPISOFT_DELAY_VARIABLE

default: 0 (disabled)

This setting must be enabled in order to use SPISoft::setDelay(). The base clock speed (i.e. delay=0) is reduced to about 1.4 MBit/s (2.1 MBit/s for fast CPU).

The appropriate delay factor can be provided in the SPISoft constructor, or by calling SPISoft::setDelay().

With the ESP8266, then the appropriate delay factor is calculated automatically from the speed passed in SPISettings. This will override any previously set delay factor. Use a speed of 0 to use the manually configured delay value.

SPISOFT_DELAY_FIXED

default: 0 (disabled) maximum: 10

Adds the requested number of ‘NOP’ CPU instructions to every clock transition.

Has less impact than enabling variable delays.

Use variable delays if 10 is insufficient.

Will be ignored if variable delays are enabled.

Performance

The following information has been obtained by measurement. The test application was built using:

make SPISOFT_CALIBRATE=1 SPISOFT_DELAY_VARIABLE=1 CPU_FAST=0

then flashed to an ESP-12F. An oscilliscope was hooked up to the SCK output and the maximum frequency at each delay setting noted. (Not too awkward, just note down the frequency each time the figure changes.)

Entering these into the spisoft.ods spreadsheet allows the values to be charted and co-efficients calculated.

Curiously values from 8 upward form a straight line so we can calculate those. Smaller values are non-linear so a small lookup table is used.

Repeat for CPU_FAST=1.

Note that these figures are concerned with maximum (i.e. burst) frequencies at each delay setting, since that is what devices are sensitive to.

80 MHz CPU
_images/spisoft-80.png

.

Frequency (kHz)

Delay

Variable

Fixed

0

1357

2288

1

1315

1869

2

1215

1824

3

1129

1780

4

1055

1744

5

990

1709

6

932

1639

7

881

1575

8

819

1511

9

743

1462

10

680

1408

11

626

1338

12

578

1294

20

367

30

251

40

192

50

155

60

129

70

112

80

98

90

87

100

79

160 MHz CPU
_images/spisoft-160.png

.

Frequency (kHz)

Delay

Variable

Fixed

0

2133

2857

1

2126

2510

2

2106

2500

3

1958

2439

4

1861

2429

5

1745

2372

6

1668

2353

7

1573

2294

8

1482

2283

9

1357

2242

10

1250

2222

11

1160

2174

12

1081

2165

20

702

30

488

40

374

50

303

60

255

70

220

80

193

90

172

100

156

API Documentation
class SPISoft : public SPIBase
#include <SPISoft.h>

Software-based SPI master.

Intended for ESP8266 due to limited I/O but will work on any architecture.

Constructors

SPISoft()

Default constructor uses same pins as hardware SPI.

inline SPISoft(uint8_t miso, uint8_t mosi, uint8_t sck, uint8_t delay = 0)

Specify pins to use plus optional delay.

Delay is ignored if code is not compiled with SPISOFT_DELAY < 0.

inline SPISoft(const SpiPins &pins, uint8_t delay = 0)

Specify pins plus optional delay.

SPISoft(uint8_t delay)

Use default pins but provide a delay.

Public Functions

virtual bool begin() override

Initialize the SPI bus by setting SCK and MOSI to outputs, pulling SCK and MOSI low.

inline virtual void end() override

Disable the SPI bus (leaving pin modes unchanged).

virtual void endTransaction() override

Stop using the SPI bus. Normally this is called after de-asserting the chip select, to allow other libraries to use the SPI bus.

virtual uint32_t transfer32(uint32_t val, uint8_t bits = 32) override

Send/receive a word of variable size.

Word is transferred either MSB first (bits-1) or LSB first (bit 0) depending on the currently applied bitOrder setting.

Parameters:
  • val – Word to send

  • bits – Size of word

virtual void transfer(uint8_t *buffer, size_t size) override

Send/receive a variable-length block of data.

Parameters:
  • buffer – IN: The data to send; OUT: The received data

  • size – Number of bytes to transfer

inline void setDelay(uint8_t delay)

Set delay factor for the SCK signal. Impacts SPI speed.

Requires code to be compiled with SPISOFT_DELAY < 0.

ESP8266 only: The delay will be automatically calculated for a requested clock speed when begin() or beginTransaction() are called. To use only the manually programmed delay, set the clock speed to zero.

inline virtual bool loopback(bool enable) override

For testing, tie MISO <-> MOSI internally.

Note: Not included in std Arduino lib

inline uint8_t transfer(uint8_t val)

Send/receive one byte of data.

Parameters:

val – The byte to send

Return values:

uint8_t – The received byte

void transfer(uint8_t *buffer, size_t size) = 0

Send/receive a variable-length block of data.

Parameters:
  • buffer – IN: The data to send; OUT: The received data

  • size – Number of bytes to transfer

Build variables
ENABLE_SPI_DEBUG

default: 0 (disabled)

Enable to print additional debug messages.

API Documentation
enum SpiMode

Values:

enumerator SPI_MODE0
enumerator SPI_MODE1
enumerator SPI_MODE2
enumerator SPI_MODE3
const uint32_t SPI_SPEED_DEFAULT = 4000000UL
static constexpr uint8_t SPI_PIN_DEFAULT = {0xff}

SPI driver uses default pin assignment.

struct SpiPins
#include <SPIBase.h>

SPI pin connections.

class SPIBase
#include <SPIBase.h>

Subclassed by SPIClass, SPISoft

Send/receive some data

SPI transfer is based on a simultaneous send and receive: the received data is returned in receivedVal (or receivedVal16). In case of buffer transfers the received data is stored in the buffer in-place (the old data is replaced with the data received).

    receivedVal = SPI.transfer(val)
    receivedVal16 = SPI.transfer16(val16)
    SPI.transfer(buffer, size)

inline uint8_t transfer(uint8_t val)

Send/receive one byte of data.

Parameters:

val – The byte to send

Return values:

uint8_t – The received byte

inline uint16_t transfer16(uint16_t val)

Send/receive one 16-bit word of data.

Word is transferred either MSB first (bit 15) or LSB first (bit 0) depending on the currently applied bitOrder setting.

Parameters:

val – The word to send

Return values:

uint16_t – The received word

inline virtual uint32_t transfer32(uint32_t val, uint8_t bits = 32)

Send/receive a word of variable size.

Word is transferred either MSB first (bits-1) or LSB first (bit 0) depending on the currently applied bitOrder setting.

Parameters:
  • val – Word to send

  • bits – Size of word

virtual void transfer(uint8_t *buffer, size_t size) = 0

Send/receive a variable-length block of data.

Parameters:
  • buffer – IN: The data to send; OUT: The received data

  • size – Number of bytes to transfer

Public Functions

virtual bool begin() = 0

Initialize the SPI bus by setting SCK and MOSI to outputs, pulling SCK and MOSI low.

virtual void end() = 0

Disable the SPI bus (leaving pin modes unchanged).

inline void beginTransaction(SPISettings &settings)

Initialize the SPI bus using the defined SPISettings.

inline virtual void endTransaction()

Stop using the SPI bus. Normally this is called after de-asserting the chip select, to allow other libraries to use the SPI bus.

inline virtual uint8_t read8()

Read one byte from SPI without setting up registers.

Used for performance tuning when doing continuous reads this method does not reset the registers, so make sure that a regular transfer(data) call was performed

Note: this method is not found on the Arduino API

USE WITH CARE !!

Parameters:

none

Return values:

byte – received

virtual bool loopback(bool enable) = 0

For testing, tie MISO <-> MOSI internally.

Note: Not included in std Arduino lib

Public Members

SPISettings SPIDefaultSettings

Default settings used by the SPI bus until reset by beginTransaction(SPISettings)

Note: Not included in std Arduino lib

enum class SpiBus

Identifies bus selection.

Values:

enumerator INVALID
enumerator MIN
enumerator SPI1
enumerator FSPI
enumerator SPI2
enumerator HSPI
enumerator MAX
enumerator DEFAULT
enumerator INVALID
enumerator MIN
enumerator SPI1
enumerator MAX
enumerator DEFAULT
enumerator INVALID
enumerator MIN
enumerator SPI1
enumerator SPI2
enumerator SPI3
enumerator MAX
enumerator DEFAULT
enumerator INVALID
enumerator MIN
enumerator SPI1
enumerator SPI2
enumerator MAX
enumerator DEFAULT
using Callback = bool (*)(Request &request)

SPI completion callback routine.

Param request:

Retval bool:

Return true if request is finished, false to re-queue it immediately

SPIClass SPI

Global instance of SPI class.

struct Data
#include <Data.h>

Specifies a block incoming or outgoing data.

Data can be specified directly within Data, or as a buffer reference.

Command or address are stored in native byte order and rearranged according to the requested byteOrder setting. Data is always sent and received LSB first (as stored in memory) so any re-ordering must be done by the device or application.

Set internal data value of 1-4 bytes

Note

Data is sent LSB, MSB (native byte order)

inline void set8(uint8_t data)

Set to single 8-bit value.

Parameters:

data

inline void set16(uint16_t data)

Set to single 16-bit value.

Parameters:

data

inline void set32(uint32_t data, uint8_t len = 4)

Set to 32-bit data.

Parameters:
  • data

  • len – Length in bytes (1 - 4)

Public Functions

inline void clear()

Reset to zero-length.

inline void set(const void *data, uint16_t count)

Set to reference external data block.

Parameters:
  • data – Location of data

  • count – Number of bytes

Public Members

void *ptr

Pointer to data.

uint16_t length

Number of bytes of data.

uint16_t isPointer

If set, data is referenced indirectly, otherwise it’s stored directly.

class Device
#include <Device.h>

Manages a specific SPI device instance attached to a controller.

Subclassed by Graphics::SpiDisplay, Graphics::XPT2046, HSPI::MemoryDevice

Public Functions

inline bool begin(PinSet pinSet, uint8_t chipSelect, uint32_t clockSpeed)

Register device with controller and prepare for action.

Parameters:
  • pinSet – Use PinSet::normal for Esp32, other values for Esp8266

  • chipSelect – Identifies the CS number for ESP8266, or the GPIO pin for ESP32

  • clockSpeed – Bus speed

inline bool isReady() const

Determine if the device is initialised.

Return values:

bool

virtual IoModes getSupportedIoModes() const = 0

Return set of IO modes supported by a device implementation.

inline bool isSupported(IoMode mode) const

Determine if the device/controller combination supports an IO mode Must be called after begin() as other settings (e.g. pinset) can affect support.

inline void onTransfer(Callback callback)

Set a callback to be invoked before a request is started, and when it has finished.

Parameters:

callback – Invoked in interrupt context, MUST be in IRAM

class MemoryDevice : public HSPI::Device
#include <MemoryDevice.h>

Base class for read/write addressable devices.

Subclassed by HSPI::RAM::IS62_65, HSPI::RAM::PSRAM64

Prepare a write request

virtual void prepareWrite(HSPI::Request &req, uint32_t address) = 0

Prepare request without data.

inline void prepareWrite(HSPI::Request &req, uint32_t address, const void *data, size_t len)

Prepare request with data.

Parameters:
  • dataData to send

  • len – Size of data in bytes

Prepare a read request

virtual void prepareRead(HSPI::Request &req, uint32_t address) = 0

Prepare without buffer.

inline void prepareRead(HSPI::Request &req, uint32_t address, void *buffer, size_t len)

Prepare with buffer.

Parameters:
  • buffer – Where to write incoming data

  • len – Size of buffer

Public Functions

inline void write(uint32_t address, const void *data, size_t len)

Write a block of data.

Note

Limited by current operating mode

Parameters:
  • address

  • data

  • len

inline void read(uint32_t address, void *buffer, size_t len)

Read a block of data.

Note

Limited by current operating mode

Parameters:
  • address

  • data

  • len

class IS62_65 : public HSPI::MemoryDevice
#include <IS62-65.h>

IS62/65WVS2568GALL fast serial RAM.

Public Types

enum class OpMode

Memory operating mode determines how read/write operations are performed.

Values:

enumerator Byte

Limited to one byte.

enumerator Page

Limited to single 32-bit page.

enumerator Sequential

Access entire memory array (DEFAULT)

Public Functions

inline virtual IoModes getSupportedIoModes() const override

Return set of IO modes supported by a device implementation.

inline bool begin(PinSet pinSet, uint8_t chipSelect, uint32_t clockSpeed)

Configure the RAM into a known operating mode.

inline OpMode getOpMode() const

Get current operating mode (cached value)

Return values:

OpMode – No device access is performed.

inline OpMode readOpMode()

Read current operating mode from device.

Return values:

OpMode

inline virtual void prepareWrite(HSPI::Request &req, uint32_t address) override

Prepare request without data.

inline virtual void prepareRead(HSPI::Request &req, uint32_t address) override

Prepare without buffer.

class PSRAM64 : public HSPI::MemoryDevice
#include <PSRAM64.h>

PSRAM64(H) pseudo-SRAM.

Public Functions

inline virtual IoModes getSupportedIoModes() const override

Return set of IO modes supported by a device implementation.

inline bool begin(PinSet pinSet, uint8_t chipSelect, uint32_t clockSpeed)

Configure the RAM into a known operating mode.

inline virtual void prepareWrite(HSPI::Request &req, uint32_t address) override

Prepare request without data.

inline virtual void prepareRead(HSPI::Request &req, uint32_t address) override

Prepare without buffer.

struct Request
#include <Request.h>

Defines an SPI Request Packet.

Request fields may be accessed directly or by use of helper methods.

Application is responsible for managing Request object construction/destruction. Queuing is managed as a linked list so the objects aren’t copied.

Applications will typically only require a couple of Request objects, so one can be prepared whilst the other is in flight. This helps to minimises the setup latency between SPI transactions.

Set value for command phase

inline void setCommand(uint16_t command, uint8_t bitCount)
Parameters:
  • command

  • bitCount – Length of command in bits

inline void setCommand8(uint8_t command)

Set 8-bit command.

Parameters:

command

inline void setCommand16(uint16_t command)

Set 16-bit command.

Parameters:

command

Set value for address phase

inline void setAddress(uint32_t address, uint8_t bitCount)
Parameters:
  • address

  • bitCount – Length of address in bits

inline void setAddress24(uint32_t address)

Set 24-bit address.

Parameters:

address

Public Functions

inline void setAsync(Callback callback = nullptr, void *param = nullptr)

Set request to asynchronous execution with optional callback.

Public Members

Device *device = {nullptr}

Target device for this request.

Request *next = {nullptr}

Controller uses this to queue requests.

uint16_t cmd = {0}

Command value.

uint8_t cmdLen = {0}

Command bits, 0 - 16.

uint8_t async

Set for asynchronous operation.

uint8_t task

Controller will execute this request in task mode.

uint8_t busy

Request in progress.

uint32_t addr = {0}

Address value.

uint8_t addrLen = {0}

Address bits, 0 - 32.

uint8_t dummyLen = {0}

Dummy read bits between address and read data, 0 - 255.

uint8_t sizeAlign = {0}

Required size alignment of each transaction (if split up)

Data out

Outgoing data.

Data in

Incoming data.

Callback callback = {nullptr}

Completion routine.

void *param = {nullptr}

User parameter.

class StreamAdapter
#include <StreamAdapter.h>

Helper class for streaming data to/from SPI devices.

class SPIClass : public SPIBase
#include <SPI.h>

Hardware SPI class.

Public Types

using IoCallback = void (*)(uint16_t c, uint8_t bits, bool read)

Used for testing purposes only.

Param c:

Value being read/written

Param bits:

Size of value in bits

Param read:

true for incoming value, false for outgoing

Public Functions

bool setup(SpiBus id, SpiPins pins = {})

Alternative to defining bus and pin set in constructor. Use this method to change global SPI instance setup.

IMPORTANT: Must be called before begin().

virtual bool begin() override

Initialize the SPI bus by setting SCK and MOSI to outputs, pulling SCK and MOSI low.

virtual void end() override

Disable the SPI bus (leaving pin modes unchanged).

virtual uint8_t read8() override

Read one byte from SPI without setting up registers.

Used for performance tuning when doing continuous reads this method does not reset the registers, so make sure that a regular transfer(data) call was performed

Note: this method is not found on the Arduino API

USE WITH CARE !!

Parameters:

none

Return values:

byte – received

virtual uint32_t transfer32(uint32_t val, uint8_t bits = 32) override

Send/receive a word of variable size.

Word is transferred either MSB first (bits-1) or LSB first (bit 0) depending on the currently applied bitOrder setting.

Parameters:
  • val – Word to send

  • bits – Size of word

virtual void transfer(uint8_t *buffer, size_t numberBytes) override

Send/receive a variable-length block of data.

Parameters:
  • buffer – IN: The data to send; OUT: The received data

  • size – Number of bytes to transfer

virtual bool loopback(bool enable) override

For testing, tie MISO <-> MOSI internally.

Note: Not included in std Arduino lib

void setDebugIoCallback(IoCallback callback)

Used for testing purposes only Must be called after begin().

Used to verify serialisation/de-searialisation bit ordering

inline uint8_t transfer(uint8_t val)

Send/receive one byte of data.

Parameters:

val – The byte to send

Return values:

uint8_t – The received byte

void transfer(uint8_t *buffer, size_t size) = 0

Send/receive a variable-length block of data.

Parameters:
  • buffer – IN: The data to send; OUT: The received data

  • size – Number of bytes to transfer

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

SSDP

https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol

Provides an SSDP server implementation aiming to be fully standards compliant.

Configuration Variables
SSDP_DEBUG_VERBOSE
  • 0 (default)

  • 1: Output details of all requests. Warning: This can produce a lot of output!

UPNP_VERSION

UPnP standard to follow.

  • 1.0: (default)

  • 1.1: Incomplete

  • 2.0: Incomplete

Versions 1.1 and 2.0 require BOOTID.UPNP.ORG and CONFIGID.UPNP.ORG fields which are not currently implemented.

Key points from UPnP 2.0 specification

I’m choosing to avoid the issue of ‘multi-homed’ devices, where both IPv4 and IPv6 are being used, and probably also WiFi+Ethernet combinations. Basically whenever multiple IP stacks are involved.

Search request

M-SEARCH: “Please tell me about yourselves, but don’t all shout at once.”

ssdp:all
   Search for all devices and services.

upnp:rootdevice
   Search for root devices only.

uuid:device-UUID
   Search for a particular device.

urn:<domain>:device:deviceType:ver
   Search for any device of this type.

urn:<domain>:service:serviceType:ver
   Search for any service of this type.

Period characters in <domain> are always substituted with hyphens (RFC 2141).

Not clear on how to handle version numbers at present. The specs. say only minor versions are backward compatible, which why perhaps we only see major numbers in interface definitions. e.g. Basic:1 not Basic:1.0.

Search response
Any device responding to a unicast M-SEARCH should respond within 1 second.

In response to an M-SEARCH request, if ST header in request was:

ssdp:all
   Respond 3+2d+k times for a root device with d embedded devices and s embedded services
   but only k distinct service types.
   Value for ST header must be the same as for the NT header in NOTIFY messages with ssdp:alive.

upnp:rootdevice
   Respond once for root device.

uuid:device-UUID
   Respond once for each matching device, root or embedded.

urn:<domain>:device:deviceType:v
   Respond once for each matching device, root or embedded.
   Should specify the version of the device type contained in the M-SEARCH request.

urn:<domain>:service:serviceType:v
   Respond once for each matching service type.
   Should specify the version of the service type contained in the M-SEARCH request.
Descriptions

The LOCATION field is for the device description or enclosing device in the case of a service.

This implies that we never respond with a service description, which makes sense:

  • The device description provides key information about its services

  • The service description contains action lists or state variable tables

Only the device description is required to learn about services, whilst the service description is only required if the Control Point needs to interact with that service.

Points arising

So we need a filter which then gets passed through the device stack. Each response must be sent on a schedule, not all together, so we’ll need to set up a timer. We’ll also need to track state something like the DescriptionStream. Actually, what we can do is create an enumerator which iterates through the entire device stack. That will take out the complexity from here and DescriptionStream. We’ll need an additional Item tag so we can differentiate. This can either be a virtual method or we could use a union with all the different Item types plus a separate tag field. That could also contain the search filter information as input.

Move all this stuff into an SsdpResponder class?

API Documentation
namespace SSDP

Typedefs

using MessageDelegate = Delegate<void(MessageSpec *ms)>

A callback function must be provided to do the actual sending.

Param ms:

Message spec. to action, must delete when finished with it

using ReceiveDelegate = Delegate<void(BasicMessage &message)>

Callback type for handling an incoming message.

using SendDelegate = Delegate<void(Message &msg, MessageSpec &ms)>

Callback type for sending outgoing message.

Note

The message spec. is provided by the UPnP Device Host, which then gets called back to construct the message content. It then calls sendMessage().

Param msg:

Message with standard fields completed

Param ms:

Parameters for constructing message

Enums

enum class MessageType

Values:

enumerator XX
enum class NotifySubtype

SSDP Notification subtype.

Values:

enumerator XX
enumerator OTHER
enum class SearchTarget

SSDP Search target types.

Values:

enumerator root

Root devices only: upnp:rootdevice

enumerator type

or urn:{domain}:service:{serviceType}:{v}

Search for device/service type: urn:{domain}:device:{deviceType}:{v}

enumerator uuid

Search for specific device: uuid:{device-UUID}

enumerator all

All devices and services: ssdp::all

enum class SearchMatch

Determines the kind of match obtained when scanning incoming packets.

Values:

enumerator root

Matched root device.

enumerator uuid

Matched with device UUID.

enumerator type

Matched device or service type.

Functions

static const IpAddress multicastIp (239, 255, 255, 250)
DECLARE_FSTR(SSDP_DISCOVER)
DECLARE_FSTR(UPNP_ROOTDEVICE)
DECLARE_FSTR(SSDP_ALL)
NotifySubtype getNotifySubtype(const char *subtype)
DECLARE_FSTR(defaultProductNameAndVersion)
String getServerId(const String &productNameAndVersion)

Variables

static constexpr uint16_t multicastPort = 1900
Server server
template<class HeaderClass>
class BaseMessage : public HeaderClass
#include <Message.h>

class template for messages

class BasicMessage : public SSDP::BaseMessage<BasicHttpHeaders>
#include <Message.h>

Handles incoming messages.

Note

Contains name/value pairs as pointers.

class Message : public SSDP::BaseMessage<HttpHeaders>
#include <Message.h>

Message using regular HTTP header management class.

Note

More flexible than BasicMessage but requires additional memory allocations

class MessageQueue
#include <MessageQueue.h>

Queue of objects managed by a single timer.

Public Functions

inline void setCallback(MessageDelegate delegate)

Set a callback to handle sending a message @Param delegate.

void add(MessageSpec *ms, uint32_t intervalMs)

Schedule a message to start after the given interval has elapsed.

The UPnP spec. requires that messages are sent after random delays, hence the interval. MessagesSpec objects must be created using the new allocator and are deleted after sending.

Parameters:
  • ms – The template spec. for constructing the message(s)

  • intervalMs – How long to wait before sending

bool contains(const MessageSpec &ms) const

Determine if a matching message specification is already queued.

See MessageSpec operator== definition for how comparison is performed.

Parameters:

ms

Return values:

bool – true if the given spec. is already queued.

unsigned remove(void *object)

Remove any messages for this object.

Return values:

unsigned – Number of messages removed

class MessageSpec
#include <MessageSpec.h>

Defines the information used to create an outgoing message.

The message queue stores these objects as a linked list.

Public Functions

inline MessageSpec(const MessageSpec &ms, SearchMatch match, void *object)

Construct a new message spec for a specific match type.

Parameters:
  • ms – Template message spec

  • match – The match type

  • object – Target for message

inline IpAddress remoteIp() const

Get the remote IP address.

inline uint16_t remotePort() const

Get the remote port number.

template<class Object>
inline Object *object() const

Get the target object pointer.

This is templated to provide cleaner code. Example:

MyObject* object = ms.object<MyObject>();

inline MessageType type() const

Get the message type.

inline NotifySubtype notifySubtype() const

Get the notification sub-type.

inline SearchMatch match() const

Get the search match type.

inline SearchTarget target() const

Get the search target.

inline void setTarget(SearchTarget target)

Set the search target.

inline void setRemote(IpAddress address, uint16_t port)

Set the remote address and port.

inline void setRepeat(uint8_t count)

Set number of times to repeat message.

inline uint8_t repeat() const

Get current repeat value.

inline bool shouldRepeat()

Check if message should be repeated and adjust counter.

class Server : private UdpConnection
#include <Server.h>

Listens for incoming messages and manages queue of outgoing messages.

Todo:

Randomise the time as required by MX and keep queue ordered by time. Each message is 12 bytes, adding time would make this 16. Need to handle alives < 1/2 expiry time as well so timer will always be active. Could also use a linked list so an additional pointer would make it 20 bytes.

Note: This is basically another timer queue, so we could use software timers directly but potentially there could be a lot of them. Better I think to use a single Timer and drive it from that.

Note

The spec. talks about random intervals, etc. but to keep things simple we just use a timer to spread all these messages out at regular intervals.

Public Functions

bool begin(ReceiveDelegate receiveCallback, SendDelegate sendCallback)

Called from UPnP library to start SSDP server.

Note

May only be called once

Return values:

bool – true on success

void end()

Stop SSDP server.

inline bool isActive()

Determine if server is running.

Return values:

bool

bool sendMessage(const Message &msg)

Send a message immediately.

bool buildMessage(Message &msg, MessageSpec &ms)

Construct a message from the given template spec.

Parameters:
  • msg – Fields of this message will be filled out

  • ms – Spec to use for constructing message

Return values:

bool – Returns false if validation failed: message should not be sent

inline void setProduct(const String &name, const String &version)

Set product name and version contained in SSDP message USER-AGENT field.

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

SD Storage

Provides Storage Management support for SD cards.

This code is ported from the SD Card library.

Currently uses only SPI communication, which offers only a limited interface and is not supported by SDUC cards.

SD Card connections
_images/mmc.jpg

See https://en.wikipedia.org/wiki/SD_card

MMC pin

SD pin

miniSD pin

microSD pin

Name

I/O

Logic

Description

1

1

1

2

nCS

I

PP

SPI Card Select [CS] (Negative logic)

2

2

2

3

DI

I

PP

SPI Serial Data In [MOSI]

3

3

3

VSS

S

S

Ground

4

4

4

4

VDD

S

S

Power

5

5

5

5

CLK

I

PP

SPI Serial Clock [SCLK]

6

6

6

6

VSS

S

S

Ground

7

7

7

7

DO

O

PP

SPI Serial Data Out [MISO]

8

8

8

NC nIRQ

O

OD

Unused (memory cards) Interrupt (SDIO cards) (negative logic)

9

9

1

NC

Unused

10

NC

Reserved

11

NC

Reserved

Usage
  • Code should be built with ENABLE_STORAGE_SIZE64 =1 unless using a very small card (1 or 2GB):

  • Add required libraries to your project’s component.mk file:

    COMPONENT_DEPENDS := SdStorage FatIFS
    
  • Create and initialise a Card Device instance:

    #include <Storage/SD/Card.h>
    
    // GPIO used for Chip Select (Esp8266)
    #define PIN_CARD_CS 5
    
    // Create the SD card Device and register with the Storage library
    auto card = new Storage::SD::Card("card1", SPI);
    Storage::registerDevice(card);
    
    if(!card->begin(PIN_CARD_CS)) {
        // Handle error here
        return;
    }
    
    // Display some information
    Serial << "CSD" << endl << card->csd << endl;
    Serial << "CID" << endl << card->cid;
    

At this point, the card can be accessed directly using Storage::Device methods. If the card has been formatted then the partitions can be accessed using the standard Storage API. For example:

Storage::Partition part;

// Find the first FAT partition on the card
part = *card->partitions().find(Storage::Partition::SubType::Data::fat);

// Get the first partition (of any type)
part = *card->partitions().begin();

// Iterate through all partitions
for(part: card->partitions()) {
    Serial << part << endl;
}
  • To create a partition table on the card:

    #include <Storage/Disk/GPT.h>
    
    Storage::Disk::GPT::PartitionTable table;
    // Create one partition using 100% of all available space
    table.add("My FAT partition", 0, 100);
    auto err = Storage::Disk::formatDisk(*card, table);
    Serial << "formatDisk: " << err << endl;
    if(!!err) {
        // If formatting fails, the disk will left in an indeterminate state!
        ...
        return;
    }
    
    // Fetch the first (and only) partition
    auto part = card->partitions().begin();
    

Note

To use the entire card as a single partition, it is not necessary to pre-format the disk. Instead, construct a partition spanning the entire disk:

auto part = card->editablePartitions().add("My Partition",
  Storage::Partition::SubType::Data::fat, 0, card->getSize());

This approach is sometimes referred to as an SFD (Single Filing-system Device).

The partition can now be formatted with the required filing system (FAT):

int res = IFS::FAT::formatVolume(part);
Serial << "formatVolume : " << IFS::Error::toString(res) << endl;

Once formatted, the partition can be mounted as usual:

auto fatfs = IFS::createFatFilesystem(part);
if(fatfs != nullptr) {
    if(fatfs->mount() == FS_OK) {
        // OK, access the filing system
        Serial << fatfs->getContent("My file.txt") << endl;
    } else {
        // Mount failed, destroy the objects
        delete fs;
        delete card; // Also de-registers the card with the Storage API
    }
}
API Documentation
namespace SD
class Card : public Storage::Disk::BlockDevice
#include <Card.h>

Public Functions

bool begin(uint8_t chipSelect, uint32_t freq = 0)

Initialise the card.

Parameters:
  • chipSelect

  • freq – SPI frequency in Hz, use 0 for maximum supported frequency

inline virtual String getName() const override

Obtain unique device name.

inline virtual uint32_t getId() const

Obtain device ID.

Return values:

uint32_t – typically flash chip ID

inline virtual Type getType() const

Obtain device type.

inline virtual size_t getBlockSize() const override

Obtain smallest allocation unit for erase operations.

struct CID
#include <CID.h>

Public Members

uint8_t mid

Manufacturer ID.

char oid[2]

OEM / Application ID.

char pnm[5]

Product name.

uint8_t prv

Product revision.

uint32_t psn

Product serial number.

uint16_t mdt

Manufacturing date.

uint8_t not_used

Always 1.

uint8_t crc

7-bit checksum

struct CSD
#include <CSD.h>

Subclassed by Storage::SD::CSD1, Storage::SD::CSD2, Storage::SD::CSD3

struct CSD1 : public Storage::SD::CSD
#include <CSD.h>
struct CSD2 : public Storage::SD::CSD
#include <CSD.h>
struct CSD3 : public Storage::SD::CSD
#include <CSD.h>
References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

_images/servo.jpg

Source: SERV-03-MI (Micro Servo)

Servo RC PWM Control

Library to control RC servos with PWM signals.

Change History

This library was revised for Sming version 4.0. These are the main changes:

  • Interrupts remain enabled during updates

  • ServoChannel value now specifies actual pulse duration in microseconds, such that minValue <= value <= maxValue. Previously it was 0 <= value <= (maxValue - minValue).

  • Max channels increased to 5.

See Pull Request #1870 for further details.

Brief introduction

There are generally two types of servo actuator (digital and analogue) and this library supports only the analogue variety.

Servo actuators have a logic-level control signal to set the position using an active-high pulse of around 1 - 2 ms. This pulse must be repeated every 20ms or so to ensure the position is maintained

Servos are generally insensitive to the exact period provided it’s no more than about 25ms. It’s the duration of the pulse which is critical.

For most servos 1.5ms is the default/centre position, however the min/max values will vary between models depending on the exact type and range of motion. These values are therefore configurable for each channel.

Physical connection

Servos typically use a 5V logic-level input but are usually fine with the 3.3v output from the ESP8266.

Warning

Like relays a servo is an electro-mechanical device, but it also has integrated control circuitry so doesn’t require flyback diodes, etc. to protect from current spikes.

However, remember to always insert a protection resistor of at least 200 ohms between the GPIO and the servo. This limits the current to <10mA if 5V is present on the line. For 12V servos a 1K resistor will perform the same function.

Technical Explanation

Each servo actuator is connected to a GPIO pin, which is toggled by an ISR driven from the Hardware Timer. The ServoChannel class represents this connection, and defines the current value (in microseconds) plus the range (minimum and maximum values) to which the value is constrained.

The hardware timer interrupt is managed by a single instance of the Servo class. All channels are updated sequentially at the start of each frame period (20ms in duration):

_images/wavedrom-c73563ab-9f96-4efc-af18-1d33a3ac57fd.svg

The first channel is set ON, then after the required time it is set OFF and the next channel set ON. The final interrupt turns the active channel OFF. This requires (NumChannels + 1) interrupts per frame, and the process repeats continuously whilst there is at least one active channel.

Channel updates (via calls to ServoChannel::setValue()) are not handled immediately, but deferred using a 10ms software timer (half the frame period). This allows more efficient updating and ensures the positions of all affected servos are changed at the same time.

A double-buffering technique is used for updates to avoid disabling interrupts, which allows use of the non-maskable timer interrupts for best timing accuracy and eliminates glitches in the output.

Updates involve re-calculating the list of active pins and timer intervals, which is stored into a second frame buffer. This is made active by the ISR when the current frame has completed.

If the ISR hasn’t yet processed a previous update, it will be retried after a further 10ms.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Signal Generator

This Component provides the SignalGenerator class which can be used to synthesise a wide range of analogue signals.

Ported from https://www.codeproject.com/Articles/30180/Simple-Signal-Generator

Simple Signal Generator

29 Oct 2008 CPOL. Tefik Becirovic

Useful software equivalent for a real, simple signal generator device

Introduction

The Simple Signal Generator is a C# class designed to generate four simple periodic waveforms including sine, square, triangle, and sawtooth. The class is provided for testing software and hardware components during the development of measurement applications. A generated signal varies across time domains, and by default, is normalized by the amplitude A € [-1,1] and period T € [0,1]. It is possible to set an arbitrary amplitude, frequency, DC-offset, and phase shift, and to invert the signal.

Background Display

There are a couple of articles on The Code Project that describe in detail about developing different user defined components for dynamically changing single values or signal shapes such as bars, graphs, charts, gauges, and a diversity of other instruments.

For testing those components, functions like y=F(x) are very often used. These functions are typically periodical, mostly sine, and their variable x will be modified in a loop with constant steps across an interval [x0,xn].

Hardware developers use a totally different technique. For analyzing and troubleshooting electronic systems, they use an additional external device called signal or function or waveform generator. The signal generator is an important piece of electronic test equipment, and should not be missed in any electronics laboratory.

On the device, we can select a built-in periodical function like y=F(t); the variable t represents real time, and we can change some parameters like frequency, amplitude, and the DC-offset. Typically, there are four basic signal types.

After customization, the desired output signal can be wired on to the input of the component that should be tested, and we monitor their response function in the time domain using an oscilloscope.

SignalGeneratorDevice

Displayed here is a typical low-cost signal generator that has on its front panel, a radio-button with four positions to choose signal types and three potentiometers to adjust frequency, amplitude, and DC-offset. It is no big problem for hardware dudes to make something such as this, especially with tips from the book [1].

Of course, there are much better and more expensive devices on the market with more functions, more parameters to choose, better range and a finer scale for these parameters, integrated display to show selected settings, user-defined functions, on-board memory, better stability, and two or more independent channels.

Note that, there are two very significant differences between the hardware and the software approach. Hardware developers use an external device, and a test signal is in the time domain, which implies that the test signal varies totally independent from the assembly that will be tested.`

The class presented offers a software equivalent for the above described real, simple signal generator device.

Using the Code

For using the class, we have a public function in the form y=F(t). The parameter t is the real time here:

float GetValue();

After a lot of consideration, I added another overload of the function in the form y=F(x). The Parameter x is explicitly given as a time here:

float GetValue(float time);

This should be used publicly only for DEBUG purposes, eventually during tests by adding new signal types!

All signals have a normalized period T € [0,1] in cycles, and not the usual T € [0,2Pi] in radian. In this case, a value 1 corresponds to a full cycle. The basis for this is an easier deal with normalized values, for example, for scaling operations by fitting in a window.

The signal generator is given as a class, but it is quite easy to transform it to a non-visual, or yet to a visual component, or maybe to a form like a front panel.

For generating all the given signals, very simple functions are used, but they are not the simplest. Some functions can be made simpler, but a development goal of this project was to generate signals exactly like the following example on Wikipedia:

_images/waveforms.svg

Sine, square, triangle, and sawtooth waveforms By Omegatron - Own work, CC BY-SA 3.0

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

SmingTest

An extensible test framework for Sming, with integrated profiling and scheduling support.

Getting started

A Sample test skeleton project is provided which you can copy to provide a starting point.

See the Sming /tests/HostTests test application for a more comprehensive example.

  • A test module contains all code related to testing a specific Component, Library or framework module

  • If a module contains more than one file then place all files in a sub-directory

  • Each module must have a registration function:

  • Each module contains one or more test groups:

    • Each group is a class inheriting from TestGroup

    • Add a call to registerGroup() to the registration function for each test class

  • Keep each group brief. Use multiple, simpler groups if necessary.

  • The group TestGroup::execute() method is called to run the tests. On return the group is considered to have been successfully completed and destroyed.

  • Call TEST_ASSERT at appropriate points; passing false will fail the test and abort the process, displaying the source location of the failure.

  • If a test fails then additional details may be shown before calling TEST_ASSERT(false).

  • For asynchronous testing calling TestGroup::pending() before returning. When the tests have been completed call TestGroup::complete(). (See /tests/HostTests/modules/Timers.cpp for an example.)

The following macros are added for ease of importing tests from other frameworks:

What happens

The registerGroup() function creates a factory function which is added to the SmingTest::Runner::groupFactories list.

The test runner creates, executes and destroys each group in turn, and deals with scheduling.

Notes

Tests are run with DEBUG_VERBOSE_LEVEL at WARNING level, so debug_i statements will not normally be shown. Tests can use other debug_X functions as required, or Serial print methods.

Tests should compile and run for all architectures.

API Documentation

Defines

REGISTER_TEST(name)

Provides consistent global name for test factory function.

Test modules should use this macro to implement factory function:

#include <SmingTest.h>

class SampleTest: public TestGroup {
   ...
};

void REGISTER_TEST(sample)
{
   registerGroup<SampleTest>();
}
Parameters:
  • name – Name of test

Functions

template<class GroupClass>
void registerGroup()

Register a factory function (a lambda) to create a given TestGroup class.

Template Parameters:

Class – to be registered

namespace SmingTest

Typedefs

typedef TestGroup *(*Factory)()

Factory function to create a TestGroup class.

using Callback = Delegate<void()>

Variables

Runner runner
class Runner
#include <SmingTest.h>

Public Functions

Runner()
inline void setGroupIntervalMs(unsigned ms)
inline void addFactory(Factory factory)
void execute(Callback onComplete, unsigned initialDelayMs = 0)
void groupComplete(TestGroup *group)

Protected Functions

void runNextGroup()

Private Types

enum class State

Values:

enumerator stopped
enumerator waiting

In between tests.

enumerator running

Private Members

Vector<Factory> groupFactories

List of registered class factories.

Timer taskTimer
unsigned taskIndex = {0}
State state = {State::stopped}
NanoTime::Time<uint32_t> totalTestTime
unsigned testCount = {0}
unsigned failureCount = {0}
unsigned groupIntervalMs = {500}
Callback onComplete

Internal check macros

INTERNAL_CHECK(expr, verbose)
INTERNAL_CHECK2(res, expr, verbose)
INTERNAL_CHECK_EQ(a, b, verbose)
INTERNAL_CHECK_NEQ(a, b, verbose)

Check an expression, print message for success or failure (verbose)

CHECK(expr)

Check expression evaluates to true.

Parameters:
  • expr

CHECK2(res, expr)

Provide separate test result and expression.

Parameters:
  • res – Result of test

  • expr – Expression to display

CHECK_EQ(a, b)

Check two values are the same.

Parameters:
  • a

  • b

CHECK_NEQ(a, b)

Check two values are not the same.

Parameters:
  • a

  • b

Check an expression, but only print message on failure

REQUIRE(expr)

Check expression evaluates to true.

Parameters:
  • expr

REQUIRE2(res, expr)

Provide separate test result and expression.

Parameters:
  • res – Result of test

  • expr – Expression to display

REQUIRE_EQ(a, b)

Check two values are the same.

Parameters:
  • a

  • b

REQUIRE_NEQ(a, b)

Check two values are not the same.

Parameters:
  • a

  • b

Defines

TEST_ASSERT(result)

Check a test result.

Note

Failure generates an assertion so when run in the host emulator the process fails.

Parameters:
  • result – true if test was successful, false on failure

class TestBase
#include <TestBase.h>

Base class supporting verification for test assertions.

Subclassed by TestGroup

Public Functions

inline virtual ~TestBase()
virtual bool testVerify(bool res, const TestParam &param)

Print result of a test.

Parameters:
  • res – Result of the operation

  • param – Details of the test for display

Return values:

bool – Same as res

inline bool test_verify(bool res, const char *expr, const String &value1, const String &value2, bool verbose)
template<typename V>
inline std::enable_if<std::is_arithmetic<V>::value, bool>::type test_verify(bool res, const char *expr, const V &value1, const V &value2, bool verbose)
template<typename V>
inline std::enable_if<!std::is_same<V, String>::value && !std::is_arithmetic<V>::value, bool>::type test_verify(bool res, const char *expr, const V &value1, const V &value2, bool verbose)
inline virtual void fail(const char *func)
struct TestParam
#include <TestBase.h>

Contains details for test verification.

Public Members

const char *expr

Text of expression generated by the macro.

String value1

First value in comparison, or expected result.

String value2

Second value in comparison (optional)

bool verbose

true to always emit message, false only for errors

Defines

startTest(s)
TEST_CASE_1_ARG(name)

Start a test item.

Use like this:

TEST_CASE("My Test", "description") {
      ...
}

Note: Description is optional.

TEST_CASE_2_ARGS(name, desc)
GET_3RD_ARG(arg1, arg2, arg3, ...)
TEST_CASE_ARG_CHOOSER(...)
TEST_CASE(...)
class TestGroup : public TestBase
#include <TestGroup.h>

Class to simplify generation of begin/end messages for a test group.

Public Types

enum class State

Values:

enumerator running
enumerator pending
enumerator complete
enumerator failed

Public Functions

inline TestGroup(const String &name)
void commenceTest()
virtual void execute() = 0

Implement this method to define the test.

Note

If tests are asynchronous, call pending() before returning and call complete() when the group has completed execution (e.g. via timer callback, etc.)

void startItem(const String &tag, const String &description = nullptr)

Note the start of a test item within a group.

virtual void fail(const char *func) override

Called when test fails to identify location.

inline const String &getName()
inline NanoTime::Time<uint32_t> elapsedTime() const
inline State getState() const
void initialiseAndExecute()

Called by test runner.

Protected Functions

inline void pending()

Call to mark test as pending so it will be executed asynchronously Call complete() when test is finished.

void complete()

Call to complete pending (asynchronous) test.

Private Members

String name
State state = {State::running}
OneShotFastUs groupTimer
jmp_buf exception
References
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Solar Calculator

Arduino library to support calculation of sunrise / sunset times

See SystemClock NTP for example usage.

This is straight port of the code used by https://www.esrl.noaa.gov/gmd/grad/solcalc/

Javascript reference: https://www.esrl.noaa.gov/gmd/grad/solcalc/main.js

API Documentation
class SolarCalculator

Calculation of apparent time of sunrise and sunset.

Note: Months are 1-based

Unnamed Group

int sunRiseSet(bool isRise, int y, int m, int d)

@briefCalculate a sunrise or sunset figure for a given day.

Parameters:
  • isRise – true for sunrise, false for sunset

  • y – Absolute year

  • m – Month number (1 - 12)

  • d – Day of month (1 - 31)

Return values:

int – Minutes since midnight, -1 if there is no sunrise/sunset

Public Functions

inline SolarCalculator()

Default constructor, uses Royal Observatory, Greenwich as default.

inline SolarCalculator(const SolarRef &ref)

Perform calculations using the given solar reference.

inline const SolarRef &getRef() const

Get the current location reference in use.

inline void setRef(const SolarRef &ref)

Set the location reference for calculations.

struct SolarRef

A location is required to compute solar times.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

SparkFun APDS9960 RGB and Gesture Sensor Arduino Library

Avago APDS-9960 Breakout Board - SEN-12787

*Avago APDS-9960 Breakout Board (SEN-12787)*

Getting Started
  • Download the Git repository as a ZIP (“Download ZIP” button)

  • Unzip

  • Copy the entire library directory (APDS-9960_RGB_and_Gesture_Sensor_Arduino_Library ) to <Arduino installation directory&gt;/libraries

  • Open the Arduino program

  • Select File -> Examples -> SparkFun_APDS9960 -> GestureTest

  • Plug in your Arduino and APDS-9960 with the following connections

-OR-

  • Use the library manager

Arduino Pin

APDS-9960 Board

Function

3.3V

VCC

Power

GND

GND

Ground

A4

SDA

I2C Data

A5

SCL

I2C Clock

2

INT

Interrupt

  • Go to Tools -> Board and select your Arduino board

  • Go to Tools -> Serial Port and select the COM port of your Arduino board

  • Click “Upload”

  • Go to Tools -> Serial Monitor

  • Ensure the baud rate is set at 9600 baud

  • Swipe your hand over the sensor in various directions!

Repository Contents
  • /examples - Example sketches for the library (.ino). Run these from the Arduino IDE.

  • /src - Source files for the library (.cpp, .h).

  • library.properties - General library properties for the Arduino package manager.

Documentation
Products that use this Library
Version History
  • V_1.4.1 - Removing blank files, updating library.properties file.

  • V_1.4.0 - Updated to new library structure

  • V_1.3.0 - Implemented disableProximitySensor(). Thanks to jmg5150 for catching that!

  • V_1.2.0 - Added pinMode line to GestureTest demo to fix interrupt bug with some Arduinos

  • V_1.1.0 - Updated GestureTest demo to not freeze with fast swipes

  • V_1.0.0: Initial release

  • Ambient and RGB light sensing implemented

  • Ambient light interrupts working

  • Proximity sensing implemented

  • Proximity interrupts working

  • Gesture (UP, DOWN, LEFT, RIGHT, NEAR, FAR) sensing implemented

License Information

This product is *open source_!

The code is beerware; if you see me (or any other SparkFun employee) at the local, and you’ve found our code helpful, please buy us a round!

Please use, reuse, and modify these files as you see fit. Please maintain attribution to SparkFun Electronics and release anything derivative under the same license.

Distributed as-is; no warranty is given.

  • Your friends at SparkFun.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

SPIFFS IFS Library

This Component provides SPIFFS filesystem support for all architectures.

A single SPIFFS partition is defined using HWCONFIG =spiffs, which supports these build variables:

DISABLE_SPIFFS

[deprecated and removed]

This value is no longer supported. Please remove it from your project’s component.mk file.

SPIFF_SIZE

[deprecated and removed]

Size (in bytes) of the SPIFFS area in Flash memory. To change this, edit the Hardware configuration.

SPIFF_FILES

default: files

The SPIFFS image is built using files from this directory, which must exist or the build will fail.

If you set this to an empty value, then an empty filesystem will be created.

SPIFF_BIN

Filename to use for the generated SPIFFS filesystem image. The default is spiff_rom.

SPIFF_BIN_OUT

[read-only] Shows the full path to the generated image file.

For more control over the SPIFFS partition you can create your own partition definition in a custom Hardware configuration.

SPIFF_FILEDESC_COUNT

Default: 7

Number of file descriptors allocated. This sets the maximum number of files which may be opened at once.

SPIFFS_OBJ_META_LEN

Default: 16

Maximum size of metadata which SPIFFS stores in each file index header (after the filename). If this value is changed, existing SPIFFS images will not be readable.

The default value given here is provided to support Installable File System extended file attribute information.

The first 16 bytes are used for system attributes (e.g. modified time), so setting this to, say, 64 leaves 48 bytes for user metadata. Each attribute has a 2-byte header (tag + size) so a single user attribute can be stored of up to 46 bytes, or multiple tags up to this limit.

Note: LittleFS provides better support for user metadata.

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: spiffs
SPIFFS (SPI Flash File System)

V0.3.7

Build Status

Copyright (c) 2013-2017 Peter Andersson (pelleplutt1976 at gmail.com)

For legal stuff, see LICENSE. Basically, you may do whatever you want with the source. Use, modify, sell, print it out, roll it and smoke it - as long as I won’t be held responsible.

Love to hear feedback though!

INTRODUCTION

Spiffs is a file system intended for SPI NOR flash devices on embedded targets.

Spiffs is designed with following characteristics in mind:

  • Small (embedded) targets, sparse RAM without heap

  • Only big areas of data (blocks) can be erased

  • An erase will reset all bits in block to ones

  • Writing pulls one to zeroes

  • Zeroes can only be pulled to ones by erase

  • Wear leveling

BUILDING

mkdir build; make

Otherwise, configure the builddir variable towards the top of makefile as something opposed to the default build. Sanity check on the host via make test and refer to .travis.yml for the official in-depth testing procedure. See the wiki for integrating spiffs into projects and spiffsimg from nodemcu is a good example on the subject.

FEATURES

What spiffs does:

  • Specifically designed for low ram usage

  • Uses statically sized ram buffers, independent of number of files

  • Posix-like api: open, close, read, write, seek, stat, etc

  • It can run on any NOR flash, not only SPI flash - theoretically also on embedded flash of a microprocessor

  • Multiple spiffs configurations can run on same target - and even on same SPI flash device

  • Implements static wear leveling

  • Built in file system consistency checks

  • Highly configurable

What spiffs does not:

  • Presently, spiffs does not support directories. It produces a flat structure. Creating a file with path tmp/myfile.txt will create a file called tmp/myfile.txt instead of a myfile.txt under directory tmp.

  • It is not a realtime stack. One write operation might last much longer than another.

  • Poor scalability. Spiffs is intended for small memory devices - the normal sizes for SPI flashes. Going beyond ~128Mbyte is probably a bad idea. This is a side effect of the design goal to use as little ram as possible.

  • Presently, it does not detect or handle bad blocks.

  • One configuration, one binary. There’s no generic spiffs binary that handles all types of configurations.

NOTICE

0.4.0 is under construction. This is a full rewrite and will change the underlying structure. Hence, it will not be compatible with earlier versions of the filesystem. The API is the same, with minor modifications. Some config flags will be removed (as they are mandatory in 0.4.0) and some features might fall away until 0.4.1. If you have any worries or questions, it can be discussed in issue #179

MORE INFO

See the wiki for configuring, integrating, using, and optimizing spiffs.

For design, see docs/TECH_SPEC.

For a generic spi flash driver, see this.

HISTORY
0.3.7
  • fixed prevent seeking to negative offsets #158

  • fixed file descriptor offsets not updated for multiple fds on same file #157

  • fixed cache page not closed for removed files #156

  • fixed a lseek bug when seeking exactly to end of a fully indexed first level LUT #148

  • fixed wear leveling issue #145

  • fixed attempt to write out of bounds in flash #130,

  • set file offset when seeking over end #121 (thanks @sensslen)

  • fixed seeking in virgin files #120 (thanks @sensslen)

  • Optional file metadata #128 (thanks @cesanta)

  • AFL testing framework #100 #143 (thanks @pjsg)

  • Testframe updates

New API functions:

  • SPIFFS_update_meta, SPIFFS_fupdate_meta - updates metadata for a file

New config defines:

  • SPIFFS_OBJ_META_LEN - enable possibility to add extra metadata to files

0.3.6
  • Fix range bug in index memory mapping #98

  • Add index memory mapping #97

  • Optimize SPIFFS_read for large files #96

  • Add temporal cache for opening files #95

  • More robust gc #93 (thanks @dismirlian)

  • Fixed a double write of same data in certain cache situations

  • Fixed an open bug in READ_ONLY builds

  • File not visible in SPIFFS_readdir #90 (thanks @benpicco-tmp)

  • Cache load code cleanup #92 (thanks @niclash)

  • Fixed lock/unlock asymmetry #88 #87 (thanks @JackJefferson, @dpruessner)

  • Testframe updates

New API functions:

  • SPIFFS_ix_map - map index meta data to memory for a file

  • SPIFFS_ix_unmap - unmaps index meta data for a file

  • SPIFFS_ix_remap - changes file offset for index metadata map

  • SPIFFS_bytes_to_ix_map_entries - utility, get length of needed vector for given amount of bytes

  • SPIFFS_ix_map_entries_to_bytes - utility, get number of bytes a vector can represent given length

New config defines:

  • SPIFFS_IX_MAP - enable possibility to map index meta data to memory for reading faster

  • SPIFFS_TEMPORAL_FD_CACHE - enable temporal cache for opening files faster

  • SPIFFS_TEMPORAL_CACHE_HIT_SCORE - for tuning the temporal cache

0.3.5
  • Fixed a bug in fs check

  • API returns actual error codes #84) (thanks @Nails)

  • Fix compiler warnings for non-gcc #83 #81 (thanks @Nails)

  • Unable to recover from full fs #82 (thanks @rojer)

  • Define SPIFFSO* flags #80

  • Problem with long filenames #79 (thanks @psjg)

  • Duplicate file name bug fix #74 (thanks @igrr)

  • SPIFFS_eof and SPIFFS_tell return wrong value #72 (thanks @ArtemPisarenko)

  • Bunch of testframe updates #77 #78 #86 (thanks @dpreussner, @psjg a.o)

0.3.4
  • Added user callback file func.

  • Fixed a stat bug with obj id.

  • SPIFFS_probe_fs added

  • Add possibility to compile a read-only version of spiffs

  • Make magic dependent on fs length, if needed (see #59 & #66) (thanks @hreintke)

  • Exposed SPIFFS_open_by_page_function

  • Zero-size file cannot be seek #57 (thanks @lishen2)

  • Add tell and eof functions #54 (thanks @raburton)

  • Make api string params const #53 (thanks @raburton)

  • Preserve user_data during mount() #51 (thanks @rojer)

New API functions:

  • SPIFFS_set_file_callback_func - register a callback informing about file events

  • SPIFFS_probe_fs - probe a spi flash trying to figure out size of fs

  • SPIFFS_open_by_page - open a file by page index

  • SPIFFS_eof - checks if end of file is reached

  • SPIFFS_tell - returns current file offset

New config defines:

  • SPIFFS_READ_ONLY

  • SPIFFS_USE_MAGIC_LENGTH

0.3.3

Might not be compatible with 0.3.2 structures. See issue #40

  • Possibility to add integer offset to file handles

  • Truncate function presumes too few free pages #49

  • Bug in truncate function #48 (thanks @PawelDefee)

  • Update spiffs_gc.c - remove unnecessary parameter (thanks @PawelDefee)

  • Update INTEGRATION docs (thanks @PawelDefee)

  • Fix pointer truncation in 64-bit platforms (thanks @igrr)

  • Zero-sized files cannot be read #44 (thanks @rojer)

  • (More) correct calculation of max_id in obj_lu_find #42 #41 (thanks @lishen2)

  • Check correct error code in obj_lu_find_free #41 (thanks @lishen2)

  • Moar comments for SPIFFS_lseek (thanks @igrr)

  • Fixed padding in spiffs_page_object_ix #40 (thanks @jmattsson @lishen2)

  • Fixed gc_quick test (thanks @jmattsson)

  • Add SPIFFS_EXCL flag #36

  • SPIFFS_close may fail silently if cache is enabled #37

  • User data in callbacks #34

  • Ignoring SINGLETON build in cache setup (thanks Luca)

  • Compilation error fixed #32 (thanks @chotasanjiv)

  • Align cand_scores (thanks @hefloryd)

  • Fix build warnings when SPIFFS_CACHE is 0 (thanks @ajaybhargav)

New config defines:

  • SPIFFS_FILEHDL_OFFSET

0.3.2
  • Limit cache size if too much cache is given (thanks pgeiem)

  • New feature - Controlled erase. #23

  • SPIFFS_rename leaks file descriptors #28 (thanks benpicco)

  • moved dbg print defines in test framework to params_test.h

  • lseek should return the resulting offset (thanks hefloryd)

  • fixed type on dbg ifdefs

  • silence warning about signed/unsigned comparison when spiffs_obj_id is 32 bit (thanks benpicco)

  • Possible error in test_spiffs.c #21 (thanks yihcdaso-yeskela)

  • Cache might writethrough too often #16

  • even moar testrunner updates

  • Test framework update and some added tests

  • Some thoughts for next gen

  • Test sigsevs when having too many sectors #13 (thanks alonewolfx2)

  • GC might be suboptimal #11

  • Fix eternal readdir when objheader at last block, last entry

New API functions:

  • SPIFFS_gc_quick - call a nonintrusive gc

  • SPIFFS_gc - call a full-scale intrusive gc

0.3.1
  • Removed two return warnings, was too triggerhappy on release

0.3.0
  • Added existing namecheck when creating files

  • Lots of static analysis bugs #6

  • Added rename func

  • Fix SPIFFS_read length when reading beyond file size

  • Added reading beyond file length testcase

  • Made build a bit more configurable

  • Changed name in spiffs from “errno” to “err_code” due to conflicts compiling in mingw

  • Improved GC checks, fixed an append bug, more robust truncate for very special case

  • GC checks preempts GC, truncate even less picky

  • Struct alignment needed for some targets, define in spiffs config #10

  • Spiffs filesystem magic, definable in config

New config defines:

  • SPIFFS_USE_MAGIC - enable or disable magic check upon mount

  • SPIFFS_ALIGNED_OBJECT_INDEX_TABLES - alignment for certain targets

New API functions:

  • SPIFFS_rename - rename files

  • SPIFFS_clearerr - clears last errno

  • SPIFFS_info - returns info on used and total bytes in fs

  • SPIFFS_format - formats the filesystem

  • SPIFFS_mounted - checks if filesystem is mounted

Switch Joycon

Introduction

This library allows you to make the ESP32 act as a Nintendo Switch Joycon and control what it does. The library uses ESP32 NimBLE for faster and lighter communication.

Disclaimer

We are not affiliated, associated, authorized, endorsed by, or in any way officially connected with Nintendo, or any of its subsidiaries or its affiliates. The names Nintendo, Nintendo Switch and Joycon as well as related names, marks, emblems and images are registered trademarks of their respective owners.

Features

Using this library you can do the following:

  • Button press and release (16 buttons)

  • Switch Hat (1 hat )

  • Rotate 4 Axis

Using
  1. Add COMPONENT_DEPENDS += SwitchJoycon to your application componenent.mk file.

  2. Add these lines to your application:

    #include <SwitchJoycon.h>
    
    namespace
    {
            SwitchJoycon joycon;
    
            // ...
    
    } // namespace
    
    void init()
    {
            // ...
    
            joycon.begin();
    }
    
Notes

By default, reports are sent on every button press/release or axis/hat movement, however this can be disabled:

joycon.setAutoReport(false);

and then you should manually call sendReport on the joycon instance as shown below:

joycon.sendReport();
HID Debugging

On Linux you can install hid-tools using the command below:

sudo pip3 install .

Once installed hid-recorder can be used to check the device HID report description and sniff the different reports:

sudo hid-recorder
References
Used by
SoC support
  • esp32

  • esp32c3

  • esp32s3

ILI9163C TFT Display

ILI9163C- A fast SPI driver for TFT that use Ilitek ILI9163C.

ILI9163C
Link to a video:

https://www.youtube.com/watch?v=y5f-VNBxgEk&feature=youtu.be


Features:

- Very FAST!, especially with Teensy 3.x where uses DMA SPI.
- It uses just 4 or 5 wires.
- Compatible at command level with Adafruit display series so it's easy to adapt existing code.
- It uses the standard Adafruit_GFX Library (you need to install).

Pay Attention to connections!!!!:

- This display has logic at 3V3 volt so YOU NEED A VOLTAGE CONVERTER if you plan to use with arduino.
If you try to connect directly you can burn it very fast so PAY ATTENTION!
- My display works at 3V3 volt for logic but LED background has resistor for 5V. Your can be different
so carefully check out before connect it.
- My library works only in SPI mode by using MOSI,SCLK and a CS pin plus an additional pin for DC (or RS).
I've used also the reset pin but you can save it by connect it at 3V3 volt and use the constructor without
the reset pin. The initialization routine will automatically use the software reset.
- People using Teensy3 should remember that have to choose for CS and DC a pin that satisfy:

if (pin == 2 || pin == 6 || pin == 9) return true;
if (pin == 10 || pin == 15) return true;
if (pin >= 20 && pin <= 23) return true;

Background:

I got one of those displays from a chinese ebay seller but unfortunately I cannot get
any working library so I decided to hack it. ILI9163C looks pretty similar to other
display driver but it uses it's own commands so it's tricky to work with it unlsess you
carefully fight with his gigantic and not so clever datasheet.
My display it's a 1.44"", 128x128 that suppose to substitute Nokia 5110 LCD and here's the
first confusion! Many sellers claim that it's compatible with Nokia 5110 (that use a philips
controller) but the only similarity it's the pin names since that this one it's color and
have totally different controller that's not compatible. Altrough I discovered that it's not
128x128 but 128x160 (!??)... Check links below to see if it's similar to yours.

http://www.ebay.com/itm/Replace-Nokia-5110-LCD-1-44-Red-Serial-128X128-SPI-Color-TFT-LCD-Display-Module-/141196897388

http://www.elecrow.com/144-128x-128-tft-lcd-with-spi-interface-p-855.html

Pay attention that ILI9163C can drive different resolutions and your display can be
160*128 or whatever, also there's a strain of this display with a black PCB that a friend of mine
got some weeks ago and need some small changes in library to get working.
If you look at TFT_ILI9163C.h file you can add your modifications and let me know so I
can include for future versions.

Code Optimizations:

The purpose of this library it's SPEED. I have tried to use hardware optimized calls
where was possible and results are quite good for most applications.
Of course it can be improved so feel free to add suggestions.

Copyright (c) 2014, .S.U.M.O.T.O.Y., coded by Max MC Costa.

TFT_ILI9163C Library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

TFT_ILI9163C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Foobar.  If not, see <http://www.gnu.org/licenses/>.

This file needs the following Libraries:

*Adafruit_GFX by Adafruit:

https://github.com/adafruit/Adafruit-GFX-Library

Remember to update GFX library often to have more features with this library!

*From this version I'm using my version of Adafruit_GFX library:

https://github.com/sumotoy/Adafruit-GFX-Library

It has faster char rendering and some small little optimizations but you can
choose one of the two freely since are both fully compatible.

Special Thanks:

Thanks Adafruit for his Adafruit_GFX!
Thanks to Paul Stoffregen for his beautiful Teensy3 and DMA SPI.

Version:

0.1a1: First release, compile correctly. Altrough not fully working!
0.1a3: Some bugfix, still some addressing problems, partial rotation solved.
0.1b1: Beta version! Fully working but still not tested with Arduino and DUE (altrough it compile)
0.2b2: Code cleaned and added support for 2.2" RED PCB displays.
0.2b4: Bug fixes and added color space support.

BugList of the current version:

- Beta version fully working!
References
Used by
SoC support
  • esp8266

TFT_S1D13781

Introduction

This is a port of the driver from the Epson S1D13781 display controller using the HardwareSPI library.

Evaluation boards are inexpensive and is a useful way to evaluate display modules with TFT interfaces.

The Newhaven NHD-5.0-800480TF-ATXL#-CTP was used during development.

_images/screentft-s1d13781.jpg

Video line: https://youtu.be/UgLX9gEdz6A

Notes

This library was developed as a stepping stone to the Bridgetek FT81x series EVE Embedded Video Engines.

These are more advanced and work by buffering graphics command primitives which are then executed in real time to construct each displayed frame.

These devices are used in the Gameduino. The author has an open source library.

This library is not fully asynchronous, but has couple of key improvements over the original source:

Cached register values

These do not need to be read for every operation. Considerable increase in performance.

Asynchronous writes

The HardwareSPI library can execute requests asynchronously, and this is used to provide some improvement in performance where no response is required from the display controller.

The next stage of development would be to build a generic graphics library to support multiple display controllers. Like the EVE controllers, it would incorporate a graphics instruction pipeline so that the application can buffer drawing requests in a fully asynchronous manner, eliminating any bottlenecks or wait states in the application.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

TM1637 LED Driver

Description

An Arduino library for 7-segment display modules based on the TM1637 chip, such as Seeed Studio’s Grove 4 digit display. The TM1637 chip also has keyboard input capability, but it’s not implemented in this library.

Hardware Connection

The display modules has two signal connection (and two power connections) which are CLK and DIO. These pins can be connected to any pair of digital pins on the Arduino. When an object is created, the pins should be configured. There is no limitation on the number of instances used concurrently (as long as each instance has a pin pair of its own)

Installation

The library is installed as any Arduino library, by copying the files into a directory on the library search path of the Arduino IDE

Usage

The library provides a single class named TM1637Display. An instance of this class provides the following functions:

  • setSegments - Set the raw value of the segments of each digit

  • showNumberDec - Display a decimal number

  • showNumberDecEx - Display a decimal number with decimal points or colon

  • setBrightness - Sets the brightness of the display

The information given above is only a summary. Please refer to TM1637Display.h for more information. An example is included, demonstrating the operation of most of the functions.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Timezone

Arduino library to support local/UTC time conversions using rules

See SystemClock NTP for example usage.

Port of https://github.com/JChristensen/Timezone for Sming.

API Documentation
class Timezone

Class to support local/UTC time conversions using rules.

Public Functions

time_t toLocal(time_t utc, const TimeChangeRule **p_tcr = nullptr)

Convert the given UTC time to local time, standard or daylight time.

Parameters:
  • utc – Time in UTC

  • p_tcr – Optionally return the rule used to convert the time

Return values:

time_t – The local time

time_t toUTC(time_t local)

Convert the given local time to UTC time.

WARNING: This function is provided for completeness, but should seldom be needed and should be used sparingly and carefully.

Ambiguous situations occur after the Standard-to-DST and the DST-to-Standard time transitions. When changing to DST, there is one hour of local time that does not exist, since the clock moves forward one hour. Similarly, when changing to standard time, there is one hour of local times that occur twice since the clock moves back one hour.

This function does not test whether it is passed an erroneous time value during the Local -> DST transition that does not exist. If passed such a time, an incorrect UTC time value will be returned.

If passed a local time value during the DST -> Local transition that occurs twice, it will be treated as the earlier time, i.e. the time that occurs before the transition.

Calling this function with local times during a transition interval should be avoided.

Note

Parameters:

local – Local time

Return values:

time_t – Time in UTC

bool utcIsDST(time_t utc)

Determine whether the UTC time is within the DST interval or the Standard time interval.

Parameters:

utc

Return values:

bool – true if time is within DST

bool locIsDST(time_t local)

Determine whether the given local time is within the DST interval or the Standard time interval.

Parameters:

local – Local time

Return values:

bool – true if time is within DST

inline const char *timeTag(bool isDst) const

Return the appropriate dalight-savings tag to append to displayed times.

Parameters:

isDst – true if DST tag is required, otherwise non-DST tag is returned

Return values:

const – char* The tag

inline const char *utcTimeTag(time_t utc)

Return the appropriate time tag for a UTC time.

Parameters:

utc – The time in UTC

Return values:

const – char* Tag, such as UTC, BST, etc.

inline const char *localTimeTag(time_t local)

Return the appropriate time tag for a local time.

Parameters:

local – The local time

Return values:

const – char* Tag, such as UTC, BST, etc.

struct TimeChangeRule

Public Members

char tag[6]

e.g. DST, UTC, etc.

week_t week

First, Second, Third, Fourth, or Last week of the month.

dow_t dow

Day of week, 0=Sun.

month_t month

1=Jan

uint8_t hour

0-23

int offset

Offset from UTC in minutes.

enum week_t

Values:

enumerator Last
enumerator First
enumerator Second
enumerator Third
enumerator Fourth
enum dow_t

Values:

enumerator Sun
enumerator Mon
enumerator Tue
enumerator Wed
enumerator Thu
enumerator Fri
enumerator Sat
enum month_t

Values:

enumerator Jan
enumerator Feb
enumerator Mar
enumerator Apr
enumerator May
enumerator Jun
enumerator Jul
enumerator Aug
enumerator Sep
enumerator Oct
enumerator Nov
enumerator Dec
References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Tone Generator

Uses the I2S module to synthesize tones using a minimal amount of Flash memory, RAM and CPU time.

Audio is output via the I2S_TX_DATA pin using Delta-Sigma modulation. This is a high-frequency bitstream which requires low-pass filtering and suitable amplification for driving speaker/headphones.

Warning

Do not connect the output directly to a loudspeaker or headphone as you may damage the GPIO. Always buffer the signal using a transistor, op-amp, etc.

See Esp8266 Drivers for further information about I2S.

As the output pin is normally used as UART0 RX, the alternate UART pin configuration is used:

GPIO

Alternate

NodeMCU

Notes

3

I2S_TX_DATA

D9

Audio output

13

RXD2

D7

Debug serial input

15

TXD2

D8

Debug serial output

GPIO3 is still required for programming so you’ll need to make sure the audio buffer is disconnected, or has a reasonably high impedance load. You’ll also need an additional USB-serial converter connected to GPIO13 & 15 for terminal operation.

API Documentation
class ToneGenerator

Generates tones with smooth transitions using Tone Buffers.

Note

During tone playback, a single active ToneBuffer feeds the I2S with samples. When a new tone is prepared via queueTone(), a ToneBuffer is added to the pending queue. If transitioning between tone and silence then additional fade in/out buffers are queued to implement low-pass filtering on these relatively abrupt transitions and thus reduce clicking. Calling submitPending() appends the pending queue onto the transition queue. The transition to the new tone (or silence) is made within i2sWrite(), at which point buffers are released back to the avail queue.

Public Functions

bool begin(unsigned sampleRate)

Initialise the tone generator and I2S.

Parameters:

sampleRate

Return values:

bool – true on success

void end()

Stop playback and un-initialise I2S.

Note

Releases all allocated memory

bool start()

Start tone playback.

void stop()

Stop tone playback and release any memory allocated for buffers.

Note

Leaves I2S initialised. Call start() to resume.

ToneBuffer *createTone(Voice voice, unsigned frequency, ToneEffect effect, unsigned repeatCount = 0)

Create a tone and queue it.

Parameters:
  • voice – Voice to use for tone

  • frequency – Frequency in Hz

  • effect

  • repeatCount – Specify non-zero to repeat this tone a specific number of times

Return values:

ToneBuffer* – The queued buffer, nullptr on error

void queueTone(Voice voice, unsigned frequency)

Create a tone with appropriate filtering.

Parameters:

voice – Voice to use for tone @frequency Frequency in Hz @

inline void submitPending()

Submit queued tone buffers for playback.

class ToneBuffer

Contains samples for one full signal cycle at a specific frequency.

Note

Data is stored Delta-Sigma modulated to minimise I2S transfer overhead.

References
Used by
SoC support
  • esp8266

  • host

UPnP Schema

This is a separate library for UPnP schema. It contains standard and reusable schema.

Code and header files are generated directly from schema to simplify the task of implementing hosted UPnP devices and controlling them.

You can find these under Sming/out/{SMING_ARCH}/{debug|release}/build/UPnP-Schema/src.

Controlling UPnP devices

Devices and services must be registered with the UPnP framework so that the correct objects can be constructed during discovery. This information is generated in groups, with one group for each domain. This means you only need to include one file in your project. For example:

#include <Network/UPnP/schemas-sming-org/ClassGroup.h>

void initUPnP()
{
   UPnP::schemas_upnp_org::registerClasses();
}

This tells UPnP about all the standard devices and services.

Custom schema

Custom schema may be imported from the application or another Component. Create a schema sub-directory and arrange as:

schema/
   {domain}/
      device/
         ...
      service/
         ...

Suitable schema are generated by the UPnP scan tool. These may be edited and customised with additional detail, comments, etc. as required.

Build variables
UPNP_SCHEMA

Read-only. Contains a list of all discovered schema directories to be built for the project.

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

UPnP

Universal Plug and Play.

Introduction

A C++ library for managing UPnP devices and services using the Sming framework.

About UPnP
Introduction

A C++ library for managing UPnP devices and services using the Sming framework.

Universal Plug and Play (UPnP) is a set of networking protocols that permits networked devices, such as personal computers, printers, Internet gateways, Wi-Fi access points and mobile devices to seamlessly discover each other’s presence on the network and establish functional network services for data sharing, communications, and entertainment. (Wikipedia)

UPnP provides a set of standards which allows user applications (Control Points such as a mobile phone App.) to discover and control embedded devices through standardised services.

_images/upnp-discovery.png

Please refer to the official UPnP Device Architecture version 2.0 for further details.

Background

There are various open source UPnP libraries available, however we have a very specific set of requirements which none of them can really meet.

If (like me) you’re only dimly aware of UPnP and how it works, researching the requirements is an important first step and in itself is time consuming.

What we’d really like is a simple API which lets us throw together a fully UPnP compliant solution in the time it takes to drink a cup of coffee, with only a basic knowledge of how it all works.

The ESP8266 is a ‘Class II IoT Device’

This classification is from RFC7228: Terminology for Constrained-Node Networks, and considers the resources available for an embedded device.

Basically, it can’t run Linux but it can support IP networking.

Whilst the ESP32 is ‘less constrained’, it’s still class II and without care and attention to framework (and application) design all that extra RAM and horsepower will rapidly disappear.

UPnP (and SSDP) is just UDP, TCP, XML and JSON, so what’s the problem?

True, you can just throw together a custom solution which implements the various protocols and works OK. But like everything to do it properly takes time and effort.

If you want it to be reliable and behave like a good neighbour then sticking to the standards is important:

  • Be nice, don’t flood the network with search requests

  • But at the same time UDP is ‘unreliable’ so we should repeat messages

  • How to listen to SSDP broadcasts without hanging the system

What’s this about XML? Why can’t we just use JSON?

One of the advantages of JSON is it’s compact, human-friendly format. XML is less so, but it is more structured and well-suited for machine-to-machine information transfer.

It’s a core part of UPnP. See UPnP Resources.

One of the many cool things about XML is XSLT, which lets us easily convert existing XML documents into any format we like. The tools to do this are readily available; we can perform these transformations in a web browser, for example, hence the wealth of online tools to do just this.

We can also use the published Schema to ensure strict standards compliance.

How do we deal with XML on a memory-constrained device?

For very simple devices it’s not an issue, but larger sets of description information will need to be handled in small chunks and assembled before it gets sent out. We want our classes to take care of all this transparently.

If we want to discover other networked devices and control them, we’ll also need to parse the XML data in a similar way.

What XML parser should I use in C?.

Most of the UPnP libraries are for Linux or Windows

The OSGi Alliance provides a dynamic module system for java which includes many good design ideas and a great deal of useful information, such as:

There are a couple of C/C++ frameworks which implemenent the OSGi specifications:

Others:

  • gUPnP object-oriented framework in C.

Curiously, I’ve failed to turn up anything for FreeRTOS.

I should probably mention Microsoft as they’re the ones who created UPnP in the first place! See UPnP APIs.

Good integration with the Sming framework

I like Arduino-upnp, it’s a good approach and has some really great ideas. So if any of this library seems familiar that’ll be why!

In the end I decided to just build something from scratch. These are the specific areas I wanted to address:

Support for existing Sming applications

You already have an embedded device but want to make it accessible via UPnP services. That means we want a lightweight layer which is easily added on top.

Be careful with RAM

We don’t want to waste RAM keeping track of state and other information which is already available elsewhere.

Instead of keeping lists of services or devices we use an abstract enumerator. For example, a lighting controller may track the state of dozens or hundreds of lights, so the overhead of creating an object instance for each one is best avoided.

Also, there is a considerably amount of UPnP configuration information but it can be stored in Flash and only read out when actually required.

Classes

An application implements their devices and services using these provided base classes.

DeviceHost

Implements SSDP discovery and notification supporting multiple root devices. The SSDP library handles the protocol details.

Device

All devices are implemented using this base class, including root devices. A common example of a root device is a Television, with separate (embedded) devices controlling subsystems such as sound, vision, networking, etc.

Sming in a TV, now there’s an idea…

Service

A service implements actions and manages state to control a device. Like when a REST request asks for a light to be turned on, it’ll be a service that performs the action and tracks state. The advantage with UPnP is that services are self-documented. You can explore this using various UPnP Tools.

Item

All UPnP classes are implemented using the Item class template, which allows them to be efficiently enumerated as a linked list. Class templates are ideal because they avoid the complication of dynamic type casting and generate efficient code.

List

A singly-linked list of items, such as devices or services.

Features
Discovery

UPnP requires a minimal amount of information exchange to advertise services, however device descriptions can be relatively large and therefore unsafe to manipulate in a limited RAM system.

Sming’s template streams are one possible solution to this problem. The IMPORT_FSTR feature allows applications to easily define their own descriptions (templates or otherwise). The alternative is to use SPIFFS, however when Partition Tables are supported this will provide the best of both worlds.

However, the application should not normally need to do all this as the framework will, by default, enumerate device fields and build the device description information ‘on the fly’.

Memory efficiency

Much of the UPnP framework is concerned with discovery and notification, which requires a significant amount of configuration data. This data is obtained via callbacks as required which allows device and service implementations to fetch it from flash memory storage or create it on demand, thus saving on RAM.

Using linked lists also avoids the need for separate RAM allocation and simpler enumeration. Applications are responsible for device and service memory allocation, but unless services need to be dynamically created or destroyed it’s simplest to just create them statically.

Enumeration

One way to manage lists of many objects is to implement an enumerator with a single Service class instance. Every call to enumerator.next() returns the same object instance but with its internal state updated.

The main caveat to this approach is that if you need to keep hold of one these objects then you must make a copy; you cannot hold onto references. For this reason enumerators have a clone() method and objects have copy constructors.

API Documentation
namespace UPnP

Typedefs

using Error = UPnP::Error

Enums

enum ErrorValues

Values:

enum class Error

Values:

enumerator XX
enumerator Pending
enum class ErrorCode

Values:

enumerator XX
enum class DescType

When building descriptions this qualifies what information is required.

Values:

enumerator header

Description template with main document element.

enumerator embedded

Details for embedded device or service.

enumerator content

Full details for this device or service.

Functions

ErrorCode getErrorCode(Error err)

Get corresponding error code for a given error.

Variables

DeviceHost deviceHost
class ActionRequest : public UPnP::ActionResponse
#include <ActionRequest.h>
class ActionRequestControl : public UPnP::ActionResponse
#include <ActionRequest.h>
class ActionResponse : public UPnP::LinkedItem
#include <ActionResponse.h>

Class to handle action requests and responses.

Subclassed by UPnP::ActionRequest, UPnP::ActionRequestControl

class Stream : public MemoryDataStream
#include <ActionResponse.h>

Public Functions

inline virtual bool isFinished() override

Check if all data has been read.

Return values:

bool – True on success.

inline virtual MimeType getMimeType() const override

Get MIME type for stream content.

Return values:

MimeType

class Base64
#include <Base64.h>
struct SearchFilter
#include <BaseObject.h>

Public Members

const MessageSpec &ms

Specification for message to be sent.

uint32_t delayMs

Message delay.

String targetString

Full search target value.

Callback callback

Called on a match.

class BaseObject : public UPnP::LinkedItem
#include <BaseObject.h>

Objects which hook into the SSDP message stack.

Subclassed by UPnP::ObjectTemplate< ControlPoint, BaseObject >, UPnP::Object

Public Functions

virtual bool formatMessage(Message &msg, MessageSpec &ms) = 0

Standard fields have been completed.

Note

Fields can be modified typically by adding any custom fields before sending response.

Parameters:
  • msg – The message being constructed

  • ms – Template spec. for message

Return values:

bool – Return true to send message, false to cancel

virtual void sendMessage(Message &msg, MessageSpec &ms)

Called by framework to construct then send a message.

Parameters:
  • msg – Message to send

  • ms – Template spec.

template<typename ObjectType, typename BaseObjectType>
class ObjectTemplate : public BaseObjectType
#include <BaseObject.h>

Base class template for linked items with type casting.

class Iterator
#include <BaseObject.h>
struct ClassGroup
#include <ClassGroup.h>
class ClassGroupList : public Vector<ClassGroup>
#include <ClassGroup.h>
class ControlPoint : public UPnP::ObjectTemplate<ControlPoint, BaseObject>
#include <ControlPoint.h>

Public Functions

inline ControlPoint(size_t maxResponseSize = 2048)

Constructor.

Parameters:

maxResponseSize – Limits size of stream used to receive HTTP responses

inline void reset()

Cancel any outstanding search and reset the list of known unique service names.

inline void clear()

Perform a reset and destroy all created devices.

inline bool beginSearch(const Urn &urn, SsdpSearch::Callback callback)

Searches for UPnP device or service and returns SSDP response messages.

Parameters:
  • urn – unique identifier of the service or device to find

  • callback – Invoked with SSDP response message

Return values:

bool – true on success, false if request queue is full

inline bool beginSearch(const Urn &urn, DescriptionSearch::Callback callback)

Searches for UPnP device or service and fetches its description.

Parameters:
  • urn – unique identifier of the service or device to find

  • callback – Invoked with device description document

Return values:

bool – true on success, false if request queue is full

inline bool beginSearch(const ObjectClass &cls, DeviceSearch::Callback callback)

Searches for UPnP device.

Parameters:
  • clsDevice class object

  • callback – Invoked with constructed control object

Return values:

bool – true on success, false if request queue is full

inline bool beginSearch(const ObjectClass &cls, ServiceSearch::Callback callback)

Searches for UPnP service.

Parameters:
  • clsService class object

  • callback – Invoked with constructed control object

Return values:

bool – true on success, false if request queue is full

inline bool isSearchActive() const

Determine if there’s an active search in progress.

bool cancelSearch()

Cancel any active search operation.

Todo:

Set timeout on search operation and call this automatically Need to inform application though - perhaps a generic callback on the class?

Return values:

bool – true if a search was active, false if there was no active search

virtual void onNotify(BasicMessage &msg)

Called by framework to handle an incoming SSDP message.

Parameters:

msg

virtual bool formatMessage(Message &msg, MessageSpec &ms) override

Standard fields have been completed.

Note

Fields can be modified typically by adding any custom fields before sending response.

Parameters:
  • msg – The message being constructed

  • ms – Template spec. for message

Return values:

bool – Return true to send message, false to cancel

bool sendRequest(HttpRequest *request)

Send a request.

Parameters:

request – Completed request object: leave response stream unassigned, will be set later

Return values:

bool – true on success, false if queue is full

bool requestDescription(const String &url, DescriptionSearch::Callback callback)

Send a request for description document.

Parameters:
  • request – Description URL

  • callback – To be invoked with requested document

Return values:

bool – true on success, false if queue is full

Public Static Functions

static void onSsdpMessage(BasicMessage &msg)

Called via SSDP when incoming message received.

static const ObjectClass *findClass(const Urn &objectType)

Find a registered class.

Parameters:

objectType – Urn uniquely identifying the class

Return values:

ObjectClass* – The class, or nullptr if not found

static inline void registerClasses(const FlashString &domain, const ObjectClass::List &classes)

Use this method to register all device and service classes you need to control.

Class information is required in order to instantiate device or service objects in response to incoming descriptions. This information must be pre-registered with the control point.

Parameters:
  • domain

  • classes – List of classes to register

class DescriptionStream : public IDataSourceStream
#include <DescriptionStream.h>

Public Functions

inline DescriptionStream(Object &object, const String &descriptionUrl)

Construct a description stream.

Parameters:

object – The Object to enumerate

void reset()

Reset back to start.

Note

Handy if you want to re-use this stream to send it somewhere else

inline virtual bool isValid() const override

Determine if the stream object contains valid data.

Note

Where inherited classes are initialised by constructor this method indicates whether that was successful or not (e.g. FileStream)

Return values:

bool – true if valid, false if invalid

virtual uint16_t readMemoryBlock(char *data, int bufSize) override

Read a block of memory.

Todo:

Should IDataSourceStream::readMemoryBlock return same data type as its bufSize param?

Parameters:
  • data – Pointer to the data to be read

  • bufSize – Quantity of chars to read

Return values:

uint16_t – Quantity of chars read

virtual bool seek(int len) override

Move read cursor.

Parameters:

len – Relative cursor adjustment

Return values:

bool – True on success.

inline virtual bool isFinished() override

Check if all data has been read.

Return values:

bool – True on success.

inline virtual String getName() const override

Returns name of the resource.

Note

Commonly used to obtain name of file

Return values:

String

inline virtual MimeType getMimeType() const override

Get MIME type for stream content.

Return values:

MimeType

struct SpecVersion
#include <Device.h>
class Device : public UPnP::ObjectTemplate<Device, Object>
#include <Device.h>

Represents any kind of device, including a root device.

Subclassed by UPnP::DeviceControl

Public Functions

virtual String getUrl(const String &path) const

Get fully qualified URL.

virtual String getUrlBasePath() const

Get the base URL path.

String resolvePath(const String &path) const

Resolve a path (relative or absolute) into an absolute path.

virtual void search(const SearchFilter &filter) override

Called during SSDP search operation.

virtual bool formatMessage(Message &msg, MessageSpec &ms) override

Standard fields have been completed.

Note

Fields can be modified typically by adding any custom fields before sending response.

Parameters:
  • msg – The message being constructed

  • ms – Template spec. for message

Return values:

bool – Return true to send message, false to cancel

virtual bool onHttpRequest(HttpServerConnection &connection) override

Called by framework to handle an incoming HTTP request.

Parameters:
  • connection

  • request

  • response

Return values:

bool – true if request was handled

virtual IDataSourceStream *createDescription() override

Called by framework to construct a device description response stream.

By default, the framework generates a stream constructed from the device information fields, but this method may be overridden if, for example, a fixed description is stored in an .xml file.

Return values:

IDataSourceStream* – The XML description content

class DeviceControl : public UPnP::Device
#include <DeviceControl.h>

Subclassed by Dial::Client

Public Functions

bool configureRoot(ControlPoint &controlPoint, const String &location, XML::Node *device)

Called on root device only during discovery.

inline DeviceControl &root()

Get the root device.

inline virtual String getUrl(const String &path) const override

Get fully-qualified URL given a relative path.

inline virtual String getUrlBasePath() const override

Get relative path for this device.

inline ControlPoint &controlPoint() const

Get managing control point for this device.

template<typename T>
inline ServiceControl *getService(const T &serviceType)

Find a service for this device given its class.

Template Parameters:

serviceTypeObjectClass or Urn of service to locate

Return values:

ServiceControl* – May be nullptr if not found

template<typename T>
inline DeviceControl *getDevice(const T &deviceType)

Find a child device given its class.

Template Parameters:

deviceTypeObjectClass or Urn of device to locate

Return values:

DeviceControl* – May be nullptr if not found

inline const String udn() const

Get UDN for this device.

bool configure(XML::Node *device)

Configure device using information from description document.

inline virtual void onConnected(HttpConnection &connection)

Inherited classes may override this to pull out any additional information from received response headers, etc. Invoked after description has been processed.

inline const Description &description()

Get device description.

struct Description
#include <DeviceControl.h>
class DeviceHost
#include <DeviceHost.h>

Public Functions

bool begin()

Applications must call this to initialise UPnP stack.

IDataSourceStream *generateDebugPage(const String &title)

Create an HTML page which applications may serve up to assist with debugging.

void onSearchRequest(const BasicMessage &request)

Called via SSDP when incoming message received.

template<typename ItemType, class EnumeratorType>
class Enumerator
#include <Enumerator.h>

Abstract class to enumerate items.

todo: We want a generic enumerator which returns Items, uses virtual methods so no upcasting required

Note

Returned items may only be considered valid for the duration of the current task call as they may be destroyed at any time.

Public Functions

virtual EnumeratorType *clone() = 0

Make a copy of this enumerator.

Note

Each copy maintains position independently

virtual void reset() = 0

Reset enumerator to start of list.

Note

Call to next() will return first item

virtual ItemType *current() = 0

Get the current item.

Return values:

Item* – nullptr if before start or at end of list

virtual ItemType *next() = 0

Get next item.

Return values:

Item* – nullptr if no more devices

class Envelope
#include <Envelope.h>

Class to manage a SOAP envelope for service request/response.

Unnamed Group

Error load(String &&content)

Load a SOAP document.

Parameters:

content – MUST remain valid for the lifetime of this Envelope, or until initialise() is called.

Public Functions

void clear()

Wipe the envelope contents.

inline String serialize(bool pretty)

Obtain content as XML string.

inline size_t serialize(Print &p, bool pretty)

Serialize XML content to a stream.

inline ContentType contentType() const

Get the current envelope content type.

inline String actionName() const

Get the action name.

Envelope &createRequest(const String &actionName)

Initialise the envelope as a request.

Envelope &createResponse(const String &actionName)

Initialise the envelope as a response.

inline void convertToResponse()

Set a flag that this should be converted to Response on next setArg() call.

inline void prepareResponse()

If Response is required but hasn’t been prepared yet, do it now. This wipes out the incoming request.

Fault createFault(ErrorCode error)

Initialise the envelope as a fault.

class Fault
#include <Envelope.h>
class Item
#include <Item.h>

Subclassed by Hue::Device, UPnP::LinkedItem

class ItemEnumerator : public UPnP::Enumerator<Item, ItemEnumerator>
#include <ItemEnumerator.h>

Public Functions

inline virtual ItemEnumerator *clone() override

Make a copy of this enumerator.

Note

Each copy maintains position independently

inline virtual void reset() override

Reset enumerator to start of list.

inline virtual Item *current() override

Get the current item.

Return values:

Item* – nullptr if before start or at end of list

inline virtual Item *next() override

Move to next item in list.

Return values:

Item* – the item, nullptr if at end

class LinkedItem : public UPnP::Item
#include <LinkedItem.h>

Base class template for items in a list.

Subclassed by UPnP::ActionResponse, UPnP::BaseObject

class LinkedItemList
#include <LinkedItemList.h>

Singly-linked list of items.

Note

We don’t own the items, just keep references to them

Subclassed by UPnP::ObjectList< Device >, UPnP::ObjectList< ObjectType >

class Object : public UPnP::BaseObject
#include <Object.h>

Subclassed by UPnP::ObjectTemplate< Service, Object >, UPnP::ObjectTemplate< Device, Object >

Public Functions

virtual void search(const SearchFilter &filter) = 0

Called during SSDP search operation.

inline virtual bool onHttpRequest(HttpServerConnection &connection)

Called by framework to handle an incoming HTTP request.

Parameters:
  • connection

  • request

  • response

Return values:

bool – true if request was handled

inline virtual IDataSourceStream *createDescription()

Called by framework to construct a device description response stream.

By default, the framework generates a stream constructed from the device information fields, but this method may be overridden if, for example, a fixed description is stored in an .xml file.

Return values:

IDataSourceStream* – The XML description content

struct ObjectClass
#include <ObjectClass.h>

Describes device or service class.

Public Types

using Version = uint8_t

Interface version number.

using CreateObject = Object *(*)(DeviceControl *owner)

Object constructor function.

struct Device
#include <ObjectClass.h>

Device description fields.

struct Service
#include <ObjectClass.h>

Service description fields.

template<typename ObjectType>
class ObjectList : public UPnP::LinkedItemList
#include <ObjectList.h>

Class template for singly-linked list of objects.

Note

We don’t own the objects, just keep references to them

Subclassed by UPnP::OwnedObjectList< DeviceControl >, UPnP::OwnedObjectList< Service >, UPnP::OwnedObjectList< ObjectType >

Public Functions

template<typename T>
inline ObjectType *find(const T &objectType)

Search list for matching entry.

Template Parameters:

Urn – or String

Return values:

ObjectType* – Located definition or nullptr if not found

inline ObjectType *find(const ObjectClass &objectClass)

Search list for matching entry given its class @objectClass Class information for object.

Return values:

ObjectType* – Located definition or nullptr if not found

template<typename ObjectType>
class OwnedObjectList : public UPnP::ObjectList<ObjectType>
#include <ObjectList.h>

Class template for singly-linked list of objects.

Note

We own the objects so are responsible for destroying them when removed

struct Search
#include <Search.h>

This is a helper class used by ControlPoint to manage different search types.

Subclassed by UPnP::DescriptionSearch, UPnP::DeviceSearch, UPnP::ServiceSearch, UPnP::SsdpSearch

struct SsdpSearch : public UPnP::Search
#include <Search.h>

Public Types

using Callback = Delegate<void(SSDP::BasicMessage &message)>

Callback invoked for every matching SSDP message.

struct DescriptionSearch : public UPnP::Search
#include <Search.h>

Public Types

using Callback = Delegate<void(HttpConnection &connection, XML::Document *description)>

Callback invoked when description received If HTTP request fails, description will be null.

struct DeviceSearch : public UPnP::Search
#include <Search.h>

Public Types

using Callback = Delegate<bool(DeviceControl &device)>

Callback invoked when device has been located.

Note

If callback sends out action requests then must return true

Param device:

A newly constructed instance matching the search criteria

Retval bool:

Return true to keep the device, false to destroy it

struct ServiceSearch : public UPnP::Search
#include <Search.h>

Public Types

using Callback = Delegate<bool(DeviceControl &device, ServiceControl &service)>

Callback invoked when service has been located.

Note

If callback sends out action requests then must return true

Param device:

A newly constructed instance matching the search criteria

Param service:

Requested service interface

Retval bool:

Return true to keep the device, false to destroy it

class Service : public UPnP::ObjectTemplate<Service, Object>
#include <Service.h>

Represents any kind of device, including a root device.

Subclassed by UPnP::ServiceControl

Public Functions

virtual void search(const SearchFilter &filter) override

Called during SSDP search operation.

virtual bool formatMessage(Message &msg, MessageSpec &ms) override

Standard fields have been completed.

Note

Fields can be modified typically by adding any custom fields before sending response.

Parameters:
  • msg – The message being constructed

  • ms – Template spec. for message

Return values:

bool – Return true to send message, false to cancel

virtual bool onHttpRequest(HttpServerConnection &connection) override

Called by framework to handle an incoming HTTP request.

Parameters:
  • connection

  • request

  • response

Return values:

bool – true if request was handled

virtual IDataSourceStream *createDescription() override

Called by framework to construct a device description response stream.

By default, the framework generates a stream constructed from the device information fields, but this method may be overridden if, for example, a fixed description is stored in an .xml file.

Return values:

IDataSourceStream* – The XML description content

inline virtual bool sendRequest(HttpRequest *request) const

Implemented in ServiceControl.

virtual Error handleAction(ActionRequest &req) = 0

An action request has been received.

Parameters:

env – Contains the action request

Return values:

ErrorCode – Implementation should place response into env after reading parameters, e.g.:

if(env.actionName() == "MyAction") {
        String arg1;
        int arg2;
        env.getArg("arg1", arg1);
        env.getArg("arg2", arg2);
        auto& response = env.createResponse();
        // Process command here
        int arg3 = processMyAction(arg1, arg2);
        response.setArg("arg3", arg3);
        return ErrorCode::Success;
    }

    return ErrorCode::InvalidAction;
This is usually handled by generated wrapper class templates.

class ServiceControl : public UPnP::Service
#include <ServiceControl.h>

Public Functions

inline DeviceControl &root()

Get the root device.

virtual bool sendRequest(HttpRequest *request) const override

Implemented in ServiceControl.

inline virtual Error handleAction(ActionRequest &req) override

An action request has been received.

Parameters:

env – Contains the action request

Return values:

ErrorCode – Implementation should place response into env after reading parameters, e.g.:

if(env.actionName() == "MyAction") {
        String arg1;
        int arg2;
        env.getArg("arg1", arg1);
        env.getArg("arg2", arg2);
        auto& response = env.createResponse();
        // Process command here
        int arg3 = processMyAction(arg1, arg2);
        response.setArg("arg3", arg3);
        return ErrorCode::Success;
    }

    return ErrorCode::InvalidAction;
This is usually handled by generated wrapper class templates.

bool configure(const XML::Node *service)

Called during initialisation to configure this object.

inline const Description &description()

Get service description.

struct Description
#include <ServiceControl.h>
namespace schemas_upnp_org
namespace device
Development Notes

These may be of some interest.

Device state management

It should be possible to add and remove devices dynamically.

For example, an application may contain a MODBUS controller which communicates with various slaves to switch lights, garden pumps, etc. We’ve also decided to create a UPnP device object for each MODBUS slave.

If we add a new slave to the network, then our application creates a new UPnP device object and passes it to e.g. ‘UPnP::addDevice()’, which does this:

  • Add the device to the stack

  • Queue a set of ‘ssdp:alive’ announcements

  • Notify the application via callback that the device has been added

If a slave is to be taken out of the network:

  • Remove the device from the stack so it won’t respond to any new requests

  • Queue a set of ‘ssdp:byebye’ announcements

  • When the final announcement has been sent, notify the application via callback that the device has been removed

When the application gets a ‘remove’ notification it can safely destroy the object, if appropriate, as UPnP has finished using it.

Device tree

Add parent device to embedded devices so we can track back up the tree. Still not clear on whether a device needs to know about its direct parent, or whether a reference to the root device is adequate. A service certainly needs to know about its parent. So we need both root and parent. Root can be obtained by traversing back up the tree.

Icons

Add IconList support

Control Points

This involves sending search requests and responding to advertisements. Requesting descriptions also parsing XML. Using a SAX parser (as listed above) would allow template classes to be populated and passed to an application callback for handling. It would be the application’s responsibility to decide what information to keep and how to manage it.

UPnP version

Decide how to handle versioning. We should support previous versions but selectively enforce version 1.0, 1.1 or 2.0 as required. The version must be consistent throughout a device stack so we should add this as a configurable value for root devices. This then gets propagated into embedded object descriptions as well. We can have multiple root devices at different versions.

URL handling

Responses from VR900 are illuminating. Gateway is on port 1900, media server on 8200. There doesn’t seem to be any technical reason why multiple root devices can share the same port, but using separate ports does mean we can use separate HttpServer instances.

It would not make much sense for a URL to be somewhere completely different, but it doesn’t appear to be prohibited by the V1.0 spec. V2, however, deprecates URLBase and mandates that URLs are all relative to the SSDP response location (where the description file is served from).

Demonstration of discovery

Write a sample for Host which performs an ssdp:all discovery and writes all the description files into a directory tree structure.

Issue multicast search, register callback to receive responses For each response, store in a table (Vector)

If not already in table, queue an HTTP request for the description file
On receipt, write out the description file into a directory:

<ip>/<port>/<path>/

Queue an HTTP request for the presentation page

Write to same location

Components required:

Item table. Implement using Vector. Contains:

IP Address Port number

HTTP request queue. Only service one fetch at a time.

Schema

A separate UPnP Schema is used to manage UPnP device and service schema. It generates C++ classes and sample code directly from these, which you can then use in your application.

Generation of suitable schema can be done using the Device Scanner tool.

Controlling devices

The Basic ControlPoint sample shows how this is done.

Registration

To control UPnP-enabled devices on your local network they must first be located.

In order for this to happen, the framework must be able to match the C++ class implementation against the schema.

The data structures are generated by the UPnP Schema, which creates a function registerClasses for each domain. For example, UPnP::schemas_upnp_org::registerClasses() registers all devices and services for the standard namespace.

Alternatively a subset of classes may be registered by calling UPnP::ControlPoint::registerClasses() directly.

Here’s an example from the ControlPoint sample:

#include <Network/UPnP/schemas-upnp-org/ClassGroup.h>

// Let's make things a little easier for ourselves
using namespace UPnP::schemas_upnp_org::device;
using namespace UPnP::schemas_upnp_org::service;

void initUPnP()
{
   UPnP::schemas_upnp_org::registerClasses();
}
Discovery

This is done using a UPnP::ControlPoint::beginSearch() method, which takes two parameters: the first identifies what you are looking for, the second is a callback which gets invoked when a match has been found. You’ll typically implement this callback using a lambda function.

For example, let’s find all MediaRenderer1 devices:

// Only one active search is permitted so be sure to cancel any existing ones first
controlPoint.cancelSearch();
controlPoint.beginSearch(Delegate<bool(MediaRenderer1&)>([](auto& device) {
   // We can now do stuff with the located device

   // Return true to keep the device, false to destroy it
   return false;
});

This method takes a template parameter which is the C++ class type defining the device you are searching for. The framework will fetch the description for each corresponding device and construct a UPnP::DeviceControl object with appropriate services and embedded devices.

Control

Your search callback function gets a reference to a located device. These devices are created on the heap and owned by the UPnP::ControlPoint. If you want to keep the device, you should take a reference to it and return true from the callback.

To actually do anything useful typically requires use of a UPnP::ServiceControl object. You’ll usually get this by calling UPnP::DeviceControl::getService<UPnP::ObjectClass>() or one of the generated helper methods. Note that this returns a pointer, which will be nullptr if the service isn’t available:

auto render = device.getRenderingControl();
if(render != nullptr) {
   // ...
}

Once you have a Service object, you can control it using action methods:

render->getVolume(0, RenderingControl1::Channel::fs_Master, [&device](auto response) {
   // Process response here
});

Action methods take a list of zero or more input parameters, with the final argument for the response.

Note

The exact type of the response can be determined for you by the compiler. Here’s the explicit call:

render->getVolume(0, RenderingControl1::Channel::fs_Master, [&device](RenderingControl1::GetVolume::Response response response) {
   // ...
});

OK, so handling the action method response. You can get the result values using methods of response, but you must first check that the device did not return a fault:

Serial.println();
Serial.println(_F("render->getVolume(0, Master):"));
// Sample uses a `checkResponse` helper function
if(auto fault = response.fault()) {
   fault.printTo(Serial);
} else {
   Serial.print(device.friendlyName());
   Serial.print(_F(": Current Volume = "));
   Serial.println(response.getCurrentVolume());
}
Serial.println();
Implementing devices

The Basic UPnP sample contains a couple of examples of how to create your own hosted devices. The TeaPot device is the simplest possible implementation, with no services.

The Wemo device is more elaborate and has two services.

Both of these are constructed using code generated from custom schema. These are located in the project’s schema directory which is picked up automatically when the UPnP Schema library is built.

The framework generates a class template for each device and service. For example, take a look in samples/Basic_UPnP/include/Wemo.h#L59-L91:

class BasicEventService : public service::basicevent1Template<BasicEventService>
{
public:
   // Need access to constructors
   using basicevent1Template::basicevent1Template;

   // Override methods if you need to customise any fields
   String getField(Field desc) const override
   {
      switch(desc) {
      case Field::serviceId:
         // You could also put this in the schema
         return F("urn:Belkin:serviceId:basicevent1");
      default:
         return basicevent1Template::getField(desc);
      }
   }

   // Access to our device implementation
   Controllee& controllee()
   {
      return reinterpret_cast<Controllee&>(device());
   }


   /* Here are the action methods */

   Error getBinaryState(GetBinaryState::Response response)
   {
      response.setBinaryState(controllee().getState());
      return Error::Success;
   }

   Error setBinaryState(bool state, SetBinaryState::Response response)
   {
      controllee().setState(state);
      return Error::Success;
   }
};

This perhaps slightly strange construction uses CRTP to use static polymorphism and avoid virtual method tables. This allows the compiler to generate more efficient code.

UPnP Tools

Windows:

Linux:

Under Ubuntu Linux you can install gupnp-tools:

sudo apt install gupnp-tools

And then discover devices on the local network using the following command:

gupnp-universal-cp
_images/upnp-browser.png

You can also start a “software” smart bulb device and use it to test your control point application:

gupnp-network-light
References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

USB

Sming library for TinyUSB.

Note that TinyUSB uses a large number of configuration variables and callback functions, so is compiled and linked directly with the application rather than as a separate library.

Device stack

The TinyUSB example applications generally consist of these three things:

tusb_config.h

Contains definitions such as which classes are supported, how many endpoints (each), buffer sizes, etc.

usb_descriptors.c

Provides descriptor/report structure definitions, interface and endpoint assignments and string definitions, with callback function implementations to provide these to the TinyUSB stack.

main.c and other translation units

Implements class-specific callbacks.

Applications may choose to use the raw TinyUSB API; this is demonstrated in the HID Composite Device sample sample. A drawback of this approach is that the configuration files have to be composed manually, which requires some knowledge of TinyUSB and can also be error-prone.

Instead, the configuration can be described in a JSON file and the configuration generated by the library. The format of this file is defined in schema.json. For vscode, the .usbcfg file extension is association with this schema by make ide-vscode. This enables use of auto-completion in the editor. See Using with MS Visual Studio Code.

This approach is demonstrated in the Basic Device sample. In the application component.mk file, set USB_CONFIG to the name of the configuration file. The configuration will be validated and configuration files generated in, for example, out/Rp2040/debug/USB.

Device classes

C++ Device implementations are provided for some of the standard interfaces. Use is demonstrated in the Basic Device sample.

CDC

Communications Device Class. USB::CDC::Device.

TinyUSB implements only the ACM modes which apparently have issues with Windows. Appears in linux as /dev/ttyACM0, etc.

DFU

Device Firmware Update. USB::DFU::Device. Use the linux dfu-util tool to test.

ECM_RNDIS and NCM

https://en.wikipedia.org/wiki/Ethernet_over_USB

Not yet implemented. USB::ECM_RNDIS::Device and USB::NCM::Device.

HID

Human Interface Device. USB::HID::Device.

MIDI

Musical Instrument Digital Interface (over USB). USB::MIDI::Device.

MSC

Mass Storage Class. USB::MSC::Device.

VENDOR

Devices are identifed by VID:PID and require appropriate host driver. USB::VENDOR::Device. TinyUSB implements a simple read/write interface for this class. This is implemented like a serial port to allow asynchronous streaming, etc.

Host stack

See Basic Host for an example.

Note

At present, there is no host support for Esp32. Samples will build for Rp2040 only.

HUB

When connected to a hub (or multiple hubs) this must be defined in the configuration.

HID

USB::HID::HostDevice

CDC

USB::CDC::HostDevice

MSC

Allows attachment of USB storage. USB::MSC::HostDevice See Basic IFS for a real-world example.

VENDOR

Support access to custom devices. USB::MSC::HostDevice. The sample contains a demonstration for connecting an original XBOX-360 joypad controller.

Configuration variables
USB_DEBUG_LEVEL

default: 0 (disable)

Set to a value from 1-3 to enable debug output messages from the TinyUSB stack.

USB_CONFIG

default: undefined

This identifies the name of the JSON USB configuration file for the application. This allows the TinyUSB configuration data to be generated rather than written manually.

API
namespace USB

Typedefs

using GetDeviceDescriptor = Delegate<const tusb_desc_device_t*(const tusb_desc_device_t &desc)>

Callback to support provision of dynamic device descriptors.

Application typically copies the source descriptor to a statically allocated buffer, then amends values as required.

Param desc:

The statically configured device descriptor

Param const:

tusb_desc_device_t* Application returns a pointer to a statically allocated descriptor to use

using GetDescriptorString = Delegate<const Descriptor*(uint8_t index)>

Application-provided callback to customise string responses.

Note

Returned descriptor MUST NOT be on the stack! Typically this is statically allocated.

Param index:

String index to fetch (STRING_INDEX_xxxx)

Retval const:

StringDescriptor* Pointer to persistent buffer containing descriptor. Return nullptr to use default value from string table.

Functions

void onGetDeviceDescriptor(GetDeviceDescriptor callback)
void onGetDescriptorSting(GetDescriptorString callback)
bool begin()
struct Descriptor
#include <Descriptors.h>

Structure of a USB descriptor.

Subclassed by USB::StringDescriptor< max_chars >

Public Functions

template<typename T>
inline const T *as() const

Less clumsy way to cast descriptor to a specific type.

Template Parameters:

T – TinyUSB defines structures beginning with ‘tusb_desc_’

Public Members

uint8_t length

Total size (in bytes) including this header.

uint8_t type

e.g. TUSB_DESC_STRING

union Type
#include <Descriptors.h>

Public Members

uint8_t value
uint8_t id
uint8_t type
uint8_t reserved
struct USB::Descriptor::Type::[anonymous] [anonymous]
struct DescriptorList
#include <Descriptors.h>

Buffer containing list of descriptors.

Supports C++ iteration.

Subclassed by USB::HID::Report

class Iterator
#include <Descriptors.h>
template<size_t max_chars>
struct StringDescriptor : public USB::Descriptor
#include <Descriptors.h>

Template for making a USB string descriptor.

Public Functions

inline StringDescriptor()

Construct an empty string descriptor.

inline StringDescriptor(const char *str, size_t charCount)

Construct a string descriptor containing text.

Parameters:
  • str – ASCII text (unicode page #0 only)

  • charCount – Number of characters in string

Public Members

uint16_t text[max_chars]

UTF16-LE encoded text (no NUL terminator)

class DeviceInterface
#include <DeviceInterface.h>

Base class to support a USB device interface implementation.

Subclassed by USB::CDC::Device, USB::DFU::Device, USB::ECM_RNDIS::Device, USB::HID::Device, USB::MIDI::Device, USB::MSC::Device, USB::NCM::Device, USB::VENDOR::Device

Public Functions

inline DeviceInterface(uint8_t instance, const char *name)

Constructor.

Parameters:
  • instance – TinyUSB instance or index (class-specific)

  • name – Declared name for this interface instance

class HostInterface
#include <HostInterface.h>

Common base class to support Host USB access.

Subclassed by USB::CDC::HostDevice, USB::HID::HostDevice, USB::MSC::HostDevice, USB::VENDOR::HostDevice

Public Functions

inline void begin(const Instance &inst)

Descendant classes should override this method to peform initialisation.

inline virtual void end()

Called when device is disconnected. Override as required.

struct Instance
#include <HostInterface.h>

Identifies a TinyUSB host interface.

Public Members

uint8_t dev_addr = {255}

Device address (from 1)

uint8_t idx = {255}

Index or instance value specific to class.

const char *name

Optional name for this interface instance.

namespace CDC

Typedefs

using MountCallback = Delegate<HostDevice*(const HostInterface::Instance &inst)>

Application callback to notify connection of a new device.

Param inst:

TinyUSB device instance

Retval HostDevice*:

Application returns pointer to implementation, or nullptr to ignore this device

using UnmountCallback = Delegate<void(HostDevice &dev)>

Application callback to notify disconnection of a device.

Param dev:

The device which has been disconnected

Enums

enum class Event

Values:

enumerator rx_data
enumerator tx_done
enumerator line_break

Functions

void onMount(MountCallback callback)

Application should call this method to receive device connection notifications.

Parameters:

callback

void onUnmount(UnmountCallback callback)

Application should call this method to receive device disconnection notifications.

Parameters:

callback

class Device : public USB::DeviceInterface, public USB::CDC::UsbSerial
#include <Device.h>

Serial device implementation, in ACM mode.

Public Functions

inline virtual size_t setRxBufferSize(size_t size) override

Sets receiving buffer size.

Parameters:

size – requested size

Return values:

size_t – actual size

inline virtual size_t setTxBufferSize(size_t size) override

Sets transmit buffer size.

Parameters:

size – requested size

Return values:

size_t – actual size

inline virtual int available() override

Return the total length of the stream.

Return values:

int – -1 is returned when the size cannot be determined

inline virtual bool isFinished() override

Check if all data has been read.

Return values:

bool – True on success.

inline virtual int read() override

Read one character and moves the stream pointer.

Return values:

The – character that was read or -1 if none is available

inline virtual size_t readBytes(char *buffer, size_t length) override

Read chars from stream into buffer.

Terminates if length characters have been read or timeout (see setTimeout). Returns the number of characters placed in the buffer (0 means no valid data found).

Note

Inherited classes may provide more efficient implementations without timeout.

inline virtual int peek() override

Read a character without advancing the stream pointer.

Return values:

int – The character that was read or -1 if none is available

inline virtual void clear(SerialMode mode = SERIAL_FULL) override

Clear the serial port transmit/receive buffers.

Note

All un-read buffered data is removed and any error condition cleared

Parameters:

mode – Whether to flush TX, RX or both (the default)

virtual size_t write(const uint8_t *buffer, size_t size) override

Write chars to stream.

Note

Although this is defined in the Print class, ReadWriteStream uses this as the core output method so descendants are required to implement it

Parameters:
  • buffer – Pointer to buffer to write to the stream

  • size – Quantity of chars to write

Return values:

size_t – Quantity of chars written to stream

class HostDevice : public USB::HostInterface, public USB::CDC::UsbSerial
#include <HostDevice.h>

Implements CDC interface for a connected serial device.

Public Functions

inline virtual size_t setRxBufferSize(size_t size) override

Sets receiving buffer size.

Parameters:

size – requested size

Return values:

size_t – actual size

inline virtual size_t setTxBufferSize(size_t size) override

Sets transmit buffer size.

Parameters:

size – requested size

Return values:

size_t – actual size

inline virtual int available() override

Return the total length of the stream.

Return values:

int – -1 is returned when the size cannot be determined

inline virtual bool isFinished() override

Check if all data has been read.

Return values:

bool – True on success.

inline virtual int read() override

Read one character and moves the stream pointer.

Return values:

The – character that was read or -1 if none is available

inline virtual size_t readBytes(char *buffer, size_t length) override

Read chars from stream into buffer.

Terminates if length characters have been read or timeout (see setTimeout). Returns the number of characters placed in the buffer (0 means no valid data found).

Note

Inherited classes may provide more efficient implementations without timeout.

inline virtual int peek() override

Read a character without advancing the stream pointer.

Return values:

int – The character that was read or -1 if none is available

inline virtual void clear(SerialMode mode = SERIAL_FULL) override

Clear the serial port transmit/receive buffers.

Note

All un-read buffered data is removed and any error condition cleared

Parameters:

mode – Whether to flush TX, RX or both (the default)

virtual size_t write(const uint8_t *buffer, size_t size) override

Write chars to stream.

Note

Although this is defined in the Print class, ReadWriteStream uses this as the core output method so descendants are required to implement it

Parameters:
  • buffer – Pointer to buffer to write to the stream

  • size – Quantity of chars to write

Return values:

size_t – Quantity of chars written to stream

class UsbSerial : public ReadWriteStream
#include <UsbSerial.h>

Base class for both device and host serial port modes.

Todo:

We could inherit from HardwareSerial here, or preferably provide an abstract base class for all serial devices.

Subclassed by USB::CDC::Device, USB::CDC::HostDevice, USB::VENDOR::Device

Public Functions

virtual size_t setRxBufferSize(size_t size) = 0

Sets receiving buffer size.

Parameters:

size – requested size

Return values:

size_t – actual size

virtual size_t setTxBufferSize(size_t size) = 0

Sets transmit buffer size.

Parameters:

size – requested size

Return values:

size_t – actual size

inline void setTxWait(bool wait)

Governs write behaviour when UART transmit buffers are full.

Parameters:

wait – If false, writes will return short count; applications can use the txComplete callback to send more data. If true, writes will wait for more buffer space so that all requested data is written

inline virtual uint16_t readMemoryBlock(char *buf, int max_len) override

Read a block of memory.

Todo:

Should IDataSourceStream::readMemoryBlock return same data type as its bufSize param?

Parameters:
  • data – Pointer to the data to be read

  • bufSize – Quantity of chars to read

Return values:

uint16_t – Quantity of chars read

inline virtual bool seek(int len) override

Move read cursor.

Parameters:

len – Relative cursor adjustment

Return values:

bool – True on success.

virtual void clear(SerialMode mode = SERIAL_FULL) = 0

Clear the serial port transmit/receive buffers.

Note

All un-read buffered data is removed and any error condition cleared

Parameters:

mode – Whether to flush TX, RX or both (the default)

void systemDebugOutput(bool enabled)

Configure serial port for system debug output and redirect output from debugf.

Note

If enabled, port will issue system debug messages

Parameters:

enabled – True to enable this port for system debug output

unsigned getStatus()

Get status error flags and clear them.

See also

SerialStatus

Return values:

unsigned – Status flags, combination of SerialStatus bits

namespace DFU
class Callbacks
#include <Device.h>

Applications must implement this class and pass an instance to Device::begin().

Public Functions

virtual uint32_t getTimeout(Alternate alt, dfu_state_t state) = 0

Invoked right before tud_dfu_download_cb() (state=DFU_DNBUSY) or tud_dfu_manifest_cb() (state=DFU_MANIFEST)

Application return timeout in milliseconds (bwPollTimeout) for the next download/manifest operation. During this period, USB host won’t try to communicate with us.

virtual void download(Alternate alt, uint32_t offset, const void *data, uint16_t length) = 0

Invoked when received DFU_DNLOAD (wLength>0) following by DFU_GETSTATUS (state=DFU_DNBUSY) requests.

This callback could be returned before flashing op is complete (async). Once finished flashing, application must call complete()

virtual void manifest(Alternate alt) = 0

Invoked when download process is complete, received DFU_DNLOAD (wLength=0) following by DFU_GETSTATUS (state=Manifest)

Application can do checksum, or actual flashing if buffered entire image previously. Once finished flashing, application must call Device::finishFlashing()

virtual uint16_t upload(Alternate alt, uint32_t offset, void *data, uint16_t length) = 0

Invoked when received DFU_UPLOAD request Application must populate data with up to length bytes and return the number of written bytes.

virtual void abort(Alternate alt) = 0

Invoked when the Host has terminated a download or upload transfer.

virtual void detach() = 0

Invoked when a DFU_DETACH request is received.

class Device : public USB::DeviceInterface
#include <Device.h>

Public Static Functions

static inline void complete(dfu_status_t status)

Applications call this method from download and manifest callbacks.

This mechanism supports use of asynchronous writes.

namespace ECM_RNDIS
class Device : public USB::DeviceInterface
#include <Device.h>

Public Functions

inline DeviceInterface(uint8_t instance, const char *name)

Constructor.

Parameters:
  • instance – TinyUSB instance or index (class-specific)

  • name – Declared name for this interface instance

namespace HID

Typedefs

using MountCallback = Delegate<HostDevice*(const HostInterface::Instance &inst, const Report &report)>

Application callback to notify connection of a new device.

Param inst:

TinyUSB device instance

Param report:

HID report descriptors for this interface

Retval HostDevice*:

Application returns pointer to implementation, or nullptr to ignore this device

using UnmountCallback = Delegate<void(HostDevice &dev)>

Application callback to notify disconnection of a device.

Param dev:

The device which has been disconnected

Functions

void onMount(MountCallback callback)

Application should call this method to receive device connection notifications.

Parameters:

callback

void onUnmount(UnmountCallback callback)

Application should call this method to receive device disconnection notifications.

Parameters:

callback

class Device : public USB::DeviceInterface
#include <Device.h>

Public Functions

inline DeviceInterface(uint8_t instance, const char *name)

Constructor.

Parameters:
  • instance – TinyUSB instance or index (class-specific)

  • name – Declared name for this interface instance

struct Report : public USB::DescriptorList
#include <HostDevice.h>
class HostDevice : public USB::HostInterface
#include <HostDevice.h>
namespace MIDI

Enums

enum class Event

Values:

enumerator rx
union Packet
#include <Device.h>

Public Members

uint8_t data[4]
uint8_t code
uint8_t cable_number
uint8_t m0
uint8_t m1
uint8_t m2
struct USB::MIDI::Packet::[anonymous] [anonymous]
class Device : public USB::DeviceInterface
#include <Device.h>
namespace MSC

Typedefs

using MountCallback = Delegate<HostDevice*(const HostInterface::Instance &inst)>

Application callback to notify connection of a new device.

Param inst:

TinyUSB device instance

Retval HostDevice*:

Application returns pointer to implementation, or nullptr to ignore this device

using UnmountCallback = Delegate<void(HostDevice &dev)>

Application callback to notify disconnection of a device.

Param dev:

The device which has been disconnected

Functions

void onMount(MountCallback callback)

Application should call this method to receive device connection notifications.

Parameters:

callback

void onUnmount(UnmountCallback callback)

Application should call this method to receive device disconnection notifications.

Parameters:

callback

class LogicalUnit : public Storage::Disk::BlockDevice
#include <Device.h>

A physical device instance managed by an MSC interface.

Public Functions

inline virtual Type getType() const override

Obtain device type.

virtual String getName() const override

Obtain unique device name.

virtual uint32_t getId() const override

Obtain device ID.

Return values:

uint32_t – typically flash chip ID

class Device : public USB::DeviceInterface
#include <Device.h>

Public Functions

inline DeviceInterface(uint8_t instance, const char *name)

Constructor.

Parameters:
  • instance – TinyUSB instance or index (class-specific)

  • name – Declared name for this interface instance

struct Inquiry
#include <HostDevice.h>

Information provided by SCSI inquiry operation.

class HostDevice : public USB::HostInterface
#include <HostDevice.h>

A USB mass storage device supports one or more logical units, each of which is a physical storage device.

Public Types

using EnumCallback = Delegate<bool(LogicalUnit &unit, const Inquiry &inquiry)>

Callback passed to enumerate() method.

Param unit:

The logical unit attached to a device

Param inquiry:

Detailed information

Retval bool:

true to continue enumeration, false to stop

Public Functions

virtual void end()

Called when device is disconnected. Override as required.

bool enumerate(EnumCallback callback)

Enumerate all logical units managed by this device.

Parameters:

callback – Invoked for each discovered Logical Unit

inline LogicalUnit *operator[](unsigned lun) const

Access a specific logical unit by number.

Parameters:

lun – The logical Unit Number

Return values:

LogicalUnit* – The corresponding LU, or nullptr if invalid

inline size_t getSectorSize(uint8_t lun) const

Get the declared block/sector size for a unit.

Parameters:

lun – The logical Unit Number

Return values:

size_t – Block size in bytes, or 0 if invalid

inline storage_size_t getSectorCount(uint8_t lun) const

Get the number of blocks/sectors for a unit.

Parameters:

lun – The logical Unit Number

Return values:

size_t – Number of blocks, 0 if invalid

bool read_sectors(uint8_t lun, uint32_t lba, void *dst, size_t size)

Read data from a unit.

Parameters:
  • lun – The logical Unit Number

  • lba – Starting Logical Block Address

  • dst – Buffer to store data

  • size – Number of sectors to read

Return values:

bool – true on success

bool write_sectors(uint8_t lun, uint32_t lba, const void *src, size_t size)

Write data to a unit.

Parameters:
  • lun – The logical Unit Number

  • lba – Starting Logical Block Address

  • src – Data to write

  • size – Number of sectors to write

Return values:

bool – true on success

bool wait()

Wait for all outstanding operations to complete.

Write operations are asynchronous so calling this method ensures that the operation has completed.

Parameters:

lun – The logical Unit Number

Return values:

bool – false on error (e.g. device forceably disconnected)

namespace NCM
class Device : public USB::DeviceInterface
#include <Device.h>

Not currently implemented.

namespace VENDOR

Typedefs

using MountCallback = Delegate<HostDevice*(const HostInterface::Instance &inst, const HostDevice::Config &cfg)>

Application callback to notify connection of a new device.

Param inst:

TinyUSB device instance

Param cfg:

HostDevice configuration

Retval HostDevice*:

Application returns pointer to implementation, or nullptr to ignore this device

using UnmountCallback = Delegate<void(HostDevice &dev)>

Application callback to notify disconnection of a device.

Param dev:

The device which has been disconnected

Functions

void onMount(MountCallback callback)

Application should call this method to receive device connection notifications.

Parameters:

callback

void onUnmount(UnmountCallback callback)

Application should call this method to receive device disconnection notifications.

Parameters:

callback

class Device : public USB::DeviceInterface, public USB::CDC::UsbSerial
#include <Device.h>

The TinyUSB vendor API is very much like a serial port. Each instance corresponds to a bi-directional interface.

Public Functions

inline virtual size_t setRxBufferSize(size_t size) override

Sets receiving buffer size.

Parameters:

size – requested size

Return values:

size_t – actual size

inline virtual size_t setTxBufferSize(size_t size) override

Sets transmit buffer size.

Parameters:

size – requested size

Return values:

size_t – actual size

inline virtual int available() override

Return the total length of the stream.

Return values:

int – -1 is returned when the size cannot be determined

inline virtual bool isFinished() override

Check if all data has been read.

Return values:

bool – True on success.

inline virtual int read() override

Read one character and moves the stream pointer.

Return values:

The – character that was read or -1 if none is available

inline virtual size_t readBytes(char *buffer, size_t length) override

Read chars from stream into buffer.

Terminates if length characters have been read or timeout (see setTimeout). Returns the number of characters placed in the buffer (0 means no valid data found).

Note

Inherited classes may provide more efficient implementations without timeout.

inline virtual int peek() override

Read a character without advancing the stream pointer.

Return values:

int – The character that was read or -1 if none is available

inline virtual void clear(SerialMode mode = SERIAL_FULL) override

Clear the serial port transmit/receive buffers.

Note

All un-read buffered data is removed and any error condition cleared

Parameters:

mode – Whether to flush TX, RX or both (the default)

virtual size_t write(const uint8_t *buffer, size_t size) override

Write chars to stream.

Note

Although this is defined in the Print class, ReadWriteStream uses this as the core output method so descendants are required to implement it

Parameters:
  • buffer – Pointer to buffer to write to the stream

  • size – Quantity of chars to write

Return values:

size_t – Quantity of chars written to stream

class HostDevice : public USB::HostInterface
#include <HostDevice.h>

Base class to use for custom devices.

Public Functions

inline virtual void end() override

Called when device is disconnected. Override as required.

virtual bool setConfig(uint8_t itf_num) = 0

Set active configuration.

Implementations typically start communicating.

virtual bool transferComplete(const Transfer &txfr) = 0

Called when a non-control USB transfer has completed.

struct Config
#include <HostDevice.h>

Device configuration received during mount procedure.

Public Members

uint16_t vid

Vendor ID.

uint16_t pid

Product ID.

DescriptorList list

Interface descriptor list.

struct Transfer
#include <HostDevice.h>

Structure passed to ‘transferComplete’ method.

References
Used by
Environment Variables
SoC support
  • esp32s2

  • esp32s3

  • host

  • rp2040

Submodule: `tinyusb <>`__

Ultrasonic

Tested on modules:

  • HC-SR04 - ranges: 2-400cm, power: 5v, levels: TTL, for work with 3.3v need voltage divider on ECHO pin

  • US-100 - power: 3.3v-5v, temp. compensation

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

VT100 Emulator

This is a vt100 emulator written for devices with under 4kb of ram (for example the ATMega microcontroller). The emulator also uses ili9340 display by default, but can be compiled to use another display with only a few changes to the source code.

Note that because of the small size of the available memory and the slow speed of the microcontroller, it is a little tricky to implement all the escape sequences that are supported in vt100.

VT100 Emulator Demo
Examples
  • see demo.cpp for code that tests the terminal

  • Note: since vt100 is 80x24 lines, and our terminal only supports 40x40 lines, you need to run “stty cols 40 rows 40” command to tell terminal programs that only 40 columns are available.

Compiling

Don’t be put off by cmake. The CMakeLists.txt file is provided for convenience only. You can basically just compile using:

  • avr-gcc -O3 -std=c99 -mmcu=atmega328p -DF_CPU=16000000UL -c ili9340.c uart.c vt100.c

  • avr-g++ -O3 -std=c++11 -mmcu=atmega328p -DF_CPU=16000000UL -o demo.elf demo.cpp ili9340.o uart.o vt100.o

Compatibility

The aim of this project is to be a vt100 compliant terminal. However, at the moment it can not really be called a vt100 terminal because it is far from complete in terms of supporting all of the vt100 terminal sequences. But I do support a few and doing normal shell stuff works fine. But some functions require some more hacking to implement with only 2kb of ram (such as cursor blink and character deletion, to name a few).

Nonetheless, I’m proud to have come this far with it.

The driver currently supports the following escape sequences:

                 /======================================================\
                 |  VT100 Escape sequences supported by the this driver |
                 \======================================================/

- ESC is the escape character "\e" or "\x1b"
- (yes) - the driver supports the sequence
- (no) - the driver currently does not support the sequence
- (?) - do we really need this function? Or: what does this do exactly?

VT52 Compatible Mode
--------------------

- (yes) ESC A           Cursor up with scroll
- (yes) ESC B           Cursor down with scroll
- (yes) ESC C           Cursor right
- (yes) ESC D           Cursor left
- (?)   ESC F           Special graphics character set
- (?)   ESC G           Select ASCII character set
- (yes) ESC H           Cursor to home (but margins currently not supported!)
- (?) ESC I                 Reverse line feed
- (yes) ESC J           Erase to end of screen
- (yes) ESC K           Erase to end of line
- (no) ESC Ylc          Direct cursor address (See note 1)
- (no) ESC Z            Identify (See note 2)
- (?) ESC =             Enter alternate keypad mode
- (?) ESC >             Exit alternate keypad mode
- (?) ESC 1             Graphics processor on (See note 3)
- (?) ESC 2             Graphics processor off (See note 3)
- (?) ESC <             Enter ANSI mode

ANSI Compatible Mode
--------------------

- (yes) ESC [ Pn A    Cursor up Pn lines (without scroll)
- (yes) ESC [ Pn B    Cursor down Pn lines
- (yes) ESC [ Pn C    Cursor forward Pn characters (right)
- (yes) ESC [ Pn D    Cursor backward Pn characters (left)
- (yes) ESC [ Pl;PcH  Direct cursor addressing, where Pl is line#, Pc is column#
- NOTE: proper action is to move relative to margins. We don't support margins yet so the moves are absolute screen based character coordinates.
- (yes) ESC [ Pl;Pcf  Same as above
- (yes) ESC D         Index - moves cursor one line down and performs scroll if at bottom
- (yes) ESC M         Reverse index - moves cursor up (with eventual scroll)
- (yes) ESC 7         Save cursor and attributes
- (yes) ESC 8         Restore cursor and attributes

- (?) ESC #3          Change this line to double-height top half
- (?) ESC #4          Change this line to double-height bottom half
- (?) ESC #5          Change this line to single-width single-height
- (?) ESC #6          Change this line to double-width single-height

- (yes) ESC [ Ps..Ps m
                                Ps refers to selective parameter. Multiple parameters are

                                Text attributes
                                0   All attributes off
                                1   Bold on
                                4   Underscore (on monochrome display adapter only)
                                5   Blink on
                                7   Reverse video on
                                8   Concealed on

                                Foreground colors
                                30  Black
                                31  Red
                                32  Green
                                33  Yellow
                                34  Blue
                                35  Magenta
                                36  Cyan
                                37  White

                                Background colors
                                40  Black
                                41  Red
                                42  Green
                                43  Yellow
                                44  Blue
                                45  Magenta
                                46  Cyan
                                47  White

- (yes) ESC [ K   Erase from cursor to end of line
- (yes) ESC [ 0K        Same
- (yes) ESC [ 1K        Erase from beginning of line to cursor
- (yes) ESC [ 2K        Erase line containing cursor
- (yes) ESC [ J         Erase from cursor to end of screen
- (yes) ESC [ 0J        Same
- (yes) ESC [ 2J        Erase entire screen

- (?) ESC [ Ps..Ps q  Programmable LEDs: Ps are selective parameters separated by
                                semicolons (073 octal) and executed in order, as follows:

                                0 or None               All LEDs off
                                1                       L1 On
                                2                       L2 On
                                3                       L3 On
                                4                       L4 On

G0 designator   G1 designator           Character set

- (?) ESC ( A         ESC ) A                 United Kingdom (UK)
- (?) ESC ( B         ESC ) B                 United States (USASCII)
- (?) ESC ( 0         ESC ) 0                 Special graphics/line drawing set
- (?) ESC ( 1         ESC ) 1                 Alternative character ROM
- (?) ESC ( 2         ESC ) 2                 Alternative graphic ROM

- (?) ESC K Pt;Pb r   Set top scrolling window (Pt) and bottom scrolling window
                                (Pb). Pb must be greater than Pb.

- (no) ESC H           Set tab at current column
- (no) ESC [ g         Clear tab at current column
- (no) ESC [ 0g        Same
- (no) ESC [ 3g        Clear all tabs

Modes
-----

The following commands are currently not supported.

Mode Name       Mode            To set seq      Mode            To reset seq

- (no)  Line feed/new   New line        ESC [20h        Line feed       ESC [20l
- (no)  Cursor key      Application     ESC [?1h        Cursor          ESC [?1l
- (no)  ANSI/VT52       ANSI            n/a             VT52            ESC [?2l
- (no)  Column mode     132 col         ESC [?3h        80 col          ESC [?3l
- (no)  Scrolling       Smooth          ESC [?4h        Jump            ESC [?4l
- (no)  Screen mode     Reverse         ESC [?5h        Normal          ESC [?5l
- (yes) Origin mode     Relative        ESC [?6h        Absolute        ESC [?6l
- (yes) Wraparound      On              ESC [?7h        Off             ESC [?7l
- (?)   Autorepeat      On              ESC [?8h        Off             ESC [?8l
- (?)   Interface       On              ESC [?9h        Off             ESC [?9l

Reports
-------

- (no) ESC [ 6n        Cursor position report
- (no) ESC [ Pl;PcR            (response; Pl=line#; Pc=column#)
- (no) ESC [ 5n        Status report
- (no) ESC [ c                 (response; terminal Ok)
- (no) ESC [ 0c                (response; teminal not Ok)
- (no) ESC [ c         What are you?
- (no) ESC [ 0c        Same
- (no) ESC [?1;Ps c            response; where Ps is option present:

                                                0               Base VT100, no options
                                                1               Preprocessor option (STP)
                                                2               Advanced video option (AVO)
                                                3               AVO and STP
                                                4               Graphics processor option (GO)
                                                5               GO and STP
                                                6               GO and AVO
                                                7               GO, STP, and AVO

- (no) ESC c           Causes power-up reset routine to be executed
- (no) ESC #8          Fill screen with "E"
- (no) ESC [ 2;Ps y    Invoke Test(s), where Ps is a decimal computed by adding the
                                numbers of the desired tests to be executed:

                                                1               Power up test
                                                2               Data loop back
                                                4               EIA modem control signal test
                                                8               Repeat test(s) indefinitely




TERMINAL COMMANDS
----------------

- (yes) ESC c       Reset
- (no)  [ ! p       Soft Reset
- (yes) # 8     Fill Screen with E's
- (no)  } 1 *       Fill screen with * test
- (no)  } 2     Video attribute test display
- (no)  } 3     Character sets display test

KEYBOARD COMMANDS
-----------------

- (?) [ 2 h     Keyboard locked
- (?) [ 2 l     Keyboard unlocked
- (?) [ ? 8 h       Autorepeat ON
- (?) [ ? 8 l       Autorepeat OFF
- (?) [ 0 q     Lights all off on keyboard
- (?) [ * q     Light * on

PROGRAMMABLE KEY COMMANDS
-------------------------

- (?) ! pk      Program a programmable key (local)
- (?) @ pk      Program a programmable key (on-line)
- (?) % pk      Transmit programmable key contents

SCREEN FORMAT (not supported)
-------------

- (?) [ ? 3h        132 Characters on
- (?) [ ? 3l        80 Characters on
- (?) [ ? 4h        Smooth Scroll on
- (?) [ ? 4l        Jump Scroll on
- (?) [ *t ; *b r   Scrolling region selected, line *t to *b
- (?) [ ? 5 h       Inverse video on
- (?) [ ? 5 l       Normal video off
- (?) [ ? 7 h       Wraparound ON
- (?) [ ? 7 l       Wraparound OFF
- (?) [ ? 75 h  Screen display ON
- (?) [ ? 75 l  Screen display OFF

CHARACTER SETS AND LABELS
-------------------------

- (?) ( A       British
- (?) ( B       North American ASCII set
- (?) ( C       Finnish
- (?) ( E       Danish or Norwegian
- (?) ( H       Swedish
- (?) ( K       German
- (?) ( Q       French Canadian
- (?) ( R       Flemish or French/Belgian
- (?) ( Y       Italian
- (?) ( Z       Spanish
- (?) ( 0       Line Drawing
- (?) ( 1       Alternative Character
- (?) ( 2       Alternative Line drawing
- (?) ( 4       Dutch
- (?) ( 5       Finnish
- (?) ( 6       Danish or Norwegian
- (?) ( 7       Swedish
- (?) ( =       Swiss (French or German)

[Note all ( may be replaced with )]


ATTRIBUTES AND FIELDS
-------

- (?) [ 0 m     Clear all character attributes
- (?) [ 1 m     Alternate Intensity ON
- (?) [ 4 m     Underline ON
- (?) [ 5 m     Blink ON
- (?) [ 7 m     Inverse video ON
- (?) [ 22 m        Alternate Intensity OFF
- (?) [ 24 m        Underline OFF
- (?) [ 25 m        Blink OFF
- (?) [ 27 m        Inverse Video OFF
- (?) [ 0 }     Protected fields OFF
- (?) [ 1 }         Protected = Alternate Intensity
- (?) [ 4 }     Protected = Underline
- (?) [ 5 }     Protected = Blinking
- (?) [ 7 }     Protected = Inverse
- (?) [ 254 }       Protected = All attributes OFF

CURSOR COMMANDS
-------

- (?) [ ? 25 l  Cursor OFF
- (?) [ ? 25 h  Cursor ON
- (?) [ ? 50 l  Cursor OFF
- (?) [ ? 50 h  Cursor ON
- (yes) 7       Save cursor position and character attributes
- (yes) 8       Restore cursor position and character attributes
- (yes) D       Line feed
- (yes) E       Carriage return and line feed
- (yes) M       Reverse Line feed
- (yes) [ A     Cursor up one line
- (yes) [ B     Cursor down one line
- (yes) [ C     Cursor right one column
- (yes) [ D     Cursor left one column
- (yes) [ * A       Cursor up * lines
- (yes) [ * B       Cursor down * lines
- (yes) [ * C       Cursor right * columns
- (yes) [ * D       Cursor left * columns
- (yes) [ H     Cursor home
- (yes) [ *l ; *c H Move cursor to line *l, column *c
- (yes) [ *l ; *c f Move curosr to line *l, column *c
- (no) Y nl nc  Direct cursor addressing (line/column number)
- (no) H        Tab set at present cursor position
- (no) [ 0 g        Clear tab at present cursor position
- (no) [ 3 g        Clear all tabs

EDIT COMMANDS
-------

- (no) [ 4 h        Insert mode selected
- (no) [ 4 l        Replacement mode selected
- (no) [ ? 14 h Immediate operation of ENTER key
- (no) [ ? 14 l Deferred operation of ENTER key
- (no) [ ? 16 h Edit selection immediate
- (no) [ ? 16 l Edit selection deferred
- (no) [ P      Delete character from cursor position
- (no) [ * P        Delete * chars from curosr right
- (no) [ M      Delete 1 char from cursor position
- (no) [ * M        Delete * lines from cursor line down
- (yes) [ J     Erase screen from cursor to end
- (yes) [ 1 J       Erase beginning of screen to cursor
- (yes) [ 2 J       Erase entire screen but do not move cursor
- (yes) [ K     Erase line from cursor to end
- (yes) [ 1 K       Erase from beginning of line to cursor
- (yes) [ 2 K       Erase entire line but do not move cursor
- (no) [ L      Insert 1 line from cursor position
- (no) [ * L        Insert * lines from cursor position

LICENSE
-------

- Multilicense: GNU GPL free for all, by default.
    For anything else, drop me an email. :)

 .............................................................................
 : INTERNET: info@fortmax.se :: Tel: 0733-387694 Int: +46-733-387694         :
 : AUTHOR: Martin K. Schröder COPYRIGHT: 2014 SWEDEN                                                 :
 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

WS2812 Neopixel

http://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/

References
Used by
SoC support
  • esp8266

WebCam

Introduction

This component provides a webcamera(aka webcam) stream and camera drivers that can help you stream your webcam images as video stream through the embedded web server.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Yeelight

Library to control Yeelight devices via JSON

See https://www.yeelight.com/en_US/activity

Specification: https://www.yeelight.com/download/Yeelight_Inter-Operation_Spec.pdf

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

FAT Filing System

Required by the SDCard library.

http://elm-chan.org/fsw/ff/00index_e.html

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Flatbuffers

FlatBuffers is an efficient cross platform serialization library for C++, C#, C, Go, Java, Kotlin, JavaScript, Lobster, Lua, TypeScript, PHP, Python, Rust and Swift. It was originally created at Google for game development and other performance-critical applications.

It is available as Open Source on GitHub under the Apache license, v2 (see LICENSE.txt).

Using

Step 1. Add these lines to your application componenent.mk file:

COMPONENT_DEPENDS += flatbuffers

Step 2. Add these lines to your application:

#include <flatbuffers/flatbuffers>

Or directly use the header file generated from the flatc compiler.

Step 3. Basic example:

#include "monster.h"

using namespace MyGame::Example;

void init()
{
   Serial.begin(SERIAL_BAUD_RATE);

   // demonstration how to encode data into a flatbuffer
   flatbuffers::FlatBufferBuilder builder;

   auto name = builder.CreateString("Sming Monster");

   MonsterBuilder monster_builder(builder);
   monster_builder.add_name(name);

   auto orc = monster_builder.Finish();

   // Call `Finish()` to instruct the builder that this monster is complete.
   // Note: Regardless of how you created the `orc`, you still need to call
   // `Finish()` on the `FlatBufferBuilder`.
   builder.Finish(orc);

   // and then decode it
   uint8_t *buffer = builder.GetBufferPointer();
   auto monster = GetMonster(buffer);
   Serial.printf("Monster name: %s\n", monster->name()->c_str());
}
Further reading

Take a look at the official flatbuffers tutorial.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: src

JerryScript

A JavaScript Virtual Machine based on JerryScript.

Introduction

This library allows running JavaScript in a sandbox on all architectures supported by Sming. The library uses JerryScript as JavaScript Virtual Machine (VM).

_inc/Sming/Libraries/jerryscript/jerryscript/docs/img/engines_high_level_design.png

The diagram above shows the interactions between the major components of JerryScript: Parser and VM. Parser performs translation of input ECMAScript application into the byte-code. Prepared bytecode is executed by the Virtual Machine that performs interpretation. Source: Official JerryScript site.

To save space and be able to run JerryScript on an embedded device Sming compiles this library without a parser. This means that the JavaScript files have to be compiled before landing on the device. See the samples below to learn more.

Console messages

Two external functions are made available for scripts to use:

  • print for normal console output (shown in AQUA)

  • alert for messages in debug mode (show in RED)

VM integrity

The Jerryscript VM may encounter situations where it cannot continue execution, such as memory allocation failure or invalid bytecode.

Fatal errors cause engine execution to halt. It must be “re-booted” (in a virtual sense). This requires application co-operation as scripts may need to be re-loaded, external functions re-registered, etc.

If intending to run unverified javascript it is especially important to guard all calls into Jerryscript using the library exception handler:

JS_TRY()
{
   // Call into jerryscript engine
}
JS_CATCH()
{
   // Engine has failed
}
JS_TRY:

Place code here which sets up jerryscript values, etc. then calls into Jerryscript. Data is allocated on local stack or in jerryscript engine.

DO NOT perform any system heap allocation here, such as creating or modifying String) objects. If required, instantiate those above.

JS_CATCH:

Jerryscript ust be re-initialised before any further calls.

  • De-allocate any globally held jerryscript values

  • Call JS::cleanup()

  • Call JS::init() and any other necessary initialisation

It is recommended to provide a single function for both initial jerryscript initialisation and re-initialisation.

See the Event_Jsvm sample for a demonstratation.

Note that the VM uses own static heap (sized by JERRY_GLOBAL_HEAP_SIZE) so main system heap is unaffected.

When JERRY_ENABLE_DEBUG is enabled it may not be possible to recover because VM objects may be left with invalid reference counts, for example, which will cause Jerryscript::cleanup() to fail. Applications should generally be built with this setting disabled (the default).

Watchdog

Sming is a single-threaded framework so requires co-operation to perform multi-tasking. This means javascript calls should be brief so as not to disrupt the system.

If a javascript call takes too long then the system watchdog will timeout and reset the system. This is generally not helpful, so a separate watchdog is implemented for the Jerryscript virtual machine.

By default, the watchdog is disabled. To enable it, add a call during initialisation:

// Enable the watchdog with a 100ms timeout (0 to disable it)
JS::Watchdog::setPeriod(100);

Now, if a call takes more than 100ms then a fatal error will be thrown as discussed above.

The setting can be changed at any time and is unaffected by jerryscript engine resets.

External Contexts

By default, a single, global VM context is created in which all code is loaded and executed. If multiple javascript applications are running then if one fails it will take out all the others.

Enabling the JERRY_EXTERNAL_CONTEXT setting allows use of the JS::Context class to run multiple isolated instances. See the Basic Context example for details.

Configuration variables
JERRY_ENABLE_DEBUG

default: 0 (disabled)

Enable assertions and debug messages in jerryscript library. Should be left disabled unless debugging the jerryscript engine itself.

JERRY_MEM_STATS

default: 1 (enabled)

Enable jerryscript heap memory tracking.

JERRY_ERROR_MESSAGES

default: 0 (disabled)

Enable text messages for common errors. Consumes ~2K RAM on ESP8266.

JERRY_COMPACT_PROFILE

default: 1 (enabled)

Compact (minimal profile) compilation profile makes the JerryScript library smaller.

Set to 0 to use es.next profile.

JERRY_PARSER

default: 0 (disabled)

Enable to build library with javascript parser enabled. Required for use of Jerryscript::eval() function.

JERRY_EXTERNAL_CONTEXT

default: 0 (disabled)

Enable this setting to make use of JS::Context to run multiple isolated instances.

JERRY_GLOBAL_HEAP_SIZE

default: 1 (in KB)

Size of the jerryscript global heap in kilobytes. Increase as required.

JERRY_SNAPSHOT_TOOL

Read-only. Path to the snapshot compiler for use by applications.

APP_JS_SOURCE_DIR

default: undefined

Snap files can be created during the build stage by setting this variable in your project’s component.mk file:

APP_JS_SOURCE_DIR := files

All .js files will be compiled into .snap files and written to APP_JS_SNAPDIR.

APP_JS_SNAP_DIR

default: out/jerryscript

Location to write generated .snap files.

APP_JS_SNAP_UPDATED

default: undefined

Set this if action required when snaps are updated, for example to rebuild SPIFFS image or update application.

JERRY_WEB_COMPILER

(read-only)

Location of pre-compiled web compiler. See Advanced-Jsvm sample for example usage.

Credits

The initial work on the JerryScript library for Sming was done as part of the U:Kit project.

Updating JerryScript

Currently we use Jerryscript version v2.4 from JerryScript, imported as a submodule.

The web-assembly (WASM) snapshot compiler uses the emscripten SDK.

Update

In order to update the version of JerryScript two tasks have to be executed:

  • In this repository update the submodule version of JerryScript to point to the new version

  • Rebuild the snapshot compiler

The snapshot compiler can be rebuilt manually as follows:

  1. Install the emscripten SDK:

    git clone --depth 1 https://github.com/emscripten-core/emsdk.git emsdk
    emsdk/emsdk install latest
    emsdk/emsdk activate latest
    
  2. run make from the emscripten-snapshot-compiler directory.

The following three files are generated in emscripten-snapshot-compiler/build/bin/{PROFILE}:

  • jsc.js

  • jsc.wasm

  • jsc.wasm.map

Copy these to the jsc sub-directory for application use as demonstrated in the Advanced_Jsvm sample.

Dockerfile

The snapshot compiler can be rebuilt using a Docker container which also includes a WebAssembly toolkit. It can be built and run with the following commands:

docker build -f Dockerfile -t jerryscript-ems .
docker run -it jerryscript-ems

Inside the container a new jerryscript snapshot compiler can be built by calling:

node /jerryscript/build/bin/minimal/jsc.js -o /tmp/test.js.snap /tmp/test.js

Where test.js.snap is the output file and /tmp/test.js is an existing JavaScript file.

To update the jsc.* files you run the following from the application directory:

docker run -v $(pwd)/files/web/:/web -it jerryscript-ems

And run the following commands inside the container:

cp -r /jerryscript/build/bin/minimal/jsc.* /web

The build system handles compilation of the main.js file, but if you prefer to use pre-compiled snaps this can also be done inside the container:

node /jerryscript/build/bin/minimal/jsc.js -o /web/main.js.snap /web/main.js
API Documentation
Classes and types
enum class Jerryscript::FatalCode

Values:

enumerator XX
enum class Jerryscript::Type

Values:

enumerator XX
enum class Jerryscript::ErrorType

Values:

enumerator XX
enum class Jerryscript::ObjectType

Values:

enumerator XX
enum class Jerryscript::FunctionType

Values:

enumerator XX
enum class Jerryscript::Feature

Values:

enumerator XX
enum class Jerryscript::Ecma

Values:

enumerator XX
using Jerryscript::HeapStats = jerry_heap_stats_t
inline bool Jerryscript::getHeapStats(HeapStats &stats)
size_t Jerryscript::getHeapUsed()
bool Jerryscript::printHeap()
jerry_value_t Jerryscript::create_arg_count_error(const char *functionName)
inline Object Jerryscript::global()

Get global context.

void Jerryscript::initialise(jerry_init_flag_t flags = JERRY_INIT_EMPTY)

Initializes the JavaScript VM.

void Jerryscript::cleanup()

Clean up the virtual machine by unloading snapshots, freeing allocated memory, etc.

Note that this does not release Jerryscript heap memory allocated to existing values. This is done via Value destructor or by manually calling Value::reset().

inline bool Jerryscript::isFeatureEnabled(Feature feature)

Check if optional feature is available.

Parameters:

feature

Return values:

bool – true if library has been compiled with requested feature

Value Jerryscript::eval(const String &jsCode)
inline void Jerryscript::gc(bool maximumEffort = false)

Perform memory garbage collection.

class Context
#include <Context.h>

Jerryscript external context.

Applications may be segregated by running in separate contexts. Each context has its own dynamically allocated heap.

Subclassed by Jerryscript::ContextTemplate< ClassType >

Public Functions

Context()

Create a context using the default JERRY_GLOBAL_HEAP_SIZE setting.

Context(size_t heapSize)

Create a context with custom heap size.

Parameters:

heapSize – Size of heap in bytes. Will be rounded up as necessary.

inline void select()

Make this the current context.

Context must be selected before calling into jerryscript.

template<class ClassType>
class ContextTemplate : public LinkedObjectTemplate<ClassType>, public Jerryscript::Context
#include <Context.h>

Implement a custom Context class.

Public Functions

Context()

Create a context using the default JERRY_GLOBAL_HEAP_SIZE setting.

Context(size_t heapSize)

Create a context with custom heap size.

Parameters:

heapSize – Size of heap in bytes. Will be rounded up as necessary.

template<class ClassType>
class ContextList : public OwnedLinkedObjectListTemplate<ClassType>
#include <Context.h>

Manages a list of contexts.

Public Functions

inline void foreach(Callback callback)

Invoke callback once for each context via task queue.

When calling into contexts we can do this:

JS::ContextList<MyContext> contexts;
...
String str = F("Some text");
int value = 12;
for(auto& ctx: contexts) {
  ctx.customNotify(str, value);
}

However, with many containers system responsiveness may be adversely affected. Instead, separate the calls out like this:

contexts.foreach([=](auto& ctx){
  ctx.customNotify(str, value);
});

Note that all parameters are captured by copy (using [=]) as, for example, str will be destroyed when it goes out of scope.

class Except
#include <Except.h>
struct CallInfo
#include <Function.h>

Maps directly onto jerry_call_info_t structure.

Public Members

Value function

invoked function object

Value this_value

this value passed to the function

Value new_target

current new target value, undefined for non-constructor calls

class Task : public Task
#include <Task.h>

  • Task that runs the loop JavaScript function in the background

Public Functions

inline virtual void loop() override

Inherited classes override this to perform actual work.

struct OwnedValue
#include <Types.h>

Use to initialise Value object by taking ownership of native/raw jerryscript value.

e.g. Value myValue = OwnedValue{raw_value};

This typecast is necessary because jerry_value_t is weakly typed (as uint32_t).

struct CopyValue
#include <Types.h>

Use to initialise Value object by copying native/raw jerryscript value.

e.g. Value myValue = CopyValue{raw_value};

struct StringValue
#include <Types.h>

Use to initialise Value object to a string given a native/raw jerryscript value.

e.g. Value myValue = StringValue{raw_value};

struct Undefined
#include <Types.h>

Use to create Value containing javascript ‘undefined’.

e.g. Value myValue = Undefined{};

struct Null
#include <Types.h>

Use to create Value containing javascript ‘null’.

e.g. Value myValue = Null{};

class Value
#include <Types.h>

Represents a Jerryscript value.

This class resembles std::unique_ptr to manage a raw/native jerry_value_t value. The get(), reset() and release() methods all behave in the same manner.

In addition, it provides intrinsic casts from numeric/boolean C++ types plus String.

You can therefore create a value like this::

Value value = 12;
Value str = "My String";
Value str2 = F("My other String");

If you need to working with floating point values be sure to compile with JERRY_COMPACT_PROFILE=0.

IMPORTANT: When dealing with raw/native values, ALWAYS use either OwnedValue or CopyValue casts.

  • This is correct: Value value = OwnedValue{jerry_create_object()};

  • This is wrong: Value value = jerry_create_object(); // Ends up with memory leak plus garbage

Subclassed by Jerryscript::Error, Jerryscript::ExternalFunction, Jerryscript::Object

Construct a value from a simple type

Value(int value)

Integer.

Value(unsigned value)

Unsigned integer.

inline Value(double value)

floating-point

inline Value(bool value)

Boolean.

inline Value(const String &s)

Wiring String.

inline Value(const char *s)

NUL-terminated ‘C’ string.

inline Value(const FSTR::String &s)

Flash String.

Get raw/native value for use with jerryscript C API

inline const jerry_value_t &get() const

const get()

inline jerry_value_t &get()

get()

Checks on value type

inline bool isCallable() const

Is this object a function? If so, can cast to Callable type.

inline bool isArray() const

Can this object be accessed as an array? If so, can cast to Array type.

inline bool isError() const

Determine if value represents an error. If so, can cast to Error type.

inline bool isEmpty() const

An empty Value contains nothing, i.e. no javascript type has been assigned. This gets interpreted as ‘undefined’ if any attempt is made to use it in javascript.

inline bool isDefined() const

Contains a javascript value, but contents undefined.

inline bool isBoolean() const

A true/false value type.

inline bool isFalse() const

Is this a Boolean type set to False?

inline bool isTrue() const

Is this a Boolean type set to True?

inline bool isNull() const

Is this a NULL value?

inline bool isString() const

Is this a String?

inline bool isObject() const

Is this an Object type? If so, can cast to Object class.

inline bool isNumber() const

Does this value contain a Number?

Public Functions

inline Value()

Construct An empty (unused) value.

inline Value(const OwnedValue &value)

Construct a Value and take ownership of the given native value.

inline Value(const CopyValue &value)

Construct a Value using a copy (or reference to) the given native value.

inline Value(const StringValue &value)

Construct a string Value from the given native value.

inline Value(const Value &value)

Copy constructor.

inline Value(Value &&value)

Move constructor.

inline Value &operator=(const Value &value)

Assignment copy operator.

inline Value &operator=(Value &&value)

Assignment move operator.

inline Value &reset(jerry_value_t value = jerry_value_t(Ecma::VALUE_EMPTY))

Reset contents of object to new value (default is unassigned)

inline jerry_value_t release()

Get raw/native value and release ownership.

inline Type type() const

Get value type.

Object toObject() const

Create a new object from this value.

inline Value toString() const

Create a new string value from this value.

size_t readString(unsigned offset, char *buffer, size_t length) const

Get content from within a string value.

Parameters:
  • offset – First character position to read, starting from 0

  • buffer – Where to store character data

  • length – Number of characters to read

Return values:

size_t – Number of characters read

String subString(unsigned offset, size_t length) const

Get content from within a string value.

Parameters:
  • offset – First character position to read, starting from 0

  • length – Number of characters to read

Return values:

String – Requested range, or nullptr if value is not a string

operator String() const

Support intrinsic cast to Wiring String.

template<typename T>
inline T as() const

Get value of object with specific type. e.g. value.as<int>().

Return value will revert to sensible default if conversion cannot be completed. If in doubt, check type first.

struct As
#include <Types.h>

Used by as() method.

class ExternalFunction : public Jerryscript::Value
#include <Types.h>

Object representing an external function implementation.

class Object : public Jerryscript::Value
#include <Types.h>

Objects support named properties.

Subclassed by Jerryscript::Array, Jerryscript::Callable

Access object properties by name

inline NamedItem operator[](const String &name)

operator[] uses NamedItem proxy object so value can be assigned or read

inline const Value operator[](const String &name) const

const operator[] returns Value directly

inline Value setProperty(const Value &name, const Value &value)

Set a property value.

Parameters:
  • name – Property name

  • valueValue to set

Return values:

Value – true on success, otherwise Error

inline Value getProperty(const Value &name) const

Get a property value.

Parameters:

name – Property name

Return values:

Value – The property value, or Error

inline bool hasProperty(const Value &name) const

Determine if a property exists.

Parameters:

name – Property name

Return values:

bool – true if property exists

inline bool removeProperty(const Value &name)

Remove a property.

Parameters:

name – Property name

Return values:

bool – true on success

Public Functions

inline Object()

Default constructor creates a new, empty object.

Array keys() const

Get list of property names.

Value runFunction(const String &name, Value &arg)

Call a specified JavaScript function with exactly one argument.

Parameters:
  • name – Name of function to run (a property name)

  • arg – The argument

Return values:

Value – Return value from function, or Error

Value runFunction(const String &name, std::initializer_list<Value> args = {})

Call a specified JavaScript function with zero or more arguments.

Parameters:

name – Name of function to run (a property name)

Return values:

Value – Return value from function, or Error

inline bool registerFunction(const String &name, jerry_external_handler_t handler)

Register an external function so it may be called from javascript.

Parameters:
  • name – Name of the function (property name)

  • handler – The function handler, see Function.h for details

  • bool – true on success

inline bool unregisterFunction(const String &name)

Unregister an external function.

Parameters:
  • name – Name of the function

  • bool – true on success

Callable getFunction(const String &name)

Retrieve the given property as a function.

Return values:

The – callable object, or error (property not found or isn’t callable)

inline Value()

Construct An empty (unused) value.

inline Value(const OwnedValue &value)

Construct a Value and take ownership of the given native value.

inline Value(const CopyValue &value)

Construct a Value using a copy (or reference to) the given native value.

inline Value(const StringValue &value)

Construct a string Value from the given native value.

inline Value(const Value &value)

Copy constructor.

inline Value(Value &&value)

Move constructor.

Value(int value)

Integer.

Value(unsigned value)

Unsigned integer.

inline Value(double value)

floating-point

inline Value(bool value)

Boolean.

inline Value(const String &s)

Wiring String.

inline Value(const char *s)

NUL-terminated ‘C’ string.

inline Value(const FSTR::String &s)

Flash String.

struct NamedItem
#include <Types.h>

Iterator and operator[] access uses this wrapper class so items may be written or read.

class Error : public Jerryscript::Value
#include <Types.h>

Error object class.

Subclassed by Jerryscript::ArgumentError

Create an error object

inline Error(ErrorType type)

Error with type only.

inline Error(ErrorType type, const String &message)

Error with type and message.

Public Functions

inline Error(const Value &value)

Copy constructor.

inline Error(Value &&value)

Move constructor.

inline ErrorType errorType() const

Get type of error.

Value message() const

Get error message, if any.

operator String() const

Formulate error message Base operator in Value class returns empty value for errors. To obtain error message, check for isError() then cast to Error():

if (value.isError()) {
  Serial.println(JS::Error(value));
}
inline Value()

Construct An empty (unused) value.

inline Value(const OwnedValue &value)

Construct a Value and take ownership of the given native value.

inline Value(const CopyValue &value)

Construct a Value using a copy (or reference to) the given native value.

inline Value(const StringValue &value)

Construct a string Value from the given native value.

inline Value(const Value &value)

Copy constructor.

inline Value(Value &&value)

Move constructor.

Value(int value)

Integer.

Value(unsigned value)

Unsigned integer.

inline Value(double value)

floating-point

inline Value(bool value)

Boolean.

inline Value(const String &s)

Wiring String.

inline Value(const char *s)

NUL-terminated ‘C’ string.

inline Value(const FSTR::String &s)

Flash String.

class ArgumentError : public Jerryscript::Error
#include <Types.h>

Provides consistent error message when checking external function arguments.

class Array : public Jerryscript::Object
#include <Types.h>

Array objects have properties accessed by index.

Iterator support

inline Iterator begin()

begin

inline Iterator end()

end

Array element/property access by index

inline IndexedItem operator[](unsigned index)

operator[] uses IndexedItem proxy object so value can be assigned or read

inline const Value operator[](unsigned index) const

const operator[] returns value directly

inline Value getProperty(unsigned index) const

Get a property value.

Parameters:

index – Index of property

Return values:

Value – The property value, or Error

inline Value setProperty(unsigned index, const Value &value)

Set a property value.

Parameters:
  • index – Index of property

  • valueValue to set

Return values:

Value – true on success, otherwise Error

Public Functions

inline Array(size_t size)

Create a new, fixed-size array with the given number of elements.

inline size_t count() const

Get number of elements in the array.

inline Object()

Default constructor creates a new, empty object.

struct IndexedItem
#include <Types.h>

Iterator and operator[] access uses this wrapper class so items may be written or read.

class Iterator
#include <Types.h>
class Callable : public Jerryscript::Object
#include <Types.h>

Callable object represent functions.

Public Functions

Value call(const Object &thisValue, const Value &arg)

Call with one argument.

Value call(const Object &thisValue, std::initializer_list<Value> args = {})

Call with zero or multiple arguments.

e.g. call(myObject, {1, 2, 3});

inline FunctionType functionType() const

Get specific type of callable object.

inline Object()

Default constructor creates a new, empty object.

Macros
group jerryscript

Macros to implement methods callable from javascript within a Context class

Must be used inside a class constructed using JS::ContextTemplate.

As with all external functions, must be registered using JS::Object::registerFunction to make available to javascript.

JS_DEFINE_METHOD(method, ...)

Argument list is fixed.

Example:

class MyContext: public JS::ContextTemplate<MyContext>
{
public:
  void init()
  {
    select();
    JS::initialise();
    JS::global().registerFunction(F("myMethod"), myMethod);
  }

protected:
  JS_DEFINE_METHOD(myMethod, 2, JS::Value& arg1, JS::Callable& arg2)
  {
    ...
    return ...;
  }
};
Parameters:
  • method – Name of jerryscript wrapper function

  • ... – Argument definitions

JS_DEFINE_METHOD_VAR(method)

Arguments are passed as array.

Method declaration has the following prototype:

JS::Value js_method(const JS::CallInfo& callInfo, JS::Value args[], unsigned argCount)

Example:

class MyContext: public JS::ContextTemplate<MyContext>
{
public:
  void init()
  {
    select();
    JS::initialise();
    JS::global().registerFunction(F("myMethod"), myMethod);
  }

protected:
  JS_DEFINE_METHOD_VAR(myMethod)
  {
    for(unsigned i = 0; i < argCount; ++i) {
         Serial.print(args[i]);
    }
    return Value();
  }
};
Parameters:
  • method – Name of jerryscript wrapper function

Macros to implement functions callable from javascript

As with all external functions, must be registered using JS::Object::registerFunction to make available to javascript.

Function arguments should be validated, returning JS::Error object on failure.

JS_DEFINE_FUNCTION(func, ...)

Argument list is fixed.

Example:

JS_DEFINE_FUNCTION(myFunction, JS::Value& arg1, JS::Array& arg2)
{
  ...
  return ...;
}
Parameters:
  • func – Name of jerryscript wrapper function

  • ... – Argument definitions

JS_DEFINE_FUNCTION_VAR(func)

Arguments are passed as array.

Example:

JS_DEFINE_FUNCTION_VAR(myFunction)
{
    for(unsigned i = 0; i < argCount; ++i) {
         Serial.print(args[i]);
    }
    return Value();
}
Parameters:
  • func – Name of jerryscript wrapper function

JS_DECLARE_FUNCTION(func)

Declare a function wrapper.

Use in a header file or as forward declaration

Parameters:
  • func – Name of wrapper function

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: `jerryscript <>`__

libsodium

From its documentation: Sodium is a modern, easy-to-use software library for encryption, decryption, signatures, password hashing and more.

Usage

This component integrates libsodium v1.0.18 into Sming. To use it, simply add ARDUINO_LIBRARIES += libsodium to your application’s component.mk and #include <sodium.h> (or one of the more specific headers in sodium/*).

For further information, see libsodiums documentation.

Build Details

To build the library, Sming’s standard component build process is used in favor of libsodium’s original autotools based build process, which is not compatible with the xtensa-lx106-elf architecture. The list of source files, as well as compiler definitions, are hard-coded in component.mk according to the outcomes of (a hacked version of) the configure script.
All optimizations leveraging x86/ARM architecture-specific assembly instructions are disabled and only C reference implementations are used instead. This is true even when compiling for the “Host” architecture. As a side effect, there is no need to invoke ``sodium_init()`` on application startup (which would otherwise detect available CPU features and select optimal implementations accordingly).

Notes on Random Number Generation

By default, the randombytes_... family of functions is hooked up to the ESP8266 hardware random number generator via os_[get_]random, which is also available in the Host emulator. However, due to the lack of documentation, it is unclear if the hardware random number generator provides sufficiently high quality random numbers for cryptographic purposes. Some additional information can be found here. Also note that the host emulator may not use a high-quality random number source and therefore should not be trusted with generating private keys and other sensitive data. Alternatively, libsodium offers the possibility to install a custom random number generator implementation via randombytes_set_implementation(), which is fully controllable by the user.

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: libsodium
Build Status Windows build status Coverity Scan Build Status Azure build status libsodium

Sodium is a new, easy-to-use software library for encryption, decryption, signatures, password hashing and more.

It is a portable, cross-compilable, installable, packageable fork of NaCl, with a compatible API, and an extended API to improve usability even further.

Its goal is to provide all of the core operations needed to build higher-level cryptographic tools.

Sodium supports a variety of compilers and operating systems, including Windows (with MingW or Visual Studio, x86 and x64), iOS, Android, as well as Javascript and Webassembly.

Documentation

The documentation is available on Gitbook and built from the libsodium-doc repository:

Integrity Checking

The integrity checking instructions (including the signing key for libsodium) are available in the installation section of the documentation.

Community

A mailing-list is available to discuss libsodium.

In order to join, just send a random mail to sodium-subscribe {at} pureftpd {dot} org.

License

ISC license.

modbusino RTU Library (modbus slave)

modbusino is lightweight RTU Modbus slave library that supports ‘read holding registers’ and ‘write multiple registers’ functions. Please note that prior to commit 02dff3c (branch Sming, port URL https://github.com/kmihaylov/modbusino) a delay may occur after sending a message (more information can be found in the PR thread #2043, https://github.com/SmingHub/Sming/pull/2043#issuecomment-615945823).

Configuration variables
RS485_RE_PIN

Default: 15

GPIO pin number for RE (Receive-Enable) output.

RS485_TX_LEVEL

Default: HIGH.

Active level for RE pin during transmission.

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: modbusino
Modbusino
Introduction

Modbusino is a ISC licensed library to handle Modbus requests on Arduino (slave).

Features

To keep it simple and to reduce memory consumption, only the two following Modbus functions are supported:

  • read holding registers (0x03)

  • write multiple registers (0x10)

Example
#include <Modbusino.h>

/* Initialize the slave with the ID 1 */
ModbusinoSlave modbusino_slave(1);
/* Allocate a mapping of 10 values */
uint16_t tab_reg[10];

void setup() {
    /* The transfer speed is set to 115200 bauds */
    modbusino_slave.setup(115200);
}

void loop() {
    /* Initialize the first register to have a value to read */
    tab_reg[0] = 0x1234;
    /* Launch Modbus slave loop with:
       - pointer to the mapping
       - max values of mapping */
    modbusino_slave.loop(tab_reg, 10);
}
Contribute

I want to keep this library very basic and small so if you want to contribute:

  1. Check for open issues or open a fresh issue to start a discussion around a feature idea or a bug.

  2. Fork the repository on Github to start making your changes on another branch.

  3. Send a pull request (of your small and atomic changes).

  4. Bug the maintainer if he’s too busy to answer :)

Nano Protocol-Buffer

Introduction

This component adds support for Nano Protocol-Buffer implementation.

Nanopb is a small code-size Protocol Buffers implementation in ansi C. It is especially suitable for use in microcontrollers, but fits any memory restricted system.

C file generation from Proto files

Once this component is installed you can use it to add Nano Protocol-Buffer support to your project and generate C and header files from Proto files. One possible way to call the generator is to go to the directory where the proto file is located and run the generator. As shown below:

make -C $SMING_HOME fetch nano-pb
cd <folder-with-proto-file>
python $SMING_HOME/Libraries/nanopb/nanopb/generator/nanopb_generator.py <desired-proto-file>.proto

After the generator tool is run you will have newly generated C and header files that can be used in your Sming application.

Using
  1. Add COMPONENT_DEPENDS += nanopb to your application componenent.mk file.

  2. Add these lines to your application:

    #include <PbUtils.h>
    // The line below should be replaced with the generated header file
    #include "cast_channel.pb.h"
    
  3. Example:

    #include <PbUtils.h>
    #include "cast_channel.pb.h"
    
    void doSomething(const uint8_t* data, size_t length)
    {
       // ...
    
       extensions_api_cast_channel_CastMessage message = extensions_api_cast_channel_CastMessage_init_default;
    
       message.protocol_version = extensions_api_cast_channel_CastMessage_ProtocolVersion_CASTV2_1_0;
       message.source_id.funcs.encode = &pbEncodeData;
       message.source_id.arg = new PbData(sourceId);
       message.destination_id.funcs.encode = &pbEncodeData;
       message.destination_id.arg = new PbData(destinationId);
       message.nameSpace.funcs.encode = &pbEncodeData;
       message.nameSpace.arg = new PbData(ns);
       message.payload_type = extensions_api_cast_channel_CastMessage_PayloadType_STRING;
       message.payload_utf8.funcs.encode = &pbEncodeData;
       message.payload_utf8.arg = new PbData((uint8_t*)data, length);
       // ...
    }
    
References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Submodule: nanopb
Nanopb - Protocol Buffers for Embedded Systems
Build Status

Nanopb is a small code-size Protocol Buffers implementation in ansi C. It is especially suitable for use in microcontrollers, but fits any memory restricted system.

Using the nanopb library

To use the nanopb library, you need to do two things:

  1. Compile your .proto files for nanopb, using protoc.

  2. Include pb_encode.c, pb_decode.c and pb_common.c in your project.

The easiest way to get started is to study the project in “examples/simple”. It contains a Makefile, which should work directly under most Linux systems. However, for any other kind of build system, see the manual steps in README.txt in that folder.

Generating the headers

Protocol Buffers messages are defined in a .proto file, which follows a standard format that is compatible with all Protocol Buffers libraries. To use it with nanopb, you need to generate .pb.c and .pb.h files from it:

python generator/nanopb_generator.py myprotocol.proto  # For source checkout
generator-bin/nanopb_generator myprotocol.proto        # For binary package

(Note: For instructions for nanopb-0.3.9.x and older, see the documentation of that particular version here)

The binary packages for Windows, Linux and Mac OS X should contain all necessary dependencies, including Python, python-protobuf library and protoc. If you are using a git checkout or a plain source distribution, you will need to install Python separately. Once you have Python, you can install the other dependencies with pip install protobuf grpcio-tools.

You can further customize the header generation by creating an .options file. See documentation for details.

Running the tests

If you want to perform further development of the nanopb core, or to verify its functionality using your compiler and platform, you’ll want to run the test suite. The build rules for the test suite are implemented using Scons, so you need to have that installed (ex: sudo apt install scons or pip install scons). To run the tests:

cd tests
scons

This will show the progress of various test cases. If the output does not end in an error, the test cases were successful.

Note: Mac OS X by default aliases ‘clang’ as ‘gcc’, while not actually supporting the same command line options as gcc does. To run tests on Mac OS X, use: scons CC=clang CXX=clang. Same way can be used to run tests with different compilers on any platform.

For embedded platforms, there is currently support for running the tests on STM32 discovery board and simavr AVR simulator. Use scons PLATFORM=STM32 and scons PLATFORM=AVR to run these tests.

Build systems and integration

Nanopb C code itself is designed to be portable and easy to build on any platform. Often the bigger hurdle is running the generator which takes in the .proto files and outputs .pb.c definitions.

There exist build rules for several systems:

And also integration to platform interfaces:

Building nanopb - Using vcpkg

You can download and install nanopb using the vcpkg dependency manager:

git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install nanopb

The nanopb port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please create an issue or pull request on the vcpkg repository.

rBPF Femto-Container support

rBPF: RIOT-style Berkeley Packet Filters

The rBPF subsystem provides a minimal virtual machine for microcontrollers. This allows code to be run in isolated environments to increase platform security.

The particular implementation here is referred to as a Femto-Container as only a very limited set of functionality is supported compared to a regular Virtual Machine.

Container code is compiled into BPF object code, which is linked into the Sming application as a BLOB (Binary Large OBject). The generated code has a very simple 64-bit instruction set which is then executed using a runtime interpreter.

See rBPF: Riot-style Berkeley Packet Filters for further details.

Toolchain setup

The following applications are required:

From a sample or project directory, install as follows.

Ubuntu:

sudo apt-get install clang
make python-requirements

Windows:

winget install llvm
set CLANG="c:\Program Files\LLVM\bin\clang"
make python-requirements
Sming interface

All .c or .cpp files are compiled into eBPF object code. The resulting binary data is linked into the project using FSTR::Array objects. These can be executed using a rBPF::VirtualMachine instance.

Compiled containers are located in the :cpp:namespace:`rBPF::Container` namespace.

Note that parameters are passed to container functions as a pointer. You should always use a structured type for this as shown in the samples.

The compiler will use the first public function in each source file as the entry point. It is recommended that all other functions be declared static or placed within an anonymous namespace.

Low-level details

Clang is used to compile container source code to an eBPF object file. This is then converted to a Femto-Container-specific format using python.

For example, the increment.c container is compiled to increment.o then converted to increment.bin. The .o file can be inspected using standard binutils tools such as objdump.

The Femto-Container application binary can be inspected as follows:

make rbpf-dump

Output from the sample increment container looks like this:

Magic:          0x46504272
Version:        0
flags:  0x0
Data length:    0 B
RoData length:  16 B
Text length:    24 B
No. functions:  1

functions:
        "increment": 0x0

data:

rodata:
    0: 0x69 0x6e 0x63 0x72 0x65 0x6d 0x65 0x6e
    8: 0x74 0x00 0x00 0x00 0x00 0x00 0x00 0x00

text:
<increment>
    0x0:        79 10 00 00 00 00 00 00 r0 = *(uint64_t*)(r1 + 0)
    0x8:        07 00 00 00 01 00 00 00 r0 += 1
   0x10:        95 00 00 00 00 00 00 00 Return r0

This shows the:

  • application header

  • list of functions

  • read-only data containing the function name and some padding

  • the application code

The application code fetches the value from the pointer in r1 (the context argument) and increments the value in the second instruction. The return parameter is stored in register r0.

Build variables
RBPF_CONTAINER_PATH

default: container

Location of Femto-Container applications. Place all .c and .cpp source modules here.

BPF_STORE_NUM_VALUES

default: 16

Maximum number of stored values.

Space is shared between all stores (global and local).

API Documentation
namespace rBPF

Functions

String getErrorString(int error)

Get text for an error code.

class LocalStore : public rBPF::Store, public rBPF::Store
#include <Store.h>

Public Functions

inline virtual bool update(Key key, Value value) override

Update value in store.

Parameters:
  • key

  • value

Return values:

bool – true on success, false if store is full

inline virtual bool fetch(Key key, Value &value) override

Fetch value from store.

If key is not found in the store then its added and set to 0.

Parameters:
  • key

  • value

Return values:

bool – true on success, false if store is full

virtual bool update(Key key, Value value) override

Update value in store.

Parameters:
  • key

  • value

Return values:

bool – true on success, false if store is full

virtual bool fetch(Key key, Value &value) override

Fetch value from store.

If key is not found in the store then its added and set to 0.

Parameters:
  • key

  • value

Return values:

bool – true on success, false if store is full

class GlobalStore : public rBPF::Store, public rBPF::Store
#include <Store.h>

Public Functions

inline virtual bool update(Key key, Value value) override

Update value in store.

Parameters:
  • key

  • value

Return values:

bool – true on success, false if store is full

inline virtual bool fetch(Key key, Value &value) override

Fetch value from store.

If key is not found in the store then its added and set to 0.

Parameters:
  • key

  • value

Return values:

bool – true on success, false if store is full

virtual bool update(Key key, Value value) override

Update value in store.

Parameters:
  • key

  • value

Return values:

bool – true on success, false if store is full

virtual bool fetch(Key key, Value &value) override

Fetch value from store.

If key is not found in the store then its added and set to 0.

Parameters:
  • key

  • value

Return values:

bool – true on success, false if store is full

class Store
#include <Store.h>

Subclassed by rBPF::GlobalStore, rBPF::GlobalStore, rBPF::LocalStore, rBPF::LocalStore

Public Functions

virtual bool update(Key key, Value value) = 0

Update value in store.

Parameters:
  • key

  • value

Return values:

bool – true on success, false if store is full

virtual bool fetch(Key key, Value &value) = 0

Fetch value from store.

If key is not found in the store then its added and set to 0.

Parameters:
  • key

  • value

Return values:

bool – true on success, false if store is full

inline Value get(Key key)

Fetch value from store.

Parameters:

key

class Entry
#include <Store.h>
class VirtualMachine
#include <VirtualMachine.h>

Create a VM and load a container

param container:

Container code blob

bool load(const Container &container, size_t stackSize = defaultStackSize)

Load container and initialise it.

Return values:

bool – true on success

void unload()

Unload container and free any allocated resources.

Run the container

param ctx:

IN/OUT Passed to container. Must be persistent.

retval int64_t:

Result returned from container

Public Functions

VirtualMachine()

Create an uninitialised VM.

inline int getLastError() const

Get error code from last call to execute()

Return values:

int – 0 on success. Retrieve text for error code using getErrorString()

References
Used by
Environment Variables
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

Si4432 RF Transceiver

SI4432 library for Arduino - v0.1

Please note that Library uses standard SS pin for NSEL pin on the chip. This is 53 for Mega, 10 for Uno.

made by Ahmet (theGanymedes) Ipkin (2014)

https://github.com/ADiea/si4432.git

References
Used by
SoC support
  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040

API Documentation

This is a separate section built using DOXYGEN, which will replace this file when built.

Information

Developing for Sming

Contributing to Sming Framework

All contributions (PRs) to Sming Framework have to be done to the develop branch.

master: Branch that contains latest production (stable) release. No PRs other than Final Release updates will be merged into master. develop: Main development branch: contains all current new features.

This means that all contributors will have to submit a PR to a develop , it will be tested and then merged to develop for automated integration testing (via TravisCI, Jenkins or SemaphoreCI), as well as manual testing on a real device.

Sming Contributing flow:

  1. Fork *Sming* repo

    After that clone your own fork.

    git clone https://github.com/<my-fork>/Sming.git

  2. Create a new branch off the develop branch

    cd Sming
    git checkout develop
    git branch feature/<short-explanation>
    git checkout feature/<short-explanation>
    

    Make sure to replace short-explanation with one or two words describing the purpose of the feature. If you want to commit a fix use fix/ as prefix for your branch name.

  3. Build, test your code

    Make sure that your code compiles and it is tested on real device. Sometimes you will be asked for a proof (usually screenshot) that it was tested on your device(s).

  4. Document your code

    As a bare minimum, please include a README.rst or README.md file. See Documentation System for further information.

  5. Commit changes

    git add <changed-files>
    git commit -m "<Description of the change(s)>"
    
  6. Push your changes to your fork on github

    git push
    
  7. Rebase if needed

    If your branch cannot be merged automatically because there are new changes in the official develop branch that conflict with yours then make sure to rebase your branch. The following steps can help you do this.

    • First step: You will need to add the upstream repository. This step should be executed ONLY once.

      cd Sming
      git remote add upstream https://github.com/SmingHub/Sming.git
      git fetch upstream develop
      git checkout develop
      git reset --hard upstream/develop
      
    • Second step: If you have already defined upstream repository and synchronized your develop branch to fetch the updates from upstream ( the commands above do this) the next step is to get the latest changes from the official develop branch.

      This can be done using

      cd Sming
      git checkout develop
      git pull
      
    • Final step: Now you are ready to merge the latest changes from official develop branch into your branch and place your changes on top. The commands below help you achieve this.

      cd Sming
      git checkout develop
      git pull
      git checkout feature/<short-explanation>
      git merge develop
      # Fix any merge conflicts if needed.
      git rebase develop
      # Fix any merge conflicts if needed.
      

      If there were merge conflicts you will have to resolve them locally. This usually involves calling the following commands:

      git mergetool
      # After resolving conflicts you should type the command below to see what are the next steps
      # Newer versions of `git` are kind enough to show hints
      git status
      

      After that operation you should have a branch that has all latest changes from develop with your changes on top.

  8. Submit PR to the main Sming repo, develop branch.

  9. Work with other contributors to test your feature and get it merged to develop

This is the most common approach for a git-flow: http://nvie.com/posts/a-successful-git-branching-model/

Sming Documentation

This directory contains the build system for Sming’s documentation. It is intended to be read online via Read The Docs.

The source directory contains some top-level files however most of the information is obtained from the various README files associated with the source code.

Setup

Linux:

../Tools/install.sh doc

Windows:

..\Tools\install.cmd doc
Building

Build the documentation like this:

make html

This will:

  • Pull in and patch every submodule

  • Generate doxygen API information

  • Build documentation in HTML format

If you make changes to any source documentation files these will be picked up automatically when make html is next run.

If you make any changes to source code comments, you’ll need to re-build the doxygen information first:

make api -B
make html
Component Building Guide
Introduction

This section will take you through creating a Sming Component from start to finish, based on a real example from Pull Request #1992.

The aim here is to make an existing library, https://github.com/nomis/ModbusMaster, available for applications to use within the framework.

Various options and settings are only mentioned here, but are linked through to Sming build system where you can find full details of what they are and how they work.

You will need to be familiar with GIT and the use of submodules.

Structure

You should carefully consider how to incorporate third-party code. The choices are:

  1. Add an existing third-party library directly as a Component, patching if required;

  2. Add the a submodule as a sub-directory within a Component;

  3. Copy code from an existing library (honouring any license agreement conditions) and modify/rewrite as necessary;

  4. Start from scratch. If drawing on ideas from other implementations those should be attributed.

We could use (a), but that is appropriate only for very simple libraries, or those written for Sming but stored in an external repository (e.g. FlashString). You can find more details on this process in Adding External Sources.

Note

Bear in mind that the asynchronous architecture of Sming can be at odds with code written for Arduino. In this example, the main ModbusMasterTransaction code incorporates a loop which effectively halts program execution until a timeout occurs or a response is received. Fixing that could require extensive patching, so option (c) or (d) may be more appropriate.

For now we are using option (b), so we’ll start by creating the Component directory:

cd $SMING_HOME/Libraries
mkdir ModbusMaster
cd ModbusMaster

Note

Do not use $SMING_HOME/Components. This is reserved for framework libraries.

Now add the submodule:

git submodule add --name Libraries.ModbusMaster https://github.com/nomis/ModbusMaster

This does three things:

  1. Clones the external repository to the ModbusMaster sub-directory;

  2. Adds an entry to the .gitmodules file with the URL, local path and specified name;

  3. Stages both changes, with a link indicating which commit should be used.

Note

The dotted name format is a Sming convention.

Before committing, we need to edit the .gitmodules file. Open in a text editor and follow the instructions therein.

You should now re-stage and commit the changes before proceeding.

Patching

Where minor changes are required to third-party code then these can be applied automatically as patches. See Adding External Sources and GIT Submodules for details.

For ModbusMaster, we want to make a slight change to how the timeout is configured, so this is provided in a file called ModbusMaster.patch.

Supported architectures

Unless there are specific reasons not to do so, Components should work on all supported architectures. In particular, it should build and run under the Host Emulator.

In order to do this, you should remove any low-level code from the library by:

  • Using Sming support classes or drivers (see Esp8266 Drivers); or

  • Placing low-level code into separate code modules or header files.

If a Component is intended only to work with specific hardware then ensure appropriate checks are incorporated so that building fails gracefully on un-supported architectures. You can do this in the component.mk file:

ifneq ($(SMING_ARCH),Esp8266)
$(error MyComponent currently only supports the ESP8266 architecture)
endif
Component configuration

For very simple Components the default settings are adequate:

  • Source code must be in the base directory or a sub-directory called src

  • Public header files must be in a sub-directory called include

The source files will be compiled into a library, in this case clib-ModbusMaster.a.

To change the defaults, provide a component.mk file and set entries as required:

Submodules

We need to tell Sming about the submodules:

COMPONENT_SUBMODULES := ModbusMaster
Source code

Put the source code into a separate directory (or directories) and add those to COMPONENT_SRCDIRS.

You can also use COMPONENT_SRCFILES.

For ModbusMaster, all the source code is in the submodule so we set this to COMPONENT_SRCDIRS := ModbusMaster/src.

Header files

Keep public and private .h or .hpp files in separate directories.

Add the public ones to COMPONENT_INCDIRS.

Any private headers can be set in EXTRA_INCDIR.

For ModbusMaster, we set this to COMPONENT_INCDIRS := ModbusMaster/src

Configuration options

ModbusMaster provides the MB_RESPONSE_TIMEOUT variable.

This is mapped onto a #define value with the same name using COMPONENT_CXXFLAGS.

Note: Don’t confuse this with COMPONENT_CFLAGS which is only used when building .c source files.

If the value changes we want Sming to rebuild both the library and any code which uses it, so we assign it to the COMPONENT_VARS variable list. Users can check the value by running make list-config.

Dependencies

If your library depends on other libraries to build, these must be declared in the component.mk file by setting COMPONENT_DEPENDS variable.

ModbusMaster doesn’t have any so this entry is not required.

Documentation

A Component MUST have a README.md (markdown) or README.rst (reStructuredText) file with a level 1 heading and brief introduction at an absolute minimum.

Note

You may not be familiar with .rst files but they are a considerably improvement on markdown and well worth investing a little time to learn.

See Contributing for further details.

Adding External Sources
Introduction

In Sming we have source code from other repositories such as rboot, spiffs, etc.

Having local copies of those modules brings some disadvantages with it:

  1. We cannot easily update the local copy from the original source code.

  2. We cannot easily send improvements to those projects once we make local changes in our local copy.

Sming uses GIT submodules which allows the build system to fetch source code from an external repository on demand.

If modifications are required then the submodule can be patched.

This simplifies the process of integrating changes from those third-party projects.

Where to place external code

Submodules may be a Component by itself (such as FlashString), or contained within a Component (e.g. rBoot).

The location must be chosen carefully:

Code required within the core Sming framework

If the Component supports multiple architectures, place it in Sming/Components. Otherwise, use the appropriate Sming/Arch/*/Components directory.

Code for general use

Create a new Library in Sming/Libraries

Please consult Sming build system for further details about how Components are constructed.

Copying Source Code

If the source code does not have a publicly accessible GIT repository then the source code needs to be copied locally.

In order to track changes more easily, the initial commit should be an exact copy of the original.

Please either comment the code or add notes to the documentation to detail any required changes for compatibility.

Add submodules

As an example, this is how the new PWM submodule was added to the Esp8266 Drivers Component:

  1. Add the submodule using GIT:

    cd $SMING_HOME
    git submodule add \
       --name ESP8266.new-pwm \
       https://github.com/StefanBruens/ESP8266_new_pwm.git \
       Arch/Esp8266/Components/driver/new-pwm
    

This adds an entry to the end of the .gitmodules file:

[submodule "ESP8266.new-pwm"]
   path = Sming/Arch/Esp8266/Components/driver/new-pwm
   url = https://github.com/StefanBruens/ESP8266_new_pwm.git

For naming submodules, please follow the convention used for the other entries in .gitmodules, which is determined by the local path:

-  ``Sming/Components``: just use the name  of the submodule
-  ``Sming/Arch/ARCH/Components``: Use ``ARCH.name``
-  ``Sming/Libraries``: Use ``Libraries.name``
  1. Open .gitmodules in a text editor and:

  1. Move the entry to a more suitable location in the file, i.e. at the end of the section listing all the ESP8266-specific submodules

  2. Add the line ignore = dirty

Applying Patches

If a submodule requires changes to work with Sming, this can be handled using patches.

This is generally done by pulling in the original submodule, making the required changes and then running a diff to create a patch file, like this:

cd <Sming-root-folder>/third-party/<module-name>
git diff --no-ext-diff > <module-name>.patch

If using a GUI such as GIT Extensions then this can be done using a right-click.

See GIT Submodules for further details about how patches are used and where they should be placed.

Using submodules

If the submodule is added as a Component in its own right, no further action is required. Applications can use it by adding the name to their COMPONENT_DEPENDS or ARDUINO_LIBARIES entries in component.mk as appropriate.

Submodules contained within a Component must be declared by adding them to the COMPONENT_SUBMODULES entry in component.mk.

Moving submodules

If you need to change the location of a submodule, here’s a suggested approach. In this example, we’re going to move the Adafruit_Sensor submodule into a Component:

# Move the submodule temporarily
Sming/Libraries$ git mv Adafruit_Sensor tmp
# Create our new Component directory
Sming/Libraries$ mkdir Adafruit_Sensor
# Move the submodule back as a sub-directory
Sming/Libraries$ git mv tmp Adafruit_Sensor/Adafruit_Sensor

Now we can add a component.mk file, README.rst, etc. as required for the component.

Sming build system
Introduction

This guide is provided to assist with understanding, developing and modifying the build system.

A Sming project is built from a set of static libraries (object archives). Typically the application code is one library, built from the user’s source code, whilst the other libraries are common to all projects and stored in a separate, shared location.

Until recently Sming itself has always been built as one large library, but this is now broken into a number of discrete Component libraries. The concept is borrowed from Espressif’s ESP-IDF build system and whilst there are some similarities the two systems are completely independent.

Building applications
Setup

These are the main variables you need to be aware of:

SMING_HOME

must be set to the full path of the Sming directory.

SMING_ARCH

Defines the target architecture

  • Esp8266 The default if not specified. ESP_HOME must also be provided to locate SDK & tools.

  • Esp32 Supports ESP32 architecture.

  • Host builds a version of the library for native host debugging on Linux or Windows

  • Rp2040 Supports Raspberry Pi RP2040-based boards.

SMING_SOC

Some architectures support families of SOCs with different capabilities. Set this value to the specific variant being targeted.

Will automatically set SMING_ARCH to the appropriate value.

SMING_CPP_STD

The build standard applied for the framework.

This defaults to C++17 if the toolchain supports it (GCC 5+), C++11 otherwise. You can override to use other standards, such as c++2a for experimental C++20 support.

These variables are available for application use:

PROJECT_DIR

Path to the project’s root source directory, without trailing path separator. This variable is available within makefiles, but is also provided as a #defined C string to allow references to source files within application code, such as with the IMPORT_FSTR macro.

COMPONENT_PATH As for PROJECT_DIR, but provides the path to the current component’s root source directory.

Converting existing projects

Instead of Makefile-user.mk a project should provide a component.mk. To convert to the new style:

  1. Copy Makefile and component.mk from the Basic_Blink sample project

  2. Copy any customisations from Makefile-user.mk into component.mk file. (Or, rename Makefile-user.mk to component.mk then edit it.)

  3. Delete Makefile-user.mk

  4. If the project uses any Arduino libraries, set the ARDUINO_LIBRARIES variable

Targets You can add your own targets to component.mk as usual. It’s a good idea to add a comment for the target, like this:

##@Building

.PHONY: mytarget
mytarget: ##This is my target

When you type make help it will appear in the list.

If you need a target to be added as a dependency to the main application build, add it to CUSTOM_TARGETS - the Basic Serial sample contains a simple example of this.

ARDUINO_LIBRARIES

If your project uses any Arduino libraries, you must set this value appropriately.

Source files Use COMPONENT_SRCDIRS instead of MODULES. Use COMPONENT_SRCFILES to add individual files.

Include paths Use COMPONENT_INCDIRS instead of EXTRA_INCDIR, unless the paths are only required to build this Component.

See component.mk for a full list of variables.

Building

You should normally work from the project directory. Examples:

  • Type make to build the project and any required Components. To speed things up, use parallel building, e.g. make -j5 will build using a maximum of 5 concurrent jobs. The optimum value for this is usually (CPU CORES + 1). Using make -j will use unlimited jobs, but can cause problems in virtual environments.

  • Type make help from the project directory to get a list of available build targets.

To switch to a different build architecture, for example:

  • Type make SMING_ARCH=Host to build the project for the host emulator

  • Type make flash to copy any SPIFFS image (if enabled) to the virtual flash, and run the application. (Note that you don’t need to set SMING_ARCH again, the value is cached.)

To inspect the current build configuration, type make list-config.

Hardware configuration

The appropriate hardware configuration should be selected in the project’s component.mk file. Use one of the standard configurations or create your own. See Hardware configuration.

Configuration variables

Configuration variables should be set in the project’s component.mk file. If appropriate, they can also be set as environment variables.

During development, the easiest way to change variables is on the make command line. These are cached so persist between make sessions, and will override any values set in your project’s component.mk file. For example:

  • Type make SPIFF_BIN=test-rom to build the project and (if enabled) create a SPIFFS image file called test-rom.bin

  • Type make flash COM_PORT=COM4 to flash the project and test-rom SPIFFS image using the provided flash memory settings

  • Next time you type make flash, the same settings will be used, no need to type them again

A separate cache is maintained for each build type (arch + release/debug). For example:

  • Type make SMING_RELEASE=1 list-config to switch to release build and display the active configuration

Type make config-clean to clear all caches and revert to defaults.

For reference, a copy of all build variables are stored in a file with each firmware image created in the ‘firmware’ directory.

Component repositories

Placing Components in a common location allows them to be used by multiple projects. To set up your own Component repository, create a directory in a suitable location which will contain your Components and set COMPONENT_SEARCH_DIRS to the full path of that directory. For example:

|_ opt/
   |_ shared/
      |_ Components/             The repository
         |_ MyComponent/
         |_ AnotherComponent/
         |_ spiffs/              Will be used instead of Sming version

User repositories are searched first, which allows replacement of any Component for a project. In this example, our spiffs component will be selected instead of the one provided with Sming.

Directory layout

The main Sming repo. is laid out like this:

|_ sming/
   |_ .appveyor.yml              CI testing
   |_ .travis.yml                CI testing
   |_ .readthedocs.yml           Documentation build
   |_ lgtm.yml                   CI Static code analysis
   |_ docs/                      Sming documentation
   |_ samples/                   Samples to demonstrate specific Sming features or libraries
   |_ Sming/
   |  |_ Makefile                Builds documentation, performs global actions on the framework
   |  |_ project.mk              Main makefile to build a project
   |  |_ build.mk                Defines the build environment
   |  |_ component.mk            Sming Component definition file
   |  |_ component-wrapper.mk    Used to build each Component using a separate make instance
   |  |_ Arch/                   Architecture-specific makefiles and code
   |  |  |_ Esp8266/
   |  |  |  |_ sming.mk          Defines architecture-specific Components and libraries
   |  |  |  |_ app.mk            Link the project, create output binaries
   |  |  |  |                       and perform architecture-specific actions
   |  |  |  |_ build.mk          Architecture-specific build definitions, such as compiler paths
   |  |  |  |_ Compiler/
   |  |  |  |_ Components/
   |  |  |  |_ Core/
   |  |  |  |_ Platform/
   |  |  |  |_ System/
   |  |  |  |_ Tools/            Pre-compiled or scripted tools
   |  |  |_ Esp32/
   |  |  |  |_ ...
   |  |  |_ Host/
   |  |     |_ ...
   |  |_ Components/             Framework support code, not to be used directly by applications
   |  |_ Core/                   Main framework core
   |  |_ Libraries/              Arduino Libraries
   |  |  |_ ...
   |  |_ out/                    All generated shared files are written here
   |  |  |_ Esp8266/             The Arch
   |  |  |  |_ debug/            The build type
   |  |  |     |_ build/         Intermediate object files
   |  |  |     |  |_ Lib/        Generated libraries
   |  |  |     |  |_ tools/      Generated tools
   |  |  |     |_ release/
   |  |  |        |_ ...
   |  |  |_ Host/
   |  |     |_ ...
   |  |_ Platform/               System-level classes
   |  |  |_ ...
   |  |_ Services/               Modules not considered as part of Core
   |  |  |_ ...
   |  |_ System/                 Common framework low-level system code
   |  |  |_ include/
   |  |_ Wiring/
   |     |_ ...
   |_ tests/                     Integration test applications
      |_ ...
   |_ Tools/
      |_ ci                      CI testing
      |_ Docker
      |_ ide                     IDE environment support tools
      |_ Python                  Shared python scripts
      |_ travis                  CI testing

A typical Project looks like this:

|_ Basic_Blink/
   |_ Makefile                   Just includes project.mk
   |_ component.mk               Project-specific definitions
    |_ app/                      Default application source directory
    |_ include/                  Default application include directory
    |_ out/                      All generated shared files are written here
       |_ Esp8266/               The Architecture
       |  |_ debug/              The build type
       |  |  |_ build/           Intermediate object files
       |  |  |_ firmware/        Target output files
       |  |  |_ lib/             Generated libraries
       |  |  |_ tools/           Generated tools
       |  |_ release/
       |     |_ ...
       |_ Host
          |_ ...
Component

The purpose of a Component is to encapsulate related elements for selective inclusion in a project, for easy sharing and reuse:

  • Shared Library with associated header files

  • App Code Source files to be compiled directly into the user’s project

  • Header files without any associated source or library

  • Build targets to perform specific actions, such as flashing binary data to hardware

By default, a Component is built into a shared library using any source files found in the base or src directories. All Arduino Libraries are built as Components. Note that the application is also built as a Component library, but the source directory defaults to app instead of src.

Components are referred to simply by name, defined by the directory in which it is stored. The Component itself is located by looking in all the directories listed by COMPONENT_SEARCH_DIRS, which contains a list of repositories. (Every sub-directory of a repository is considered to be a Component.) If there are Components with the same name in different search directories, the first one found will be used.

Components are customised by providing an optional component.mk file.

You can see details of all Components used in a project using make list-components. Add V=1 to get more details.

Note that the application itself is also built as a Component, and may be configured in a similar way to any other Component.

Library variants

Libraries can often be built using different option settings, so a mechanism is required to ensure that libraries (including the application) are rebuilt if those settings change. This is handled using variants, which modifies the library name using a hash of the settings values. Each variant gets its own build sub-directory so incremental building works as usual.

There are several types of config variable:

Variable type

Cached?

Rebuild Component?

Rebuild application ?

Relink application

COMPONENT

Y

Y

Y

Y

CONFIG

Y

N

Y

Y

RELINK

Y

N

N

Y

CACHE

Y

N

N

N

DEBUG

N

N

N

N

Variables are usually defined in the context of a Component, in the component.mk file. All Components see the full configuration during building, not just their own variables.

The type of a configuration variable is defined by adding its name to one of the following lists:

CONFIG_VARS

The Application library derives its variant from these variables. Use this type if the Component doesn’t require a rebuild, but the application does.

COMPONENT_VARS

A Component library derives its variant from these variables. Any variable which requires a rebuild of the Component library itself must be listed. For example, the esp-open-lwip Component defines this as ENABLE_LWIPDEBUG ENABLE_ESPCONN. The default values for these produces ENABLE_LWIPDEBUG=0 ENABLE_ESPCONN=0, which is hashed (using MD5) to produce a46d8c208ee44b1ee06f8e69cfa06773, which is appended to the library name.

All dependent Components (which list this one in COMPONENT_DEPENDS) will also have a variant created.

Behaves just like COMPONENT_VARS except dependent Components are not rebuilt. This is appropriate where the public interface (header files) are not affected by the variable setting, but the library implementation still requires a variant.

Code isn’t re-compiled, but libraries are re-linked and firmware images re-generated if any of these variables are changed. For example, make RBOOT_ROM_0=new-rom-file rewrites the firmware image using the given filename. (Also, as the value is cached, if you then do make flashapp that same image gets flashed.)

CACHE_VARS

These variables have no effect on building, but are cached. Variables such as COM_SPEED_ESPTOOL fall into this category.

DEBUG_VARS

These are generally for information only, and are not cached (except for SMING_ARCH and SMING_RELEASE).

Note that the lists not prefixed COMPONENT_xx are global and so should only be appended, never assigned.

Dependencies

COMPONENT_DEPENDS identifies a list of Components upon which this one depends. These are established as pre-requisites so will trigger a rebuild. In addition, all dependent COMPONENT_VARS are (recursively) used in creation of the library hash.

For example, the axtls-8266 Component declares SSL_DEBUG as a COMPONENT_VAR. Because Sming depends on sming-arch, which in turn depends on axtls-8266, all of these Components get rebuilt as different variants when SSL_DEBUG changes values. The project code (App Component) also gets rebuilt as it implicitly depends on Sming.

GIT Submodules

Sming uses source code from other repositories. Instead of including local copies, these are handled using GIT submodules. Where changes are required, patches may be provided as a diff .patch file and/or set of files to be added/replaced. Only those submodules necessary for a build are pulled in, as follows:

  • The submodule is fetched from its remote repository

  • If a .patch file exists, it is applied

  • Any additional files are copied into the submodule directory

  • An empty .submodule file is created to tells the build system that the submodule is present and correct.

The patch file must have the same name as the submodule, with a .patch extension. It can be located in the submodule’s parent directory:

|_ Components/
   |_ heap/
      |_ .component.mk             Component definition
      |_ umm_malloc.patch          Diff patch file
      |_ umm_malloc/               Submodule directory
         |_ .submodule             Created after successful patching
      ...

However, if the Component is itself a submodule, then patch files must be placed in a ../.patches directory:

|_ Libraries/
   |_ .patches/
   |  |_ Adafruit_SSD1306.patch    Diff patch file
   |  |_ Adafruit_SSD1306/
   |     |_ component.mk           This file is added to submodule
   |_ Adafruit_SSD1306/            The submodule directory
      |_ .submodule                Created after successful patching
      ...

This example includes additional files for the submodule. There are some advantages to this approach:

  1. Don’t need to modify or create .patch

  2. Changes to the file are easier to follow than in a .patch

  3. IMPORTANT Adding a component.mk file in this manner allows the build system to resolve dependencies before any submodules are fetched.

In the above example, the component.mk file defines a dependency on the Adafruit_GFX library, so that will automatically get pulled in as well.

Component configuration

The component.mk is parsed twice, first from the top-level makefile and the second time from the sub-make which does the actual building. A number of variables are used to define behaviour.

These values are for reference only and should not be modified.

COMPONENT_NAME

Name of the Component

COMPONENT_PATH

Base directory path for Component, no trailing path separator

COMPONENT_BUILD_DIR

The current directory.

This should be used if the Component provides any application code or targets to ensure it is built in the correct directory (but not by this makefile).

This value changes depending on the build variant.

COMPONENT_BUILD_BASE

This value does not change with build variant.

If the Component generates source code, for example, it can be placed here (in a sub-directory).

COMPONENT_LIBDIR

Location to store created Component (shared) libraries

COMPONENT_VARIANT

Name of the library to build

COMPONENT_LIBPATH

Full path to the library to be built

These values may be used to customise Component behaviour and may be changed as required.

COMPONENT_LIBNAME

By default, the library has the same name as the Component but can be changed if required. Note that this will be used as the stem for any variants.

Set COMPONENT_LIBNAME := if the Component doesn’t create a library. If you don’t do this, a default library will be built but will be empty if no source files are found.

COMPONENT_TARGETS

Set this to any additional targets to be built as part of the Component, prefixed with $(COMPONENT_RULE).

If targets should be built for each application, use CUSTOM_TARGETS instead. See SPIFFS IFS Library for an example.

COMPONENT_PREREQUISITES

These targets will be built before anything else. If your library generates source code, for example, then it should be done by setting this value to the appropriate targets.

COMPONENT_RULE

This is a special value used to prefix any custom targets which are to be built as part of the Component. The target must be prefixed by $(COMPONENT_RULE) without any space between it and the target. This ensures the rule only gets invoked during a component build, and is ignored by the top-level make.

COMPONENT_SUBMODULES

Relative paths to dependent submodule directories for this Component. These will be fetched/patched automatically before building.

Default behaviour is to initialise submodules recursively. To prevent this behaviour and initialise only the top-level submodule, add a file to the parent directory with the same name as the submodule and a .no-recursive extension.

COMPONENT_SRCDIRS

Locations for source code relative to COMPONENT_PATH (defaults to “. src”)

COMPONENT_SRCFILES

Individual source files. Useful for conditional includes.

COMPONENT_INCDIRS

Default: “include”.

Include directories available when building ALL Components (not just this one). Paths may be relative or absolute

EXTRA_INCDIR

Include directories for just this Component. Paths may be relative or absolute

INCDIR

The resultant set of include directories used to build this Component. Will contain include directories specified by all other Components in the build. May be overridden if required.

COMPONENT_APPCODE

List of directories containing source code to be compiled directly with the application. (Ignore in the project.mk file - use COMPONENT_SRCDIRS instead).

CUSTOM_BUILD

Set to 1 if providing an alternative build method. See Custom building section.

EXTRA_OBJ

Absolute paths to any additional binary object files to be added to the Component archive library.

COMPONENT_DEPENDS

Set to the name(s) of any dependent Components.

EXTRA_LIBS

Set to names of any additional libraries to be linked.

EXTRA_LDFLAGS

Set to any additional flags to be used when linking.

COMPONENT_PYTHON_REQUIREMENTS

If the component requires uncommon Python modules (e. g. as part of a custom build step), set this variable to one or more requirements.txt files. This allows installation of all python requirements of the project by invoking:

make python-requirements [PIP_ARGS=...]

Note

A requirements.txt file in the root directory of the Component is detected automatically without setting this variable. To prevent autodetection (e.g. if the python requirements depend on another configuration variable) you must set this variable to an empty value.

PIP_ARGS

See COMPONENT_PYTHON_REQUIREMENTS.

These values are global so must only be appended to (with +=) , never overwritten.

CUSTOM_TARGETS

Identifies targets to be built along with the application. These will be invoked directly by the top-level make.

GLOBAL_CFLAGS

Use only if you need to provide additional compiler flags to be included when building all Components (including Application) and custom targets.

APP_CFLAGS

Used when building application and custom targets.

COMPONENT_CFLAGS

Will be visible ONLY to C code within the component.

COMPONENT_CXXFLAGS

Will be visible ONLY to C++ code within the component.

COMPONENT_CPPFLAGS

Will be visible to both C and C++ code within the component.

Important

During initial parsing, many of these variables (specifically, the COMPONENT_xxx ones) do not keep their values. For this reason it is usually best to use simple variable assignment using :=.

For example, in Esp8266/Components/gdbstub we define GDB_CMDLINE. It may be tempting to do this:

GDB_CMDLINE = trap '' INT; $(GDB) -x $(COMPONENT_PATH)/gdbcmds -b $(COM_SPEED_GDB) -ex "target remote $(COM_PORT_GDB)"

That won’t work! By the time GDB_CMDLINE gets expanded, COMPONENT_PATH could contain anything. We need GDB_CMDLINE to be expanded only when used, so the solution is to take a simple copy of COMPONENT_PATH and use it instead, like this:

GDBSTUB_DIR := $(COMPONENT_PATH)
GDB_CMDLINE = trap '' INT; $(GDB) -x $(GDBSTUB_DIR)/gdbcmds -b $(COM_SPEED_GDB) -ex "target remote $(COM_PORT_GDB)"

These values are global and should be used ONLY in the Sming/Arch/*/build.mk files to tune the architecture compilation flags. These values must only be appended to (with +=), never overwritten.

CPPFLAGS

Used to provide both C and C++ flags that are applied globally.

CFLAGS

Used to provide ONLY C flags that are applied globally.

CXXFLAGS

Used to provide ONLY C++ flags that are applied globally.

SMING_C_STD

Used to provide the C language standard. The default is c11.

Important

Do NOT set CPPFLAGS, CFLAGS and CXXFLAGS outside of the Sming/Arch/*/build.mk files.

Building

For faster builds use make with the -j (jobs) feature of make. It is usually necessary to specify a limit for the number of jobs, especially on virtual machines. There is usually no point in using a figure greater than (CPU cores + 1). The CI builds use -j3.

Note that Makefile-app.mk enforces sequential building to ensure submodules are fetched and patched correctly. This also ensures that only one Component is built at a time which keeps the build logs quite clean and easy to follow.

Components can be rebuilt and cleaned individually. For example:

  • make spiffs-build runs the Component ‘make’ for spiffs, which contains the SPIFFS library.

  • make spiffs-clean removes all intermediate build files for the Component

  • make spiffs-rebuild cleans and then re-builds the Component

By default, a regular make performs an incremental build on the application, which invokes a separate (recursive) make for the App Component. All other Components only get built if any of their targets don’t exist (e.g. variant library not yet built). This makes application building faster and less ‘busy’, which is generally preferable for regular application development. For Component development this behaviour can be changed using the FULL_COMPONENT_BUILD variable (which is cached). Examples:

  • make FULL_COMPONENT_BUILD=lwip will perform an incremental build on the lwip Component

  • make FULL_COMPONENT_BUILD=1 will incrementally build all Components

Custom Building

To use an external makefile or other build system (such as CMake) to create the Component library, or to add additional shared libraries or other targets, customise the component.mk file as follows:

  1. Set CUSTOM_BUILD=1

  2. Define the custom rule, prefixed with $(COMPONENT_RULE). Note that Components are built using a separate make instance with the current directory set to the build output directory, not the source directory.

It is important that the rule uses the provided values for COMPONENT_LIBNAME, COMPONENT_LIBPATH and COMPONENT_LIBDIR so that variant building, cleaning, etc. work correctly. See below under ‘Building’, and the Host lwip Component for an example.

Components are built using a make instance with the current directory set to the build output directory, not the source directory. If any custom building is done then these variables must be obeyed to ensure variants, etc. work as expected:

COMPONENT_LIBNAME as provided by component.mk, defaults to component name, e.g. Sming COMPONENT_LIBHASH hash of the component variables used to create unique library names, e.g. 13cd2ddef79fda79dae1644a33bf48bb COMPONENT_VARIANT name of the library to be built, including hash. e.g. Sming-13cd2ddef79fda79dae1644a33bf48bb COMPONENT_LIBDIR directory where any generated libraries must be output, e.g. /home/user/sming/Sming/out/Esp8266/debug/lib/ COMPONENT_LIBPATH full path to the library to be created, e.g. /home/user/sming/Sming/out/Esp8266/debug/lib/clib-Sming-13cd2ddef79fda79dae1644a33bf48bb.a COMPONENT_BUILDDIR where to write intermediate object files, e.g. /home/user/sming/Sming/out/Esp8266/debug/build/Sming/Sming-13cd2ddef79fda79dae1644a33bf48bb

Porting existing libraries

to be completed

Known Issues

Cleaning Components are not cleaned unless defined. e.g. make axtls-8266-clean will fail unless you also specify ENABLE_SSL=1.

Empty libraries Components without any source code produce an empty library. This is because, for simplicity, we don’t want to add a component.mk to every Arduino library.

Empty Component directories Every sub-directory in the COMPONENT_SEARCH_DIRS is interpreted as a Component. For example, spiffs was moved out of Arch/Esp8266/Components but if an empty directory called ‘spiffs’ still remains then it will be picked up instead of the main one. These sorts of issues can be checked using make list-components to ensure the correct Component path has been selected.

Continuous Integration Testing

It is important to ensure that any change made to framework code does not introduce additional bugs. In practice, this is impossible to guarantee but we can at least perform a full build of the entire framework with all samples (both in the framework any any associated libraries).

This is done using the integration testing framework using linux and Windows build environments.

In addition, a number of integration tests are run using Host builds which verify the logic of a large proportion of the code. Testing low-level operation requires real hardware and this must be done manually, but in general libraries and samples can be largely tested using Host builds and carefully constructed tests.

The SmingTest library should be used for such test applications to ensure they are supported on all architectures. It also provides a mechanism for logging test results.

Github Actions

We use Github Actions to manage all test builds. This service is free of charge for open-source projects.

Note

Appveyor has been removed in favour of GitHub Actions.

We used to use Travis but this is no longer free of charge.

Sming performs the build and test logic is handled using scripts, which are intended to be easily portable to other CI services if necessary. Mostly batch scripts (.cmd) are used for Windows, and bash scripts (.sh) for GNU/Linux but where practical powershell core is used as this runs on either.

Note

Sming doesn’t perform CI builds for MacOS.

Library CI support

Sming libraries may be separately built and tested whether or not they are included as part of the Sming repository (or a fork).

There are two mechanisms available.

GitHub Actions

The library.yml reusable workflow is provided, which takes care of these tasks:

  • Checking in the library to test

  • Checking in the Sming framework

  • Installing build tools

  • Builds all applications within the library’s samples directory, for all supported architectures

  • If a test application is provided then that should be located in a test directory. This is built for all architectures, and also executed for Host.

Builds are handled using Tools/ci/library/Makefile.

See also https://docs.github.com/en/actions/using-workflows/reusing-workflows.

To use this in a project, add a suitable workflow to the .github/workflows directory. Templates are provided in the .github/workflows/library directory.

Here is the basic push scenario:

name: CI Push
on: [push]
jobs:
  build:
    uses: SmingHub/Sming/.github/workflows/library.yml@develop
    # Inputs are all optional, defaults are shown
    with:
      # Repository to fetch Sming from
      sming_repo: 'https://github.com/SmingHub/Sming'
      # Sming branch to run against
      sming_branch: 'develop'
      # Library alias
      alias: ''

The sming_repo and sming_branch inputs are provided if your library requires modifications to Sming which are not (yet) in the main repository.

The alias input is required where the library repository name does not correspond with the working title. For example, the jerryscript library is in a repository called Sming-jerryscript, so must be checked out using a different name. If Sming contains a library (or Component) with the same name then it will be overridden, with a warning Multiple matches found for Component 'jerryscript' in the build log.

The ci-dispatch.yml example demonstrates manual triggering, which allows these inputs to be easily changed. See https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow.

Note that the workflow must be available in the library’s default branch, or it will not appear in the github web page.

Appveyor

Appveyor may be configured to test a Sming library separately. Steps to enable:

Add project to appveyor account

Projects -> New Project and select from list

Set Custom Configuration

to https://raw.githubusercontent.com/SmingHub/Sming/develop/Tools/ci/library/appveyor.txt.

Set Project URL slug

If the library under test already exists in the Sming framework then the test directory MUST have the same name to ensure it gets picked up.

For example, testing the Sming-jerryscript library requires this value to be set to jerryscript to match the Sming library name. Build logs should then report a warning Multiple matches found for Component 'jerryscript'.

Set sming fork/branch

By default builds use the main Sming develop branch. If testing a library which requires changes to the framework, you’ll need to use a fork and add SMING_REPO and SMING_BRANCH environment variables to the project settings.

Note that environment variables set here will override any values set in appveyor.txt.

The provided default makefile builds all applications within the library’s samples directory. If a test application is provided then that should be located in a test directory. This is built for all architectures, and also executed for Host.

Build on your own ‘cloud’

Resources are limited to one concurrent build job per appveyor account. Each build gets two virtual CPUs but they’re not particular fast. Network connectivity is, on the other hand, excellent!

One very useful feature that appveyor provides is Bring Your Own Cloud or Computer. This allows the actual builds to be run on other hardware.

Builds can have up to 5 concurrent jobs and as many CPUs as are available. In addition, build images can be pre-installed with toolchains. This can reduce total run times from 5+ hours to around 30 minutes.

Configuration

Full support requires a Windows server with Hyper-V, WSL2 and Docker installed. Hyper-V is built into Windows 10/11 professional edition. WSL2 should be available on all Windows versions.

Linux/MacOS are supported but only for GNU/Linux images.

Note that whilst Docker supports both Windows and Linux images, both cannot be used at the same time: it is necessary to manually switch between Linux/Windows containers. However, testing shows much better performance using Hyper-V for Windows builds.

  1. Add Docker build cloud for Linux builds:

    • Appveyor -> BYOC -> Add Cloud

    • Cloud Provider: Docker

    • Operating system: Windows

    • Base Image: Ubuntu 20.04 Minimal

    • Shell commands:

      git clone https://github.com/SmingHub/Sming --branch develop --depth 1 /tmp/sming
      pwsh /tmp/sming/Tools/Docker/appveyor/setup.ps1
      
    • Image name: linux

    Execute commands as indicated in the resulting screen.

    Wait for the image to be built.

    The final stage updates the cloud information in your appveyor account. Customise as follows:

    • Name

      Change this so it contains only letters, numbers and dash (-). Default names contain a space, e.g. COMPUTER Docker so change to COMPUTER-Docker

    • Custom Docker command arguments

      Customise CPU resources, RAM usage, etc. For example:

      --cpus=8
      

      See https://docs.docker.com/engine/reference/commandline/run/.

    • Failover strategy

      Default values will fail a job if no worker is available to service it. The following settings are suggested:

      Job start timeout: 60
      Provisioning attempts: 100
      
  2. Add Hyper-V build cloud for Windows builds:

Same as (1) above except:

  • Cloud Provider: Hyper-V

  • Base Image: Windows Server Core 2019 Minimal

  • Image name: windows

When complete, fix the build cloud name as previously, e.g. COMPUTER-HyperV. Also check CPU cores, RAM allocation, failover strategy.

  1. Fix authorization token

The above steps will also install the Appveyor Host Agent software on your computer. This is the software which communicates with the Appveyor server and directs the build jobs.

The authorization token used by the agent can be found in the registry:

Computer\HKEY_LOCAL_MACHINE\SOFTWARE\AppVeyor\HostAgent

Make sure that both clouds have the same token.

  1. Configure BYOC images

    Select BYOC -> Images and amend mappings as follows:

      • Image Name: Ubuntu2004

      • OS Type: Linux

      • Build cloud: “COMPUTER-Docker” (as configured above)

      • Image Name: Visual Studio 2019

      • OS Type: Windows

      • Build cloud: “COMPUTER-HyperV” (as configured above)

    Now, when a build is started it should use your own server. To revert back to normal operation change the Image Name fields in the entries. It’s not necessary to delete them: just add, say, “X” to the name so they’re not recognised.

    Note

    Clouds may also be configured on a per-project basis by setting the APPVEYOR_BUILD_WORKER_CLOUD environment variable to the appropriate cloud name.

    To get both Linux and Windows builds working concurrently using this approach would require a single cloud to support dual images.

Rebuilding docker images

Appveyor images are customised by pre-installing Sming build tools. When these are updated images must be re-built.

The easiest way to do this is using the provided dockerfiles:

cd $SMING_HOME/../Tools/Docker/appveyor
docker build --no-cache -t linux -f Dockerfile-Linux .
docker build --no-cache -t windows -f Dockerfile-Windows .
Custom images

To use a Sming fork for building the image simply replace the repo URL and branch in the Shell Commands given above.

These may also be passed to docker build as follows:

docker build -t linux-test -f Dockerfile-Linux --build-arg SMING_REPO=https://github.com/myrepo/Sming --build-arg SMING_BRANCH=feature/appveyor-revisions .
Issues

If you get error image not supported by cloud this probably means an image has been mapped to the wrong clould. Goto Appveyor -> BYOC -> Images and update/delete the offending entries.

If either cloud is shown as offline then check the authorization token (step 4 above). It may be necessary to restart the Appveyor Host Agent service (via Windows service manager).

Clang Tools

clang-format is a tool that implements automatic source code formatting. It can be used to automatically enforce the layout rules for Sming.

clang-tidy is a C++ “linter” tool to assist with diagnosing and fixing typical programming errors such as style violations, interface misuse, or bugs that can be deduced via static analysis. It is provided as part of

You can find details for the current release at https://releases.llvm.org/download.html. Note that clang-format is part of the main Clang project, whilst clang-tidy can be found in clang-tools-extra.

Installation

In Ubuntu you should be able to install them using the following command:

sudo apt-get install clang-format clang-tidy

See the the download page of the Clang project for installation instructions for other operating systems.

Important

Different versions of clang-format can produce different results, despite using the same configuration file.

We are using version 8.0.1 of clang-format and clang-tidy on our Continuous Integration (CI) System.

You should install the same version on your development computer.

Configuration
Rules

The coding rules are described in the .clang-format file, located in the root directory of the framework.

You should not edit this file unless it is a discussed and agreed coding style change.

IDE integration

There are multiple existing integrations for IDEs. You can find details in the ClangFormat documentation.

Eclipse IDE

For our Eclipse IDE, which is our preferred IDE, we recommend installing the CppStyle plugin. You can configure your IDE to auto-format the code on “Save” using the recommended coding style and/or format according to our coding style rules using Ctrl-Shift-F (for formatting of whole file or selection of lines). Read Configure CppStyle for details.

Usage
Command Line

Single File

If you want to directly apply the coding standards from the command line you can run the following command:

cd $SMING_HOME
clang-format -style=file -i Core/<modified-file>

Where Core/<modified-file> should be replaced with the path to the file that you have modified.

All files

The following command will run again the coding standards formatter over all C, C++ and header files inside the Sming/Core, samples and other key directories:

cd $SMING_HOME
make cs

The command needs time to finish. So be patient. It will go over all files and will try to fix any coding style issues.

If you wish to apply coding style to your own project, add an empty .cs marker file to any directory containing source code or header files. All source/header files in that directory and any sub-directories will be formatted when you run:

make cs

from your project directory.

Eclipse

If you have installed CppStyle as described above you can configure Eclipse to auto-format your files on Save.

Alternatively, you can manually apply the coding style rules by selecting the source code of a C, C++ or header file or a selection in it and run the Format command (usually Ctrl-Shift-F).

Coding Style Rules

The benefits of coding standards are readability, maintainability and compatibility. Any member of the development team in Sming should be able to read the code of another developer. The developer who maintains a piece of code tomorrow may not be the coder who programmed it today.

Therefore we enforce coding standards as described in this guide. The coding style rules are mandatory for most of the framework, and Pull Request that does not adhere to the coding style rules will not be merged until those rules are applied.

The rules are optional for libraries, but recommended.

Tools will help you adhere to these coding standards without the need to know them by heart. See Clang Tools for further details.

Please also bookmark the C++ Core Guidelines. This is an invaluable reference for writing good code and making the best use of this powerful language.

Indentation

We use tabs for indentation. Configure your editor to display a tab as long as 4 spaces. For reference, the corresponding settings in clang-format are:

TabWidth:        4
UseTab:          Always
IndentWidth:     4
Naming

Classes, Structures, type aliases

Must be nouns in UpperCamelCase, with the first letter of every word capitalised. Use whole words — avoid acronyms and abbreviations (unless the abbreviation is much more widely used than the long form, such as URL or HTML).

Examples:

class HttpClient {}

class HttpClientConnection {}

using LargeValue = uint32_t;

struct MyStruct {
   ...
};

enum MyEnum {
   a, ///< Comment if required
   b,
   c,
};

Note

The trailing , on the final item in an enumeration declaration will ensure that clang-format places each item on a separate line. This makes for easier reading and the addition of line comments if appropriate.

Methods

Must be either verbs in lowerCamelCase, or a multi-word name that begins with a verb in lowercase; that is, with the first letter lowercase and the first letters of subsequent words in uppercase.

Examples:

bind();

getStatus();

Variables

Local variables, instance variables, and class variables must also be written in lowerCamelCase. Variable names must not start with, end with or contain underscore (_) or dollar sign ($) characters. This is in contrast to some coding conventions which prefix all instance variables with underscore, however this is reserved by the C++ standard and can create problems.

Variable names should be short yet meaningful. The choice of a variable name should be mnemonic — that is, designed to indicate to the casual observer the intent of its use. One-character variable names should be avoided except for temporary “throwaway” variables. Common names for temporary variables are i, j, k, m, and n for integers; c, d, and e for characters.

Examples:

int i;

char c;

WebsocketClient* client;

Pre-processor definitions

#defined macros must be written in uppercase characters separated by underscores. Names may contain digits if appropriate, but not as the first character. For example:

#define MAX_PARTICIPANTS 10

Constants

Typically declared using const or constexpr and, like variables, should be lower-camelcase. Names MUST NOT be all-uppercase as these may be confused with #defined values.

See C++ Core Guidelines.

Use of typedef in C++

Use of typedef in C++ code is not recommended.

The using keyword has been available since C++11 and offers a more natural way to express type definitions. It is also necessary in certain situations such as templating.

For example:

using ValueType = uint32_t;

is more readable than:

typedef uint32_t ValueType;

Especially in multiple type declarations the subject is always immediately after the using keyword and makes reading much easier.

https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#t43-prefer-using-over-typedef-for-defining-aliases

https://www.nextptr.com/tutorial/ta1193988140/how-cplusplus-using-or-aliasdeclaration-is-better-than-typedef

enum/struct declarations

This:

typedef struct _MyStructTag {
  ...
} MyStruct;

typedef enum _MyEnumTag {
  ...
} MyEnum;

is overly verbose and un-necessary. It’s a hangover from ‘C’ code and should generally be avoided for readability and consistency. This is the preferred definition:

struct MyStruct {
  ...
};
enum MyEnum {
.............
};

It’s also un-necessary to qualify usage with enum. i.e. MyEnum e; is sufficient, don’t need enum MyEnum e;.

C++ Standard

For the moment we recommend the use of C++11. The corresponding settings in clang-format are:

Standard:        Cpp11
Cpp11BracedListStyle: true
Starting and ending spaces

We don’t recommend the use of a starting or ending space in angles, container literals, c-style cast parentheses, parentheses and square brackets. Our settings are:

SpaceAfterCStyleCast: false
SpaceBeforeParens: Never
SpaceInEmptyParentheses: false

SpacesInAngles:  false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false

See the meaning of those keys and their selected values in the ClangFormatStyleOptions document.

Line length

We are living in the 21st century so most of the monitors should be capable of displaying 120 characters per line. If a line is longer than those characters it will be split whenever possible:

ColumnLimit:     120
Empty Lines

Two or more empty lines will be compacted to one. Also we delete empty lines at the start of a block:

KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 1
Braces

See the meaning of these keys and their selected values in the ClangFormatStyleOptions document:

BraceWrapping:
    AfterClass:      false
    AfterControlStatement: false
    AfterEnum:       true
    AfterFunction:   true
    AfterObjCDeclaration: false
    AfterStruct:     false
    BeforeElse:      true
    IndentBraces:    false
BreakBeforeBraces: Linux
Pointer Alignment

Always on the left:

PointerAlignment: Left
Includes

We don’t re-sort includes although it is highly recommended to order the headers alphabetically whenever possible:

SortIncludes:    false
Comments

We try not to split comment lines into smaller ones and also we add one space between code and trailing comment:

ReflowComments: false
SpacesBeforeTrailingComments: 1
Spaces

For readability put always spaces before assignment operators:

SpaceBeforeAssignmentOperators: true
Standard file headers

Please use the standard Sming header with copyright notice:

/****
 * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development.
 * Created 2015 by Skurydin Alexey
 * http://github.com/anakod/Sming
 * All files of the Sming Core are provided under the LGPL v3 license.
 *
 * [Insert filename here] - [optional brief description of file]
 *
 * @author [date] [name] [email]
 *
 * [comments]
 *
 ****/

Do not include details of minor changes to the file as this is handled by GIT. It may be appropriate to add notes to identify major changes or contributions. These should be marked with a new @author tag.

Deprecating code

Where a change in the Sming API may break existing users’ code, then the existing type/method/function/variable must be maintained for a time to allow time for migration to the new technique. Such changes should only be made if there is a good reason, for example improved reliability, performance, ease of use.

Deprecation requires two steps:

Step 1: Add a @deprecated tag to the method header comment so the change is flagged in the auto-generated API documentation. Include a brief explanation of the new method or technique to be adopted. See also Documenting the API.

Example:

/** @deprecated Use `anotherMethod()` instead */

Step 2: Append SMING_DEPRECATED to the method declaration so the compiler will flag a warning if that method is used during compilation.

The framework and samples must build without referencing any deprecated methods, functions or variables.

Virtual Classes

Sming makes extensive use of virtual classes. If you are modifying or adding virtual methods then please follow these guidelines:

Rule: The base class must have a virtual destructor, even if it doesn’t do anything. Example:

virtual ~Stream() {}

Rule: Inherited classes must not prepend virtual or append override to any destructor. Example:

~IDataSourceStream();

Rationale: virtual destructors do not behave like regular virtual methods - they are ‘chained’ rather than overridden - therefore override is not appropriate and virtual is both un-necessary and unhelpful

Rule: Use the override directive on inherited virtual methods:

int read() override;

Rationale: The compiler will ensure there is actually a base method to inherit from and generate a warning if one is not found, or if parameters do not correspond.

Rule: Don’t use empty destructors in inherited virtual classes

Rationale: They’re not necessary

Common issues

Some notes on commonly occurring issues:

/**
  * @brief Basic example class
  */
class VirtualBuffer
{
public:
    virtual ~VirtualBase
    {
    }

    virtual unsigned getLength() const = 0;
};

/**
  * @brief Descendant example class
  */
class MemoryBuffer : public VirtualBuffer
{
public:
    /*
        Note: Omit destructor if not required in descendant
    */
    ~VirtualDescendant()
    {
        /*
            Note: delete includes null pointer check so you don't have to
        */
        delete buffer;
    }

    /*
        Use `const` qualifier for methods which don't modify object
     */
    const char* getBuffer() const
    {
        return pos;
    }

    /*
        Trivial code should go into the class header file where possible.
        Rationale: Compiler is better able to optimise code. Easier to read.

        Use `override` on virtual methods
    */
    unsigned getLength() const override
    {
        return length;
    }

    /*
        Use methods to access member variables rather than making them public
        Rationale: Protects data, helps when tracking down bugs
    */
    void setBuffer(char* newBuffer, unsigned newLength)
    {
        delete buffer;
        buffer = newBuffer;
        length = newLength;
    }

private:
    /*
        Each class should operate on a small, well-defined item of data.
    */


    /*
        Class variables should be defined with initialisers, rather than using code in the constructor.
        Rationale: Reduces/eliminates risk of un-initialised data causing unpredictable behaviour.
    */
    char* buffer = nullptr;

    /*
        Remember `int` can be unsigned! If a value doesn't need to be signed, don't make it so.
        Rationale: unsigned values are simpler to check, less likely to introduce bugs, compiler can better optimise computations
    */
    unsigned length = 0;
};
Documentation System
Read the Docs and Sphinx

Online documentation is managed via Read the Docs, which uses Sphinx as the documentation build system.

This page describes specific details for the Sming documentation build system.

reStructuredText

These have a .rst file extension. Some references you should have a read through:

Markdown .md files are supported (except for samples) but please ensure there is only one main heading (*HEADING) at the top containing the title of the Library or Component or it may be incorrectly rendered in the table of contents. If you run into problems trying to get your markdown to display correctly, then it’s probably time to move to reStructuredText!

If you need to convert existing documentation into reStructuredText take a look at pandoc. Note that their online tool doesn’t handle long files.

Source file location

Sphinx requires all document files (documents, images, etc.) to be located in one place, namely inside the docs/source directory. During the build process, documentation files from the Component, Library and Sample directories are copied:

README.md
*.rst
*.svg
*.png
*.jpg

This is also performed for any submodules declared by the COMPONENT_SUBMODULES variable in a component.mk file.

This should be sufficient for most cases. However, if additional documentation is referred to, such as in submodules belonging to a Component, then these must be listed in the component.mk file like this:

COMPONENT_DOCFILES := submodule/docs/api-guide.md submodule/api.txt

See Sming build system for more information about component.mk files.

README files

All Components, Libraries and Samples must include a README.rst or README.md file as follows:

  • Main Heading: This will be used as the title in the online documentation, so keep it brief but informative. For example, BME280 Barometric Pressure Sensor.

  • Purpose: What is the purpose of the code?

  • References: Is this based on or does it use existing code? Please include details.

  • Datasheets: If appropriate, please include links to manufacturer’s or external development websites. Note that any submodules or dependencies are automatically documented: see Sming build system for details, specifically COMPONENT_SUBMODULES and COMPONENT_DEPENDS.

You should also try to include any other information which could be useful information for a new developer. The purpose of samples projects is to demonstrate specific features or libraries, so please ensure this is adequately described.

Optionally, you may also like to include a screenshot or other diagrams or illustrations. Please use .png, .jpg or .svg files.

Attention

The README filename is case-sensitive

Attention

Please ensure there is only one top-level heading or the documentation contents will not display correctly.

You should use the available annotations to make browsing the documentation easier. Using the Sphinx roles <https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html> will insert hyper links to the corresponding definitions. Additional roles are provided specifically for the Sming documentation - see link_roles below.

Code blocks

There are multiple ways to show syntax-higlighted literal code blocks in Sphinx. See Showing code examples for full details.

Use the code-block directive like so:

.. code-block:: c++

   for(int i = 0; i < 100; ++i) {
      goto hell;
   }

The language for highlighting is indicated. You can find a full list at pygments.org, however for consistency it is suggested that you use one of these:

text     Doesn't highlight anything
c++      C++ code examples
bash     Linux shell code
batch    Windows batch file or commands
make     Makefile
rst      reStructuredText

You can set a default like this:

.. highlight:: c++

which will apply to any subsequent use of:

.. code:block::

or, the short-hand version:

::
API Documentation

Function, structure, class and type information is extracted from comments in the source code (see Documenting the API). This is parsed using Doxygen into XML, which is then made available using the Breathe sphinx extension. You can then pull in definitions like this:

.. doxygenclass:: String
   :members:

If you wish to refer to a type within documentation, you can add a link to the definition like this:

The :cpp:class:`String` class is really useful.

This is handled using cpp inline expressions.

See Esp8266 GDBSTUB for Sming for a more complex example. At the bottom of the file we pull in the documentation for all the #defined configuration using:

.. doxygenfile:: gdbstub-cfg.h

We can then refer to a macro like this:

Don't wait on startup by setting :c:macro:`GDBSTUB_BREAK_ON_INIT` =0

In many cases including a file like this is not the best approach, perhaps using a group:

.. dogygengroup:: wstring

Or individual classes. Some experimentation may be necessary but there are plenty of examples within the main documentation to guide you.

You can use the following build variables within your Component’s component.mk file to direct doxygen parsing:

COMPONENT_DOXYGEN_INPUT

Specifies directories or files to be parsed by Doxygen. All paths are relative to the Component directory.

If you need to specify an absolute path, append directly to DOXYGEN_INPUT instead.

COMPONENT_DOXYGEN_INCLUDE

Add any directories or files which should be pre-processed but not included in the output.

If you need to specify an absolute path, append directly to DOXYGEN_INCLUDE_PATH instead.

COMPONENT_DOXYGEN_PREDEFINED

Specify any necessary pre-processor definitions. An example where this is required is for function attributes #defines which would otherwise be incorrectly interpreted as variable names and cause parsing errors:

CUSTOM_ATTR void myFunc();
^^^

So we can do this:

COMPONENT_DOXYGEN_PREDEFINED := \
   CUSTOM_ATTR=
Build (environment) variables

These are defined in the README for the corresponding Component using:

:envvar::`COM_SPEED`
Determines default serial port speed

You can refer to them like this:

Change baud rate using the :envvar:`COM_SPEED` variable.
Eclipse

You can find a good plugin editor for Eclipse by searching the marketplace for rest editor. For example, http://resteditor.sourceforge.net/. A useful feature is dealing with heading underscores, just type this:

My Heading
==

Then when you save the file it gets formatted like this:

My Heading
==========

Tables, unfortunately, do take a bit of manual formatting to get right.

Sphinx Extensions

The documentation system is easily extended to support new features. This section summarises the extensions included.

m2r2

Provides support for markdown content.

breathe

To support Doxygen integration. See API Documentation.

link-roles

A custom extension implemented in link-roles.py. See Link Roles.

sphinxcontrib.wavedrom

For implementing timing and other waveform diagrams within documents. See Servo RC PWM Control for an example.

sphinxcontrib.seqdiag

For embedding sequence diagrams, such as in the Tasks page.

Documenting the API

This guide describes how to document the Sming API. It is aimed at developers of the framework, not users of the framework.

Sming API documentation is created using javadoc style comments within the source code. Comments are added to header files and doxygen is used to create HTML documentation. API documentation is organised as a set of modules, each of which represents a logical topic, e.g. Date and Time.

The API documentation covers the core Sming framework and relevant parts of the ESP SDK (where this is required to use the framework). The API does not cover contributed libraries which should be documented by the upstream contributor. Within the source code tree, this includes:

  • Services

  • SmingCore

  • Wiring

  • rboot

The following elements of source code are documented:

  • Global constants

  • Classes

  • Class public members: functions, variables, operators

  • Macros

  • Structures

  • Enumerations

Javadoc comments use the following style:

  • Start with /**

  • End with */

  • Each line (between start and end) starts with a space and a *

  • Doxygen commands are prefixed with @

  • Constants documentation use the command ///<

Each API documentation comment should be part of a module. To define a module, use the @defgroup command. This should be performed only once within the Sming framework source code. The format is:

@defgroup groupname Brief description of the group

To configure a group to span code, use the @{ and @} commands. When appended to a comment block, @{ will include all subsequent javadoc comments until the next @} command. For example:

/**
 * @defgroup DateTime Date and time functions
 * @{
 */
...
/**
 * @brief  Date and time class
...
 * @}
 */

To add some documentation comment to a group, use the @addtogroup command. This may be performed several times within the Sming framework source code. The format is:

@addtogroup groupname

Use the @{ @} commands to span comments. This avoids the need to add @addtogroup commands to each comment.

To create a hierarchy of groups, use the @ingroup command for child groups, for example:

/**
 * @defgroup SystemTime System time functions
 * @ingroup DataTime
 ...

It is common to be able to define a group and span a whole file using @defgroup and @{ @} commands. This is often possible when all content of a header file relates to a common topic.

Constant values should be included in the constants group, for example:

/**
 * @brief  ESP GPIO pin configuration
 * @ingroup constants
 */

To document a function, use the following comment style:

/**
 * @brief  Brief description of function
 * @param  "Parameter name" Description of parameter (Default: value if defined)
 * @param  ... rest of parameters
 * @retval DataType Description of return value
 * @note   Any extra notes that may assist application developer
 */

For example:

/** @brief  Get human readable date and time
 *  @param  format Select the date format, e.g. dtDateUS for mm.dd.yyyy (Default: dd.mm.yyyy)
 *  @retval String Date and time in format dd.mm.yyyy hh:mm:ss
 *  @note   Date separator may be changed by adding #define DT_DATE_SEPARATOR "/" to source code
 */
String toFullDateTimeString(dtDateFormat_t format = dtDateFormatEU);

To document a Sming framework constant, use command ///<, for example:

int8_t DayofWeek; ///< Day of week (0-6 Sunday is day 0)

To reference another method or function, write its name with ():

Use `otherMethod()` instead

This will match any method in the current class called otherMethod, regardless of arguments. Note the backticks ` are there just to highlight the code. If you need to be specific:

Use `otherMethod(const char*, int)` instead

To add a See Also section, use @see. Example:

@see See `OtherClass()` for details

Multitasking

Pre-emptive Multitasking

Modern computers have evolved so that you can write a program and it will (mostly) run without interfering with anything else that is also running.

With multiple CPUs tasks can actually run at the same time (concurrently), but as there are always many more threads (tasks) these have to be shared. This is especially true on older systems which only have a single CPU.

The OS does this using a mechanism called Pre-emptive Multitasking. As far as your program is concerned, it just runs without any apparent interruptions, but in reality it only gets a little ‘slice’ of time before the operating system forcibly switches to a different program and lets it run for another ‘slice’ of time. (Hence the term, time slicing.)

With pre-emptive multitasking the operating system has to maintain state for every single task that is active. This requires additional RAM.

There is also the overhead of switching between tasks. Each task also requires its own stack space so there is usually more restriction on how it is used.

FreeRTOS is perhaps the most well-known example of a pre-emptive embedded OS.

Co-operative Multitasking

By contrast, Co-operative Multitasking, requires applications to ‘play fair’ and not hog the CPU. This means that whenever you get called to do some work, you must release control back to the system in a timely manner.

This technique is well-suited to resource-limited systems such as the Esp8266.

The Esp8266 has only a single CPU, and relatively limited RAM; about 50KBytes is available for application use. The heap grows from the bottom of RAM, and the stack starts at the top.

Sming uses the ESP8266 Non-OS SDK, which manages much of the low-level system control including the WiFi stack. It contains the main control loop, which in pseudo-code goes something like this:

for(;;) {
   ServiceWifi();
   ServiceTimers();
   ServiceTasks();
   FeedWatchdog();
}

Application code gets called from there in response to a network request, or a timer that you’ve set up, and you must deal with the request and return promptly to allow things to continue.

If you don’t do this, the system will very likely behave erratically and suffer from dropped WiFi connections and poor responsiveness. In extreme cases, the system will reset as a self-defence mechanism via Watchdog Timer; If it didn’t do this, the device would remain unresponsive until physically reset, which is generally a bad thing for an embedded device!

Note that whilst the ESP32 implementation uses the ESP IDF framework based on FreeRTOS, which is a pre-emptive OS, the programming model for Sming is the same.

Attention

Although there are functions available to manually reset watchdog timers, you should endeavour to avoid doing this from application code unless absolutely necessary.

Events

Introduction

The Sming framework has an event-driven architecture. In other words, we need to think of our application as performing some action in response to an event, input or stimulus.

Software events

Let’s look at the Basic Blink sample. We define a couple of global variables:

Timer procTimer;
bool state = true;

The one to note is procTimer, which is a software timer (see Timers and Clocks).

The startup entry point for a Sming application is the init() function:

void init()
{
   pinMode(LED_PIN, OUTPUT);
   procTimer.initializeMs(1000, blink).start();
}

This sets up a repeating timer, so that every 1000ms the blink function gets called:

void blink()
{
   digitalWrite(LED_PIN, state);
   state = !state;
}

Note that what we don’t do is sit in a loop waiting for something to happen.

Interrupt events

The Basic Interrupts sample can be summarised as follows:

  • We have a button connected to GPIO0 as an input

  • Button pressed
    • Hardware interrupt on GPIO0
      • Change output state of LED via GPIO2

There are two ways we can determine when the state of our GPIO0 pin changes:

Polling

We read the input very quickly and compare the current value with the previous value, which we keep a note of. Our event occurs when the value changes, or if the input goes HIGH, or LOW, etc.

Interrupt

If we need to catch fast pulses then polling may not work as the CPU might be doing something else and we’ll miss it. So instead, we’ll get the hardware to monitor the input for us and generate an event when it changes.

Both techniques have advantages and disadvantages, and interrupts are certainly not appropriate in all situations.

Bear in mind that every time an interrupt occurs the CPU has to stop executing your regular program, save any critical registers then jump to the interrupt service routine to run your code. All this takes time, so if the input changes very fast and very frequently then it can consume a lot of CPU and make the system very sluggish (or even crash it).

Callbacks

In Sming, we generally register a callback function to be invoked when an event occurs, such as if a timer expires.

This can be a regular ‘C’ callback function, which you should use for handling interrupts.

For regular application code, a Delegate provides more flexibility and allows you to create simpler, cleaner code. See Delegation for a bit of background.

The Basic Delegates sample shows how to use various types of callback.

One common requirement is to use a class method as a callback, which is easily done using Delegates.

This flexibility comes at a cost, however:

  • Speed. They are slower than their regular C-function counterparts

  • Memory. Some calls may use the heap in the background.

These are the main reasons why you should not use Delegates in an interrupt context.

See Pull Request #1734 for some further details about the relative speeds.

Memory

Map

You can find a map for the ESP8266 memory layout in the Wiki.

Memory Types

The ESP8266 has several types of memory, and it is important to have a basic apprecation of what they are and how they’re used.

DRAM

Data RAM where variables, etc. are stored. Contains the stack (which starts at the top of RAM) and the heap (which starts near the bottom, after any static data).

IRAM

Instruction RAM. All code executes from here, but there’s not much of it so so only parts of your application are loaded at any one time. This caching happens in the background by mapping certain memory address ranges into physical flash locations.

If a location is accessed which is already in the cache (a ‘hit’) then the access runs at full RAM speed. If it’s not in the cache (a ‘miss’) then an interrupt (exception) is generated internally which performs the flash read operation.

This is why interrupt service routines must not access PROGMEM directly, and must be marked using IRAM_ATTR to ensure it’s always available.

You may get a performance improvement using IRAM_ATTR, but as frequently-used code will be cached anyway it won’t necessarily run faster.

If the code is timing critical it may benefit from pre-caching. See Esp8266 SPI Flash Support.

Flash

Main storage for your application, libraries, the Espressif SDK code, etc. Flash memory is accessed via external serial bus and is relatively slow. For the ESP8266, it’s approximately 12x slower, though this only applies on cache misses.

See Flash memory for further details.

ROM

Fixed code stored in the Esp8266 itself containing very low-level support code which is factory-programmed and cannot be changed.

Initialisation

At startup, only the non-volatile Flash and ROM memories contain anything useful, whilst DRAM and IRAM will probably just contain garbage. The Arduino platform was initially designed to work with much simpler hardware, where the program was executed directly from Flash memory on hardware reset.

BIOS

The ESP8266 and ESP32 are far more complex, and most of the low-level initialisation happens in the ROM code. The ROM essentially contains the systems BIOS, with various low-level routines which may be used instead of accessing hardware directly. It is also responsible for setting up memory caching.

Runtime libraries

Control is passed to runtime libraries provided by Espressif, stored in Flash memory. Both ROM and runtime code are closed-source and not generally available for inspection, though disassemblies do exist.

Boot loader

The first point we really see what’s going on is in the bootloader (rBoot). The bootloader identifies the correct program image (as there can be more than one), loads the starting portion into IRAM and jumps there. It also configures the caching mechanism so that the correct program image is loaded. You can find more details about this in the rBoot documentation.

Memory initialisation

Code is copied from flash into IRAM, and const data copied into DRAM. Also static and global variable values are initialised from tables stored in flash. Static and global variables without an initialised value are initialised to 0.

Sming initialisation

{todo}.

Flash memory
Introduction

ESP8266 flash memory sizes vary from 512Kbytes on the ESP-01 up to 4Mbytes on the ESP12F. Up to 16MBytes are supported for custom designs.

Sming version 4.3 introduced partition tables to support multiple architectures, different hardware variants and custom flash layouts without restriction.

See Hardware configuration for details.

A typical layout for a 4MByte device might look like this:

Address

Config variable

Size

Source filename

Description

(hex)

(if any)

(KB)

(if applicable)

000000

1

rboot.bin

Boot loader

001000

4

rBoot configuration

002000

ROM_0_ADDR

rom0.bin

First ROM image

102000

ROM_1_ADDR

rom1.bin

Second ROM image

200000

RBOOT_SPIFFS_0

300000

RBOOT_SPIFFS_1

3FA000

4

Partition table

3FB000

4

blank.bin

RF Calibration data (Initialised to FFh)

3FC000

4

esp_init_data_default.bin

PHY configuration data

3FD000

12

blank.bin

System parameter area

Note

This was the previous layout for a 4MByte flash device:

Address

Config variable

Size

Source filename

Description

(hex)

(if any)

(KB)

(if applicable)

000000

1

rboot.bin

Boot loader

001000

4

rBoot configuration

002000

ROM_0_ADDR

rom0.bin

First ROM image

100000

RBOOT_SPIFFS_0

202000

ROM_1_ADDR

rom1.bin

Second ROM image

300000

RBOOT_SPIFFS_1

3FB000

4

blank.bin

RF Calibration data (Initialised to FFh)

3FC000

4

esp_init_data_default.bin

PHY configuration data

3FD000

12

blank.bin

System parameter area

Speed and caching

Flash memory on the ESP8266 is accessed via an external SPI bus, so reading it takes about 12x longer than reading from internal RAM. To mitigate this, some of the internal RAM is used to cache data. Part of this is managed in hardware, which means if the data required is already in the cache then there is no difference in speed. In general, then, frequently accessed data is read as if it were already in RAM.

Bear in mind that every time new data is read via the cache, something else will get thrown away and have to be re-read. Therefore, if you have large blocks of infrequently accessed data then it’s a good idea to read it directly using flashmem_read(). You can get the address for a memory location using flashmem_get_address().

See Program Space for details of how to store data in flash, and access it.

Bulk Storage and Filing Systems

Sming uses a class-based layered approach to bulk storage.

The Storage Management Component defines the Storage::Device abstract class, which devices such as SPI flash implement to provide raw read/write/erase access. Devices are partitioned into areas for specific uses which applications access using a Storage::Partition object.

The Disk Storage library provides support for block-access devices which use standard partitioning schemes (MBR, GPT). SD cards are supported via the SD Storage library.

Sming uses an installable (virtual) filing system mechanism based on IFS::IFileSystem. This is managed by the Installable File System Component and contains the FWFS lightweight read-only filing system.

Additional filing system implementations are provided in separate libraries:

Note that when using bulk storage of more than about 4GB in size applications should be built with ENABLE_STORAGE_SIZE64 =1. This changes all sizes and offsets to 64-bit.

If manipulating files greater than about 2GB (signed 32-bit value) then the ENABLE_FILE_SIZE64 setting should also be enabled.

Strings

Sming provides three classes to deal with string content:

Interrupts

Rules

Normal code runs in a Task context, that is to say there are no particular restrictions on what functions can be called. Sming is a single-threaded framework, so all tasks execute sequentially.

However, there are some rules you need to follow when writing code which runs in an interrupt context:

  • Use IRAM_ATTR attribute on the interrupt handler

  • Don’t call any code which isn’t in IRAM, or marked with __forceinline. Note: inline by itself is just a compiler ‘hint’ and there is no guarantee the code will actually be inlined.

  • Keep the code as brief as possible

  • Don’t use the heap (malloc, free, new, etc.). Buffers, etc. must be pre-allocated in task context.

  • Put processing code into a separate function and add to the task queue SystemClass::queueCallback(), or use a SimpleTimer or Timer.

  • Be wary if using Delegate callbacks as the compiler may need to use the heap, if parameter lists do not match. For this reason, avoid capturing lambdas and method callbacks. If in doubt, stick with regular ‘C’ function pointers as defined by the InterruptCallback type.

Contact bounce

If you use a jumper wire to ground the input pin (or even a proper switch) then it will bounce around as contact is made and then broken. This means you’ll get multiple outputs instead of a clean signal, which will cause havoc with interrupt handlers.

One solution is to use a ‘debounce circuit’, which can be as simple as this:

               3v3
               _|_
               ___ 100nF
                |
                |
INPUT   >--------------> GPIO PIN

Provided pull-ups are enabled on the GPIO, this is sufficient to slow the input.

An alternative solution is to poll the input instead. See Tasks.

Tasks

Introduction

If you need to perform any kind of background processing task, then you will need to consider how to break it up so it executes in small slices to allow other system functions to continue normally.

You can use callback timers to schedule periodic tasks, but if you simply need to run the task as soon as possible you should use the Task Queue.

The BasicTask class

This class uses the task queue plus a timer to provide an easy way to write a background task. All you need to is define a loop() function which does the work. The task has three states:

_images/tasks.png

To see this in operation, have a look at the Basic Tasks sample.

Task Schedulers

If you want to read further about schedulers, there are various libraries available for Arduino.

These are quite simple and generic:

This one is rather more complex:

Using a scheduler is a powerful technique which allows the programmer to focus on the task at hand, rather than how to get it to run.

Debugging

Debugging is a powerful technique allowing you to interactively run your code and be able to see much more information about the things that went wrong.

Architectures

Debugging on the supported architectures requires the installation and initial setup of different debugging tools. The links below will give you more information and step-by-step instructions.

Debugging on Host
Required tools and hardware

A GNU C/C++ debugger is the only requirement for the Host architecture. Make sure that you have the following executable in your PATH:

gdb

No additional hardware is required.

Recompilation is required

In order to debug applications based on Sming Framework make sure that you are using Sming version 3.8.0 or newer.

Compilation directives

If you want to debug your application and the Sming Framework code make sure to (re)compile it with ENABLE_GDB =1 directive:

cd $SMING_HOME/../samples/LiveDebug
make dist-clean
make ENABLE_GDB=1

The commands above will re-compile Sming with debug symbols and optimizations for debugging. These commands need to be executed once.

You can recompile Sming with the following directives to debug better Sming and the LWIP TCP/IP stack

cd $SMING_HOME/../samples/LiveDebug
make Sming-build all ENABLE_GDB=1 ENABLE_LWIPDEBUG=1
Application

To use, (re)compile your application with the ENABLE_GDB option and flash it to the board. For this example we will use the Live Debug sample application:

cd $SMING_HOME/../samples/LiveDebug
make clean
make ENABLE_GDB=1 # -- recompiles your application with debugging support

The next step is to start the debugger. This can be done with the command below:

make gdb

After that a new interactive debugging session will be started:

Welcome to SMING!
Type 'r' to run application

To start the execution of the application type r or run:

(gdb) r
Starting program: /x/Sming/samples/LiveDebug/out/Host/debug/firmware/app --flashfile=out/Host/debug/firmware/flash.bin --flashsize=4M --pause
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
[New Thread 0xf7bdcb40 (LWP 16428)]

Welcome to the Sming Host emulator

host_flashmem_init: Created blank "out/Host/debug/firmware/flash.bin", 4194304 bytes

...
main: >> Starting Sming <<

You can pause the program execution by pressing Ctrl-C. And work further using some further GDB commands. The next paragraph describes some of them.

GDB commands

There are multiple commands supported in GDB and we will mention only some of them.

List current source code

One possibility is to see the source code of the current line where the execution has stopped. To achieve this you should type list in the gdb console:

(gdb) list
102     }
103 }
104
105 int main(int argc, char* argv[])
106 {
107     trap_exceptions();
108
109     host_printf("\nWelcome to the Sming Host emulator\n\n");
110
111     static struct {
Break the execution

This command will pause the debugger once it reaches a specific function or line in the code. This is called breakpoint and can be set like this:

(gdb) break blink
Breakpoint 1 at 0x40105d4c: file app/application.cpp, line 66.
Continue the execution

To continue the execution of the application we can use the continue command:

(gdb) continue
Continuing.

Breakpoint 1, blink () at app/application.cpp:66
66 {
(gdb)

Because we have set already a breakpoint for the blink function the execution will be paused when the blink function is reached and from here you can go to the next line or see the current values of the variables.

Go to the next line

This can be done using next:

(gdb) next
67      digitalWrite(LED_PIN, ledState);
See variable value

The command to see a value is print followed by the name of the value. For example to see the value of the ledState variable inside the blink function we could type:

(gdb) print ledState
$1 = true

You can see more useful commands here.

Or watch the following short video

https://img.youtube.com/vi/hVwSX_7Ey8c/3.jpg
Debugging with visual debuggers like Eclipse CDT

A good visualization helps us understand things faster. What we can do is use Eclipse CDT and its debugging plugins to do remote debugging as we did from the command line.

Here is how this can be done:

  • Start Eclipse CDT and import the Live Debug sample:

    • Select File -> New -> Project -> C/C++ -> Makefile Project with Existing Code

    • Point Eclipse to the location of the LiveDebug sample

    • Import the Sming Framework (if you haven’t done it yet)

Import Project

Import Project

Once the two projects are in Eclipse, set the LiveDebug project to reference the Sming project.

Now create a new Debugging Configuration:

  • Select Run -> Debug Configurations -> C/C++ Application

  • Right-click and create a new C/C++ Application

  • In the Main tab set, set:

    • Project: Basic_Build

    • C/C++ Application: out/Host/debug/firmware/app

    • disable for now the auto build

C/C++ Application

C/C++ Application

Then go to the Debugger tab and point the GDB debugger to your gdb binary. (Type make list-config and look for GDB.)

Debugger configuration

Debugger configuration

We are now ready for debugging. Press the Debug button. (In the screenshot above the Debug button is in the bottom-right corner.) After some seconds your debugging session should be up and running and you can enjoy live debugging.

Live Debugging Session

Live Debugging Session

You will be able to see the current variables and their values. You should be able to go step by step, go inside of functions, add add breakpoints and watchpoints.

Debugging on ESP8266
Required tools and hardware

A debugger is the only requirement for ESP8266. It is usually part of the provided toolchain. Make sure that you have the following executable in your PATH:

xtensa-lx106-elf-gdb

No additional hardware is required (Apart from a standard USB-to-UART adapter).

Recompilation is required

In order to debug applications based on Sming Framework make sure that you are using Sming version 3.8.0 or newer.

Framework

If you want to debug inside of the Sming Framework make sure to (re)compile it with ENABLE_GDB =1 directive:

cd $SMING_HOME
make dist-clean
make ENABLE_GDB=1

The commands above will re-compile Sming with debug symbols and optimizations for debugging. These commands need to be executed once.

Application

To use, (re)compile your application with the ENABLE_GDB option and flash it to the board. For this example we will use the Live Debug sample application:

cd $SMING_HOME/../samples/LiveDebug
make clean
make ENABLE_GDB=1 # -- recompiles your application with debugging support
make flashapp # flashes ONLY the (re)compiled application

The device will restart then wait for a debugger to be connected:

make gdb

This will start a new debugging session where you can run your code interactively:

Remote debugging using /dev/ttyUSB0
gdbstub_init () at /home/slavey/dev/esp8266.dev.box/dev/Sming/Sming//gdb/gdbstub.cpp:914
914         gdb_do_break();
(gdb)

If the debugger is exited, the application will continue execution as normal. Re-connecting the debugger will pause execution.

GDB commands

There are multiple commands supported in GDB and we will mention only some of them.

List current source code

One possibility is to see the source code of the current line where the execution has stopped. To achieve this you should type list in the gdb console:

(gdb) list
909     SD(GDBSTUB_ENABLE_HOSTIO);
910 #undef SD
911
912 #if GDBSTUB_BREAK_ON_INIT
913     if(gdb_state.enabled) {
914         gdb_do_break();
915     }
916 #endif
917 }
918
Break the execution

This command will pause the debugger once it reaches a specific function or line in the code. This is called breakpoint and can be set like this:

(gdb) break blink
Breakpoint 1 at 0x40105d4c: file app/application.cpp, line 66.

Notice: break sets a software breakpoint. This means that the blink function must be in IRAM. Otherwise the execution will fail. If you take a look at samples/LiveDebug/app/application.cpp#L663, you will see a in the definition of the init function the following attribute GDB_IRAM_ATTR:

void GDB_IRAM_ATTR init()

This attribute is used to put the init function in IRAM when the code is compiled with the ENABLE_GDB=1 directive.

Continue the execution

To continue the execution of the application we can use the continue command:

(gdb) continue
Continuing.
LiveDebug sample
Explore some capabilities of the GDB debugger.

[OS] mode : sta..
...
[OS] cnt

Breakpoint 1, blink () at app/application.cpp:66
66 {
(gdb)

Because we have set already a breakpoint for the blink function the execution will be paused when the blink function is reached and from here you can go to the next line or see the current values of the variables.

Go to the next line

This can be done using next:

(gdb) next
67      digitalWrite(LED_PIN, ledState);
See variable value

The command to see a value is print followed by the name of the value. For example to see the value of the ledState variable inside the blink function we could type:

(gdb) print ledState
$1 = true

You can see more useful commands here.

Or watch the following short video

https://img.youtube.com/vi/hVwSX_7Ey8c/3.jpg
Debugging with visual debuggers like Eclipse CDT

A good visualization helps us understand things faster. What we can do is use Eclipse CDT and its debugging plugins to do remote debugging as we did from the command line.

Here is how this can be done:

  • Start Eclipse CDT and import the Live Debug sample:

    • Select File -> New -> Project -> C/C++ -> Makefile Project with Existing Code

    • Point Eclipse to the location of the LiveDebug sample

    • Import the Sming Framework (if you haven’t done it yet)

Import Project

Import Project

Once the two projects are in Eclipse, set the LiveDebug project to reference the Sming project.

Now create a new Remote Debugging Configuration:

  • Select Run -> Debug Configurations -> C/C++ Remote Application

  • Right-click and create a new C/C++ Remote Application

  • In the Main tab set, set:

    • Project: Basic_Build

    • C/C++ Application: out/build/Esp8266/Debug/app.out

    • disable for now the auto build

Remote Debugging Session

Remote Debugging Session

Then go to the Debugger tab and point the GDB debugger to your Xtensa-gdb binary. (Type make list-config and look for GDB.)

Remote Debugging Session

Remote Debugging Session

Make sure to load also GDB command file. To find out its location, run make list-config and look for GDBSTUB_DIR. The file is called gdbcmds, and you may wish to place a copy of the file somewhere else, especially if you intend to modify it. You can see the file here Sming/Arch/Esp8266/Components/gdbstub/gdbcmds.

Finally we should configure the remote connection. Go to the Debugger -> Connection tab and set:

  • type: Serial

  • device: /dev/ttyUSB0 (or as required for your operating system)

  • speed: 115200

Set remote connection

Set remote connection

We are now ready for debugging. Press the Debug button. (In the screenshot above the Debug button is in the bottom-right corner.) After some seconds your debugging session should be up and running and you can enjoy live debugging.

Live Debugging Session

Live Debugging Session

You will be able to see the current variables and their values. You should be able to go step by step, go inside of functions, add breakpoints to code in RAM or add breakpoints to code that was in FLASH, after it was executed executed at least once.

Debugging on ESP32
Serial debugging

If an exception occurs in debug builds then a prompt will be printed to the serial terminal such as Entering gdb stub.

As with the ESP8266, if such an exception occurs you can stop the serial debug terminal and type make gdb.

More advanced debugging is available via JTAG if you have the appropriate tools.

See https://docs.espressif.com/projects/esp-idf/en/release-v4.3/esp32/api-guides/fatal-errors.html for further details.

Required tools and hardware

A debugger and a JTAG hardware are required. The debugger is part of the provided toolchain. Make sure that you have the following executable in your PATH:

xtensa-esp32-elf-gdb

Debugging with JTAG is explained in details in the ESP-IDF documentation. Make sure to read it carefully.

For the purposes of this documentation we will be using ESP-Prog JTAG adapter and ESP32-Cam microcontroller from AI-Thinker.

Configure Hardware

The JTAG adapter has to be connected to your ESP32 microcontroller. The following pins from the JTAG adapter have to be connected to ESP32 for the communication to work.

ESP32 Pin

nodeMCU Pin

USB Blaster

JTAG Signal

1

VCC

3V3

4

VCC

2

MTDO / GPIO15

D15

3

TDO

3

MTDI / GPIO12

D12

9

TDI

4

MTCK / GPIO13

D13

1

TCK

5

MTMS / GPIO14

D14

5

TMS

6

GND

GND

2

GND

Running OpenOCD

Once the JTAG adapter is connected to the microcontroller and to a computer we have to start the OpenOCD server that will communicate with the JTAG adapter. For our specific hardware the following command has to be executed:

openocd -f interface/ftdi/esp32_devkitj_v1.cfg -f target/esp32.cfg

If you have configured your JTAG adapter correctly the following messages should show up:

Open On-Chip Debugger  v0.10.0-esp32-20190313 (2019-03-13-09:52)
Licensed under GNU GPL v2
For bug reports, read
    http://openocd.org/doc/doxygen/bugs.html
none separate
adapter speed: 20000 kHz
Info : Configured 2 cores
esp32 interrupt mask on
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : ftdi: if you experience problems at higher adapter clocks, try the command "ftdi_tdo_sample_edge falling"
Info : clock speed 20000 kHz
Info : JTAG tap: esp32.cpu0 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
Info : JTAG tap: esp32.cpu1 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
Info : esp32: Debug controller 0 was reset (pwrstat=0x5F, after clear 0x0F).
Info : esp32: Core 0 was reset (pwrstat=0x5F, after clear 0x0F).
Info : esp32: Debug controller 1 was reset (pwrstat=0x5F, after clear 0x0F).
Info : esp32: Core 1 was reset (pwrstat=0x5F, after clear 0x0F).
Info : Detected debug stubs @ 3ffb42ac on core0 of target 'esp32'
Info : Listening on port 3333 for gdb connections
Info : accepting 'gdb' connection on tcp/3333
Recompilation is required

In order to debug applications based on Sming Framework make sure that you are using Sming version 3.8.0 or newer.

Compilation directives

If you want to debug your application and the Sming Framework code make sure to (re)compile it with ENABLE_GDB =1 directive:

cd $SMING_HOME/../samples/Basic_Blink
make clean components-clean
make ENABLE_GDB=1

The commands above will re-compile Sming with debug symbols and optimizations for debugging.

Application

To use, (re)compile your application with the ENABLE_GDB option and flash it to the board. For this example we will use the Basic Blink sample application:

cd $SMING_HOME/../samples/Basic_Blink
make clean
make ENABLE_GDB=1 # -- recompiles your application with debugging support
make flashapp # flashes ONLY the (re)compiled application

The device will restart then wait for a debugger to be connected. Before starting the debugger you must be sure that the OpenOCD server is running and listening for incoming connections on localhost port 3333.

Now start the debugger:

make gdb

This will start a new debugging session. The debugger will try to connect to OpenOCD server and in the OpenOCD logs you should see a message similar to the one below:

Info : accepting 'gdb' connection on tcp/3333
Info : Target halted. PRO_CPU: PC=0x4012F7EE (active)    APP_CPU: PC=0x4012F7EE
Info : Target halted. PRO_CPU: PC=0x4009171A (active)    APP_CPU: PC=0x4012F7EE
Info : Flash mapping 0: 0x10020 -> 0x3f400020, 89 KB
Info : Flash mapping 1: 0x30018 -> 0x400d0018, 388 KB
Info : Target halted. PRO_CPU: PC=0x4009171A (active)    APP_CPU: PC=0x4012F7EE
Info : Auto-detected flash size 4096 KB
Info : Using flash size 4096 KB

And in the GDB console you will see a message similar to this one:

Reading symbols from out/Esp32/debug/build/app.out...done.
0x4012f7ee in is_wifi_clk_peripheral (periph=PERIPH_LEDC_MODULE)
    at /x/esp-idf/components/driver/periph_ctrl.c:225
225     switch(periph) {
JTAG tap: esp32.cpu0 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
JTAG tap: esp32.cpu1 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
esp32: Debug controller 0 was reset (pwrstat=0x5F, after clear 0x0F).
esp32: Core 0 was reset (pwrstat=0x5F, after clear 0x0F).
esp32: Debug controller 1 was reset (pwrstat=0x5F, after clear 0x5F).
esp32: Core 1 was reset (pwrstat=0x5F, after clear 0x5F).
Target halted. PRO_CPU: PC=0x5000004B (active)    APP_CPU: PC=0x00000000
esp32: Core 0 was reset (pwrstat=0x1F, after clear 0x0F).
Target halted. PRO_CPU: PC=0x40000400 (active)    APP_CPU: PC=0x40000400
Hardware assisted breakpoint 1 at 0x400e1cd3: file /x/Sming/Sming/Arch/Esp32/Components/esp32/startup.cpp, line 21.
(gdb)

If the debugger is exited, the application will continue execution as normal. Re-connecting the debugger will pause execution.

GDB commands

There are multiple commands supported in GDB and we will mention only some of them.

List current source code

One possibility is to see the source code of the current line where the execution has stopped. To achieve this you should type list in the gdb console:

(gdb) list
220
221 static bool is_wifi_clk_peripheral(periph_module_t periph)
222 {
223     /* A small subset of peripherals use WIFI_CLK_EN_REG and
224        CORE_RST_EN_REG for their clock & reset registers */
225     switch(periph) {
226     case PERIPH_SDMMC_MODULE:
227     case PERIPH_SDIO_SLAVE_MODULE:
228     case PERIPH_EMAC_MODULE:
229     case PERIPH_RNG_MODULE:
(gdb)
Break the execution

This command will pause the debugger once it reaches a specific function or line in the code. This is called breakpoint and can be set like this:

(gdb) break blink
Breakpoint 2 at 0x400e1dc4: file app/application.cpp, line 9.
Continue the execution

To continue the execution of the application we can use the continue command:

(gdb) continue
Continuing.
Target halted. PRO_CPU: PC=0x400E1DC4 (active)    APP_CPU: PC=0x4012F7EE
[New Thread 1073483724]
[New Thread 1073514968]
[New Thread 1073494600]
[New Thread 1073487892]
[Switching to Thread 1073412944]

Breakpoint 1, blink () at app/application.cpp:9
9 {
(gdb)

Because we have set already a breakpoint for the blink function the execution will be paused when the blink function is reached and from here you can go to the next line or see the current values of the variables.

Go to the next line

This can be done using next:

(gdb) next
10      digitalWrite(LED_PIN, state);
See variable value

The command to see a value is print followed by the name of the value. For example to see the value of the ledState variable inside the blink function we could type:

(gdb) print state
$1 = true

You can see more useful commands here.

Or watch the following short video

https://img.youtube.com/vi/hVwSX_7Ey8c/3.jpg
Debugging with visual debuggers like Eclipse CDT

A good visualization helps us understand things faster. What we can do is use Eclipse CDT and its debugging plugins to do remote debugging as we did from the command line.

Here is how this can be done:

  • Start Eclipse CDT and import the Basic Blink sample:

    • Select File -> New -> Project -> C/C++ -> Makefile Project with Existing Code

    • Point Eclipse to the location of the Basic_Blink sample

    • Import the Sming Framework (if you haven’t done it yet)

Import Project

Import Project

Once the two projects are in Eclipse, set the Basic_Blink project to reference the Sming project.

Now create a new Remote Debugging Configuration:

  • Select Run -> Debug Configurations -> C/C++ Remote Application

  • Right-click and create a new C/C++ Remote Application

  • In the Main tab set, set:

    • Project: Basic_Build

    • C/C++ Application: out/build/Esp8266/Debug/app.out

    • disable for now the auto build

Remote Debugging Session

Remote Debugging Session

Then go to the Debugger tab and point the GDB debugger to your Xtensa-gdb binary. (Type make list-config and look for GDB.)

Remote Debugging Session

Remote Debugging Session

Make sure to load also GDB command file. The file is called gdbinit, and you may wish to place a copy of the file somewhere else, especially if you intend to modify it. You can see the file here Sming/Arch/Esp32/Tools/gdbinit.

Finally we should configure the remote connection. Go to the Debugger -> Connection tab and set:

  • type: TCP

  • host: localhost

  • port: 3333

Set remote connection

Set remote connection

We are now ready for debugging. Press the Debug button. (In the screenshot above the Debug button is in the bottom-right corner.) After some seconds your debugging session should be up and running and you can enjoy live debugging.

Live Debugging Session

Live Debugging Session

You will be able to see the current variables and their values. You should be able to go step by step, go inside of functions, add breakpoints to code in RAM or add breakpoints to code that was in FLASH.

Debugging on RP2040

Currently debugging support on Sming is limited to serial output and inspecting disassembly using GDB.

See https://www.raspberrypi.org/documentation/microcontrollers/raspberry-pi-pico.html

rBoot and OTA updates

Introduction

rBoot is an open source bootloader for the ESP8266, which is now fully integrated with Sming. rBoot is a more flexible alternative to the closed source bootloader supplied by Espressif with the SDK. A bootloader allows you to have more than one application on the esp8266, either completely different apps, or different version of the same app, which can be updated over the air.

The example Basic Ota demonstrates the use of rBoot, but if you want to add it to an existing project this little tutorial will guide you.

Need to know
  • The esp8266 can only memory map 1MB of flash at a time, your ROM must fit within the a single 1MB block of flash.

  • Different 1MB blocks can be mapped at different times, so your ROM does not have to be within the first 1MB block. The support for this in rBoot is called big flash mode. A single compiled and linked ROM image can be placed at the same relative position in any 1MB block of flash.

  • If you have two smaller ROMs in a single 1MB of flash (your only option if you flash is 1MB or smaller) this is referred to as two ROM mode in rBoot. Each ROM needs to be separately linked to have the correct memory mapped flash offset. Examples are given below for common scenarios.

Building
  • The default rBoot options are perfect for a 4MB flash, so if this is what you have (e.g. an esp-12) you don’t need to do anything else. Otherwise see information below about configuration.

  • Run make as normal, rBoot and your app ROM will both be built for you.

  • Running make flash will flash rBoot and the first ROM slot.

Configuring for two ROM mode

If you have a 1MB flash, you will need to have two 512KB ROM slots, both in the same 1MB block of flash. You can accommodate this by setting the appropriate hardware configuration in your project’s component.mk file:

.. code-block:: make

HWCONFIG = two-rom-mode

See Sming/Arch/Esp8266/two-rom-mode.hw for details. You can copy this and customise it in your project.

SPIFFS

To use SPIFFS think about where you want your SPIFFS to sit on the flash.

If you have a 4MB flash the default position is for the first ROM to be placed in the first 1MB block and the second ROM to be placed in the third 1MB block of flash. This leaves a whole 1MB spare after each ROM in which you can put your SPIFFS. This is the behaviour when you set HWCONFIG = spiffs in your project’s component.mk file.

If you have a smaller flash the SPIFFS will have to share the 1MB block with the ROM. For example, the first part of each 1MB block may contain the ROM, and the second part the SPIFFS (but does not have to be split equally in half). So for the 4MB example you could put the SPIFFS for your first ROM at flash address at 0x100000 and the SPIFFS for your second ROM at 0x300000; in each case that is the 1MB block after the ROM.

To mount your SPIFFS at boot time add the following code to init:

     String name = F("spiffs");
     name += rboot_get_current_rom();
     auto part = Storage::findPartition(name);
if(part) {
   //debugf("trying to mount SPIFFS at %x, length %d", part.address(), part.size());
   spiffs_mount(part);
} else {
   debug_e("SPIFFS partition missing for slot #%u", slot);
}
Over-the-air (OTA) updates

Instead of insisting on a “one-solution-fits-all” approach, Sming provides you with the ingredients to build an OTA upgrade mechanism tailored to your application. This involves selecting a transport protocol and a backend that interacts with the flash memory. Any protocol from Sming’s rich set of network classes can be used, ranging from raw TCP sockets to HTTP, FTP, MQTT, with or without SSL, etc. To conserve program memory, you might prefer a protocol already employed by your application.

On the backend side, there is OtaUpgradeStream from the Over-the-Air Firmware Upgrade library, which supports multiple ROM images in one upgrade file, as well as state-of-the-art security features like a digital signatures and encryption. Check out the HttpServer Firmware Upload example, which demonstrates a browser-based firmware upgrade mechanism similar to what is found in many consumer products.

A more lightweight solution is provided by RbootOutputStream, which is just a thin wrapper around rBoot’s flash API, in combination with RbootHttpUpdater, which pulls individual ROM image from an HTTP server.

Tips and Tricks

Reading VCC on ESP8266

If you are running on a battery operated device then function system_get_vdd33() from the official ESP8266 NONOS SDK can help you read the power voltage. For the latter to work properly you should make a small change in your application component.mk file and add vdd to the HWCONFIG_OPTS configuration variable.

If you cannot see such a variable in your component.mk file then append the following line to it:

HWCONFIG := vdd

You can have multiple options selected. They should be separated by comma. For example the command below will add 4MB flash, spiffs and vdd support:

HWCONFIG := 4m,spiffs,vdd

You can check the final hardware configuration using the command below:

make hwconfig

If a custom hardware configuration is needed then read Storage Management.

Minimising Memory Usage

Literals use up memory, so its a good idea to move them to flash. See Program Space and FlashString.

Webpages and Spiffs

FlashString turns out to be very useful for sending web pages, javascript, CSS and so on. Many examples for the ESP8266 exist where a Spiffs file system is used for this purpose, but in fact Spiffs is not ideal. If you want to release a new version of your software, and your web pages are in spiffs, you now have two things to release, so there is double the chance of something going wrong. Plus you have the challenge of preserving any user files while refreshing just a few.

One solution is to use a FlashString hooked up to a FlashMemoryStream instead. In the example below, the CSS file is sent compressed to save time and space. The browser asks for core.js and gets a compressed version:

IMPORT_FSTR(flash_corejsgz, PROJECT_DIR "/web/build/core.js.gz")

void onSendCore_js(HttpRequest &request, HttpResponse &response)
{
    response.headers[HTTP_HEADER_CONTENT_ENCODING] = _F("gzip");
    auto stream = new FlashMemoryStream(flash_corejsgz);
    response.sendDataStream(stream, MimeType::MIME_JS);
}

See FlashString for further details.

Webpages Performance

HTML markup can get quite large and the bigger the file the slower the page loads. One way to deal with that is to remove the white space, this process is called minifying. The downside is that the result is difficult for a human to read. I recommend against it, at least in the early stages of your project.

To support the HTML files there are CSS files and JS files, which must be kept locally on the server if one wants things to work even when the internet is absent.

I use the bootstrap library and the CSS I write goes into another special file. The file count is now three, an HTML file and two CSS files. This is already a lot of files for a microcontroller to deal with especially if it gets download requests for all three at once. A browser will start a download request for each file it sees, and for the ESP, any more than three is a problem, meaning we need to keep this under control.

One way to deal with that is to combine the CSS files together into one.

Next we have JavaScript files which includes the custom code, the bootstrap library and the jquery library. Three extra files. Once again we can deal with these by combining them into one, in which We are back to having 3, one HTML file one CSS file and one JavaScript file.

But the files are big and this is a problem not just because it is slow. The watchdog does not like things to take a long time, and you will almost certainly end up with a timeout.

When a browser asks for a file it doesn’t mind receiving a compressed version using gzip. (Note that you need to add “Content-Encoding/gzip” to the header in the response from the server). Using gzip vastly reduces the sizes of files and it’s well worth doing.

Another size optimisation for CSS files is to remove unused CSS (UNCSS) - I recommend against this as it was too aggressive at removing stuff I really needed - YMMV.

I use gulp to automate the extraction and concatenation and compression of the CSS and JS files, here is the relevant part of my gulpfile.js:

function htm() {
   return gulp.src(htmConfig.src)
      .pipe(useref())
      .pipe(gzip())       // compresses to a gzip file
      .pipe(size({ showFiles: true }))
      .pipe(gulp.dest('web/build/'))
 }

My webpage looks like this

<!-- build:css core.css -->
<link rel="stylesheet" type="text/css" href="bootstrap.css">
<link rel="stylesheet" type="text/css" href="style.css">
<!-- endbuild -->

After gulp runs it looks like this

<link rel="stylesheet" href="core.css">

Upgrading

If you are migrating from version older than 3.8 you should read the release log to find the needed steps to migrate. For newer versions we have dedicated pages.

From v5.1 to v5.2

Breaking change

The Storage::PartitionStream constructors with blockErase parameter have been deprecated. The intended default behaviour is read-only, however previously this also allowed writing without block erase. This can result in corrupted flash contents where the flash has not been explicitly erased beforehand.

The new constructors instead use a Storage::Mode so behaviour is more explicit. The default is read-only and writes will now be failed.

From v4.7 to v5.1

Command Processing

The CommandProcessing service has been refactored and moved to a component. This means that the following classes CommandHandler, CommandExecutor and CommandOutput are no longer available.

Enabling

The command processing component used to be enabled by setting the directive ENABLE_CMD_EXECUTOR to 1 in your component.mk file or during compilation. This has to be replaced with the directive COMPONENT_DEPENDS += CommandProcessing in your component.mk file.

Including Header Files

To include the command processing headers in your C/C++ application we used to do the following

For example:

#include <Services/CommandProcessing/CommandProcessingDependencies.h>

becomes:

#include <CommandProcessing/Utils.h>
Usage

There is no longer a global instance of commandHandler. This means that you will need to create one yourself when you need it. This can be done using the code below:

CommandProcessing::CommandHandler commandHandler;

In order to register a command the old example code:

commandHandler.registerCommand(
        CommandDelegate("example", "Example Command", "Application", processExampleCommand));

becomes:

commandHandler.registerCommand(
        CommandProcessing::Command("example", "Example Command", "Application", processExampleCommand));

HardwareSerial no longer is dependent on CommandProcessing classes. And the following command will no longer work:

Serial.commandProcessing(true);

The line above has to be replaced with:

CommandProcessing::enable(commandProcessing, Serial);

See the modified samples CommandLine, TelnetServer and HttpServer Websockets for details.

From v4.6 to v4.7

Storage Partition methods

The Storage::Partition::getDevice() method has been removed because it could be used to bypass protections offered by the partitioning API.

Storage Device Partitions

The CustomDevice class has been removed as it is simpler and more flexible to instead use PartitionTable methods.

The Storage::Device::partitions() method returns a read-only (const) Storage::PartitionTable object for general use to avoid inadvertent modification.

Use the Storage::Device::editablePartitions() method to make partition table changes.

For example:

part = device->createPartition("archive", Storage::Partition::SubType::Data::fwfs, startOffset, size);

becomes:

part = device->editablePartitions().add("archive", Storage::Partition::SubType::Data::fwfs, startOffset, size);
Custom Partition Types

Creating custom partition types now require use of Storage::Partition::FullType.

For example:

part = device->createPartition("fs_app", Storage::Partition::Type::data, 100, startOffset, size);

becomes:

part = device->editablePartitions().add("fs_app", {Storage::Partition::Type::data, 100}, startOffset, size);

Note how the type and subtype values are enclosed in braces (instantiating a FullType struct). This avoids confusing the subtype value 100 with the start offset.

From v4.5 to v4.6

SPI Byte/Bit ordering

The SPISettings class has been revised to be consistent with Arduino ESP8266, ESP32, RP2040, etc. which provide a bitOrder setting but not byteOrder.

Bytes are now always sent LSB first, which corresponds with the system endianness. Bit order simply indicates how the bits within each byte are sent on the wire. This is predominantly MSB first, the default.

The SPISoft class has been upgraded to support bit ordering, transactions and includes automatic delay calculation for the ESP8266.

See Issue #1428.

From v4.4 to v4.5

Template Streams

The TemplateStream class has been updated to improve tag recognition (Pull Request #2400). This means regular {varname} tags are sufficient for most purposes, including JSON templates.

The IFS::JsonDirectoryTemplate previously used double-brace tags such as {{varname}}. It now uses regular tags by default, so if you use this class either:

  1. Add a call to TemplateStream::setDoubleBraces() in your code, or

  2. Update your templates to use single braces

Eclipse Project Files

Starting with version 4.5.x we don’t provide Eclipse meta files in our samples. These can be generated using the ide-eclipse makefile target. For more information read the updated Using with Eclipse CDT.

Esp8266 toolchain

Sming now requires the ESP Quick Toolchain for building.

Support for the old legacy toolchains (ESP open SDK, UDK) have been dropped. They may still work but are no longer tested.

user_config.h

This header file is part of the original ESP8266 SDK code and is now deprecated. Libraries should use only the necessary headers. Applications do not require it.

Breaking Changes

Undeprecated HttpRequest::getQueryParameter() and removed a lot of old deprecated code.

  • Removed WebsocketClient::disconnect(). Use WebsocketClient::close() instead.

  • Removed TimerDelegateStdFunction. Use TimerDelegate instead.

  • Removed class URL. Use class Url instead.

  • Removed TemplateVariables. Use TemplateStream::Variables instead.

  • Removed StreamTransformer::transformCallback. Create inherited class and override StreamTransformer::transform() method instead.

  • Removed StreamTransformer::StreamTransformer(IDataSourceStream* stream, const StreamTransformerCallback& callback, size_t resultSize, size_t blockSize). Instead, create inherited class, override StreamTransformer::transform() method and use alternative constructor.

  • Removed SslValidatorCallback. Use Ssl::ValidatorCallback instead.

  • Removed SslSessionId and SSLSessionId. Use Ssl::SessionId instead.

  • Removed SslKeyCertPair and SSLKeyCertPair. Use Ssl::KeyCertPair instead.

  • Removed SslCertificate. Use Ssl::Certificate instead.

  • Removed SeekOriginFlags. Use SeekOrigin instead.

  • Removed eSO_FileStart. Use SeekOrigin::Start instead.

  • Removed eSO_CurrentPos. Use SeekOrigin::Current instead.

  • Removed eSO_FileEnd. Use SeekOrigin::End instead.

  • Removed OtaUpgrade::BasicStream::errorToString(). Use toString() instead.

  • Removed deprecated stuff in Mqtt, including MQTT_MAX_BUFFER_SIZE and MQTT_MSG_PUBREC.

  • Removed MqttClient::publishWithQoS(const String& topic, const String& message, int QoS, bool retained, MqttMessageDeliveredCallback onDelivery). Use bool MqttClient::publish(const String& topic, const String& message, uint8_t flags) instead. If you want to have a callback that should be triggered on successful delivery of messages, use MqttClient::setEventHandler().

  • Removed MqttClient::setCallback(MqttStringSubscriptionCallback subscriptionCallback). Use MqttClient::setEventHandler() instead.

  • Removed MqttClient::setWill (const String& topic, const String& message, int QoS, bool retained). Use MqttClient::setWill() instead.

  • Removed MqttMessageDeliveredCallback and MqttStringSubscriptionCallback. Use MqttDelegate instead.

  • Removed IDataSourceStream::length(). Use IDataSourceStream::available() instead.

  • Removed HttpServer::setDefaultResource(HttpResource* resource). Use paths.setDefault() instead.

  • Removed HttpServer::addPath(String path, const HttpPathDelegate& callback), HttpServer::addPath (const String& path, const HttpResourceDelegate& onRequestComplete) and HttpServer::addPath (const String& path, HttpResource* resource). Use paths.set() instead.

  • Removed HttpResponse::toString(const HttpResponse& res). Use :HttpResponse::toString() method or toString() global function instead.

  • Removed HttpResponse::sendTemplate(IDataSourceStream* newTemplateInstance). Use HttpResponse::sendNamedStream() instead.

  • Renamed commandFunctionDelegate to CommandFunctionDelegate.

  • Removed DateTime::convertFromUnixTime(time_t timep, int8_t* psec, int8_t* pmin, int8_t* phour, int8_t* pday, int8_t* pwday, int8_t* pmonth, int16_t* pyear). Use DateTime::fromUnixTime() instead.

  • Removed DateTime::convertToUnixTime (uint8_t sec, uint8_t min, uint8_t hour, uint8_t day, uint8_t month, uint16_t year). Use DateTime::toUnixTime() instead.

  • Removed DateTime::fromUnixTime(time_t timep, int8_t* psec, int8_t* pmin, int8_t* phour, int8_t* pday, int8_t* pwday, int8_t* pmonth, int16_t* pyear). Use unsigned version instead void DateTime::fromUnixTime(time_t, uint8_t*, uint8_t*, uint8_t*, uint8_t*, uint8_t*, uint8_t*, uint16_t*).

  • Removed DateTime::parseHttpDate(const String& httpDate). Use DateTime::fromHttpDate() instead.

  • Renamed DNSServer class to DnsServer.

  • Removed eFO_Append. Use File::Append instead.

  • Removed eFO_CreateIfNotExist. Use File::Create instead.

  • Removed eFO_CreateNewAlways. Use File::CreateNewAlways instead.

  • Removed eFO_ReadOnly. Use File::ReadOnly instead.

  • Removed eFO_ReadWrite. Use File::ReadWrite instead.

  • Removed eFO_Truncate. Use File::Truncate instead.

  • Removed eFO_WriteOnly. Use File::WriteOnly instead.

  • Removed eSO_FileStart. Use SeekOrigin::Start instead.

  • Removed eSO_CurrentPos. Use SeekOrigin::Current instead.

  • Removed eSO_FileEnd. Use SeekOrigin::End instead.

  • Removed fileList() function. Use Directory object, or fileOpenDir() / fileReadDir() / fileCloseDir() functions.

  • Removed FileStream::attach(const String& fileName, FileOpenFlags openFlags=File::ReadOnly). Use FileStream::open() instead.

  • Removed FTPServer. Use FtpServer instead.

  • Removed FtpServer::checkUser(const String& login, const String& pass). Use FtpServer::validateUser() instead

  • Renamed Hardware_Timer to HardwareTimer.

  • Renamed HardwareSerial::setCallback(StreamDataReceivedDelegate dataReceivedDelegate) to HardwareSerial::onDataReceived().

  • Removed HttpClient::request(const String& url). Use HttpClient::createRequest() instead.

  • Removed HttpConnection::getLastModifiedDate(). Use getResponse()->headers.getLastModifiedDate() instead.

  • Removed HttpConnection::getResponseCode(). Use getResponse()->code instead.

  • Removed HttpConnection::getResponseHeader(const String& headerName, const String& defaultValue). Use getResponse()->headers[] instead.

  • Removed HttpConnection::getResponseHeaders(). Use getResponse()->headers instead.

  • Removed HttpConnection::getResponseString (). Use ``getResponse()->getBody() instead.

  • Removed HttpConnection::getServerDate (). Use ``getResponse()->headers.getServerDate() instead.

  • Removed httpGetErrorName (HttpError err). Use toString() instead.

  • Renamed HttpPartProducerDelegate type to MultipartStream::Producer.

  • Renamed HttpPartResult type to MultipartStream::BodyPart.

  • Removed HttpRequest::getPath(). Use request.uri.Path instead.

  • Removed HttpRequest::operator=(const HttpRequest& rhs). Use HttpRequest::clone() instead.

  • Removed HttpRequest::setPostParameters(const HttpParams& params). Use request.postParams = params instead.

  • Removed HttpResponse::hasHeader(const String& name). Use headers.contains() instead.

  • Removed HttpResponse::forbidden(). Use response.code = HTTP_STATUS_FORBIDDEN instead.

  • Removed HttpResponse::notFound(). Use response.code = HTTP_STATUS_NOT_FOUND instead.

  • Removed HttpResponse::redirect(const String& location). Use headers[HTTP_HEADER_LOCATION] instead.

From v4.3 to v4.4

Network support

If WiFi is not required then the DISABLE_WIFI setting can be used to reduce code size. This has a more pronounced effect for the ESP8266 which uses an experimental library.

The core network code has been moved out of Sming/Core/Network and into a separate component at Components/Network. Some support code has been moved into Core/Data/WebHelpers: applications should still build OK but you will get a compiler warning advising of the changes.

Note that Network/WebHelpers/aw-sha1.h has been removed in favour of the Cryptographic Support library.

Ethernet support has been added, currently only for the ESP32 embedded MAC. If WiFi is not used then the DISABLE_WIFI setting can be used to reduce code size.

The DISABLE_NETWORK setting can be used to exclude all networking support for significant code size reduction.

ESP32

If you have made use of Sming’s experimental ESP32 support then you will need to update the ESP IDF SDK and tools to version 4.3. See Sming Esp32 Architecture.

From v4.2 to v4.3

Storage Management

The layout of flash memory has been revised and is now managed via partition table. Hardware configuration is stored in a JSON file (with .hw extension).

If your project has minimal customisation then you may only need to change the HWCONFIG setting.

You can find full details in the Storage Management library. See also background on Flash memory.

Removed build targets
spiffs-image-update

Use the new buildpart target instead

spiffs-image-clean

Use the new part-clean target instead

New and updated build targets
hwconfig

Displays the current configuration in JSON format

hwconfig-list

Show available hardware configs

hwconfig-options

Show available hardware configuration options

map

Display the current flash memory map

readmap

Read partition table from device and display the map

readpart

Read contents of partition into a file, e.g. make readpart PART=spiffs0 will create out/Esp8266/debug/spiffs0.read.bin

flash

Flash partition table and all partitions. Previously it was necessary to run make flashinit to write system parameter information. Failure to do this was a common problem and should now be a thing of the past.

flashinit

This now just erases the flash memory. The ESP8266 initialisation data gets written when running make flash.

flashmap

Flash just the partition map

flashpart

Flash a single partition, e.g. make flashpart PART=spiffs0

erasepart

Erase a partition, e.g. make erasepart PART=spiffs0

buildpart

Re-builds images associated with partitions, such as SPIFFS or other filesystem images.

part-clean

Removes any partition images with build information. This is done as part of a normal project clean.

Configuration variables

A number of configuration variables have been removed or made read-only, as these are now generated from the Hardware configuration.

SPI_MODE, SPI_SIZE, SPI_SPEED

The variables are still used internally but are read-only; they cannot be set at the command line. Values are read from the hardware configuration under /devices/spiFlash.

RBOOT_ROM0_ADDR, RBOOT_ROM1_ADDR, RBOOT_ROM2_ADDR

Used by rBoot, and are now read-only. Values are read from the address property of rom0-2 in the hardware configuration.

RBOOT_SPIFFS_0, RBOOT_SPIFFS_1

Removed.

SPIFF_SIZE

Removed. Attempting to set this automatically within a hardware configuration is liable to cause more problems than it solves, so updating the hardware config is the now only way to change this setting.

SPIFF_FILES

[deprecated]

You can still use this to specify the source location for the primary SPIFFS partition (spiffs0). The preferred method is to set the files property in a partition build key.

The default SPIFFS partition settings can be overridden in a custom profile. For example:

{
    ...
    "base_config": "spiffs",
    "partitions": {
        "spiffs0": {
            "size": "128K",
            "build": {
                "files": "some/other/folder"
            }
        }
    }
}
Installable File System (IFS)

Sming now supports multiple filesystems via Installable File System.

See Basic IFS for a demonstration.

Core/FileSystem.h has been modified to use IFS but the API remains largely unchanged, although somewhat expanded. Functions are now mainly just wrappers around filing system calls.

A single global IFileSystem instance is used.

SPIFFS

All access is now managed via the IFS::SPIFFS::FileSystem implementation.

Applications should not use SPIFFS functions directly.

Important

SPIFFS is now built with SPIFFS_OBJ_META_LEN=16 to store extended attribute information. Existing volumes built with other values will not be directly compatible; the file listing may be correct but file contents will not.

To accommodate use of existing pre-built SPIFFS images, SPIFFS_OBJ_META_LEN has been added:

make SPIFFS_OBJ_META_LEN=0

You will, however, lose the additional file information (such as modification time).

File open flags

e.g. eFO_ReadOnly. These will still work but are now deprecated and should be replaced with their C++ equivalent such as File::ReadOnly.

From v4.1 to v4.2

Summary
Stream methods

The Stream::readBytes() has been virtualised and overridden for IDataSourceStream descendents for more efficient operation, especially with ArduinoJson. For normal read operations where the stream position is to be updated, applications should use this method in preference to IDataSourceStream::readMemoryBlock().

An addition method IDataSourceStream::moveString() has been added to support extracting the content of a memory-based stream into a String object without duplicating the data. This is supported by LimitedMemoryStream and MemoryDataStream.

Stream / file seeking

To provide a consistent interface SeekOrigin is now used with all seek methods for streams, and also by file system functions. Mappings are:

SeekOrigin::Start instead of eSO_FileStart
SeekOrigin::Current instead of eSO_CurrentPos
SeekOrigin::End instead of eSO_FileEnd

These map to the standard C SEEK_SET, SEEK_CUR and SEEK_END but as SeekOrigin is strongly typed it offers compile-time checking, and has a toString(SeekOrigin) overload.

getBody methods

The HttpRequest::getBody() and HttpResponse::getBody() methods have been revised to use move semantics. Previously, the data was copied into a new String which effectively doubled memory usage.

If you have set a non-memory stream type (e.g. FileStream) which does not implement IDataSourceStream::moveString() then an invalid String will be returned. In this situation you should use HttpResponse::getBodyStream() instead.

ContentType / MIME types

String toString(MimeType)() has been moved out of the ContentType namespace, so no longer requires the ContentType:: qualifier.

From v4.0 to v4.1

Summary

With Sming version 4.1 there are some backwards incompatible changes. This page is provided to help with migrating your applications.

SSL

In version 4.1 one can choose between different SSL implementations. At the moment Sming supports axTLS and BearSSL for creating SSL enabled clients and servers.

In order to allow multiple SSL adapters and seamless integration the library code had to be refactored and that introduced some breaking changes.

Detailed documentation can be found in SSL: Secure Sockets Layer.

See SSL: Upgrading for a migration guide.

MultipartParser

The MultipartParser component has been decoupled from the framework and converted into a Library. In the process, the former config option ENABLE_HTTP_SERVER_MULTIPART has been removed. Therefore, in your components.mk, replace:

ENABLE_HTTP_SERVER_MULTIPART := 1

by:

ARDUINO_LIBRARIES += MultipartParser

Also, the body parser for handling HTTP requests of content-type multipart/form-data must now be registered explicitly by the application code:

#include <MultipartParser.h>

HttpServer server;
...

server.setBodyParser(MIME_FORM_MULTIPART, formMultipartParser);

From v3.8 to v4.0

Summary

With Sming version 4.0 there are some backwards incompatible changes. This page is provided to help with migrating your applications.

In particular, the build system has changed so the process is now driven from the project directory.

See Sming build system for detailed information and a list of known issues.

Header files

The folder structure has been revised to provide support for additional architectures, such as the Host Emulator, and in the future the ESP32.

The Sming/Arch directory should never be accessed directly: it contains specific files for the target architecture, and may provide different header file versions to the main ones. Instead, consider these directories to be your ‘root’ include directories:

Sming
Sming/Components
Sming/Core
Sming/Wiring

Examples of #include statements:

Old-style

Recommended

"SmingCore/SmingCore.h"

<SmingCore.h>

"SmingCore/Network/SmtpClient.h"

<Network/SmtpClient.h>

"SmingCore/Data/Stream/FlashMemoryStream.h"

<Data/Stream/FlashMemoryStream.h>

"Wiring/WString.h"

<WString.h>

"SmingCore/Platform/Station.h"

<Platform/Station.h>

Changed Headers

If you use some of the includes below directly in your application make sure to apply the following changes:

Description

Old name

New name

uart driver

"espinc/uart.h"

<driver/uart.h>

flesh memory

"flashmem.h"

<esp_spi_flash.h>

C compatible types

<espinc/c_types_compatible.h>

<c_types.h>

user_include.h

This file is generally #included ahead of everything else so that common architecture-specific definitions are available. Unless you’ve made changes to the file, it is not required and you should delete it: Sming provides a default which will be used.

If you have made customisations, please amend the file as follows:

#pragma once
#include_next <user_config.h>

<< User Customisations here >>

If you’re using #include <SmingCore.h> then you don’t need #include <user_config.h> as this is included automatically.

Application Makefile
  • Rename Makefile-user.mk file to component.mk.

  • Replace the file Makefile with the one from the Basic_Blink sample project. If you’ve ignored the instructions and modified the file (!) then you’ll need to move those changes into your new component.mk file instead.

  • Sming uses the #pragma once statement for header guards, so consider updating your own files if you’re not already doing this.

Arduino Libraries

Your project must specify which Arduino Libraries it uses (if any). Do this by setting ARDUINO_LIBRARIES in your project’s component.mk file. For example:

ARDUINO_LIBRARIES := OneWire

This change means only the libraries you require for a project need to be built.

The following libraries have changes in their API:

JSON

ArduinoJson is now an optional Component, so you need to make a couple of changes to use it:

  • Add ArduinoJson6 to the ARDUINO_LIBRARIES variable in your project’s component.mk file. (e.g. ARDUINO_LIBRARIES = ArduinoJson6) To support migration of existing projects, you can elect to continue using version 5 by specifying ArduinoJson5 instead.

  • Add #include <JsonObjectStream.h> to your source code. If you’re not using the stream class, add #include <ArduinoJson.h> instead.

See library documentation for further details:

WiFi Classes
Additions

New class to handle MAC addresses: Sming/Wiring/MacAddress.h.

Sming/Platform/Station.h
  • getConnectionStatusName() returns String instead of char*

  • EStationConnectionStatus renamed to StationConnectionStatus

Sming/Platform/WifiEvents.h
  • Callback handler parameter lists have changed

Hardware Timers

The following functions have been removed from HardwareTimer.h:

  • usToTimerTicks

  • timerTicksToUs

Their presence is confusing. The ESP8266 has two hardware timers:

Timer1:

A 23-bit count-down timer accessed via the HardwareTimer class. Use the usToTicks and ticksToUs methods.

Timer2

A 32-bit counter accessed bia the ElapseTimer class. The current tick value is obtained using the NOW() macro. The clock frequency is defined by HW_TIMER2_CLK (in driver/hw_timer.h) and depends on the USE_US_TIMER compiler flag.

Deprecated / Changed types

Deprecated types will generate a compiler warning. See Deprecated List.

Experimental Stuff

Enabling SSL in HttpServer

  • At the moment any TCP based server in Sming can use TLS/SSL.

  • That applies to HttpServer (also with Websockets).

  • But make sure to read the security considerations and limitations.

Enabling SSL in HttpServer

The listen method in the TcpServer, and the child HttpServer class, accepts a second optional parameter. If you look at the original code: samples/HttpServer_WebSockets/app/application.cpp#L95-L99.

That can be changed to something like:

void startWebServer()
{
    // TODO: Make sure to set a server certificate and key
    server.listen(443, true);

And what is left is the actual setting of the server certificate:

void startWebServer()
{
    // Assign the certificate
    server.setSslInitHandler([](Ssl::Session& session) {
         session.keyCert.assign(serverKey, serverCert);
    });
    server.listen(443, true);

The final code can be something like:

void startWebServer()
{
#ifdef ENABLE_SSL
  server.setSslInitHandler([](Ssl::Session& session) {
    session.keyCert.assign(serverKey, serverCert);
  });
  server.listen(443, true);
#else
  server.listen(80);
#endif
  server.paths.set("/", onIndex);
  //...
Security Considerations

Does it really make sense to use SSL for an HttpServer on an ESP8266 device?

The certificate/private key pair should make it impossible for an external user to decrypt your traffic so that the things that you sent are kept private, but there are some complications with this:

  • The private key will not stay private for long. The private key should be kept encrypted on the flash memory, to prevent casual reading. But even with decryption there is a high probability that someone will be able to disassemble your application and figure out how to decrypt the key.

  • Costs for certificate. Let’s imagine that you have overcome the first issue. Then comes the second issue - if you want your users to accept the certificate it has to be signed by one of the trusted certificate authorities. And that costs money. And if you want to use a unique certificate/private key pair for every device than it will make things worse, moneywise. Note: Free SSL certificates are now available, for example https://letsencrypt.org/. These will expire if not kept up to date so adds additional complexity to your application.

  • You can handle up to 2 or maximum 3 connections. SSL needs 16K of memory to make the initial handshake. The memory consumption after a successful handshake can decrease to 4K, just for the SSL, per request. But realistically this means that you will end up with a server that can handle maximum 2 or 3 simultaneous connections before the heap memory is consumed and has to be released.

Therefore, in our humble opinion, it would be better to rely on the WIFI security that your Access Point (AP) provides and make this AP accessible only for your IoT devices.

Signed OTA Updating

Introduction

Deploying embedded devices with (automatic) OTA functionality introduces new risks to local networks and the whole internet. If an attacker takes over the update server or runs a MITM attack, he might be able to turn the devices into zombies.

To prevent this, you can either provide a secure connection between device and update server (e. g. VPN or TLS) or add a cryptographic signature to all OTA files. Pull Request #893 provides hooks to the OTA functionality to allow checking of such signatures.

A proven method for this is, for example, ECDSA in conjunction with SHA-256. For both steps libraries are available (micro-ecc and Arduino Cryptosuite).

To use it, you can subclass RbootOutputStream like this:

#define PREFIX_MAGIC    0x54, 0x49, 0x55, 0x53
#define PREFIX_TYPE     0x00, 0x01
#define PREFIX_SIZE     sizeof(_my_prefix)
#define SIGNATURE_SIZE  64

const u8 _my_prefix[6] = { PREFIX_MAGIC, PREFIX_TYPE };

struct MyHdr {
    u8  prefix[PREFIX_SIZE];
    u8  signature[SIGNATURE_SIZE];
};

//-----------------------------------------------------------------------------
class MyStream : public RbootOutputStream {
public:
   MyStream(uint32_t startAddress, size_t maxLength = 0): RbootOutputStream(startAddress, maxLength)
   {
      // do some initialization if needed.
   }

   size_t write(const uint8_t* data, size_t size) override;
   bool close() override;
   virtual ~MyStream()
   {
     delete sha256;
   }

protected:
    bool init() override;

private:
    Sha256 *sha256 = nullptr;
    u8      hdr_len;
    MyHdr   hdr;
};

//-----------------------------------------------------------------------------
bool MyStream::init() {
    RbootOutputStream::init();
    delete sha256;
    sha256  = new Sha256;
    hdr_len = 0;
}

size_t MyStream::write(const uint8_t* data, size_t size) {
    //  store header
    u8 missing = sizeof(hdr) - hdr_len;
    if (missing) {
        if (size < missing) missing = size;
        memcpy( &hdr, data, missing );
        size    -= missing;
        data    += missing;
        hdr_len += missing;

        //  check prefix
        if ( hdr_len >= PREFIX_SIZE ) {
            if ( memcmp(hdr.prefix, _my_prefix, PREFIX_SIZE) ) {
                debugf("invalid prefix");
                return 0;
            }
        }
    }

    //  update hash
    sha256->update(data, size);

    //  save data
    return RbootOutputStream::write(data, size);
}

bool MyStream::close() {
    if (!RbootOutputStream::close()) {
      return false;
    }

    u8 hash[SHA256_BLOCK_SIZE];
    sha256->final( hash );

    bool sig_ok = /* add signature check here */;
    if (!sig_ok) {
        debugf("wrong signature");
        // TODO: if needed delete the block at the startAddress
        return 0;
    }
    return 1;
}

And then in your application you can use your MyStream with the following setup:

RbootHttpUpdater* otaUpdater = new RbootHttpUpdater();

MyStream* stream = new MyStream(1234); // Replace 1234 with the right start address

otaUpdater->addItem(ROM_0_URL, new MyStream()); // << the second parameter specifies that your stream will be used to store the data.

// and/or set a callback (called on failure or success without switching requested)
otaUpdater->setCallback(OtaUpdate_CallBack);

Simulator: Wokwi

  • At the moment Sming and Wokwi have experimenta support for the Esp32 architecture.

  • Only VS Code IDE is supported

  • The Esp32 simulator lacks certain features and may encounter failures; hence, it is recommended for use with simple applications.

Wokwi Support into VS Code

To integrate Wokwi support into your VS Code, run the following command from your application’s root directory:

make ide-vscode ENABLE_WOKWI=1 SMING_ARCH=Esp32
Merge all partitions to a single flash file

From the root directory of your application, run the following command to consolidate all partitions for the simulator and flash them into a single file:

make mergeflash SMING_ARCH=Esp32
Usage
Debugging

Running the Basic_Blink sample in the simulator enables you to debug it directly in VS Code. Set a breakpoint in the init function in the Basic_Blink app/application.cpp file. Press F1 and select “Start Simulator and Wait for Debugger.” In the Launch configurations, choose “Wokwi GDB” and click the play button. This initiates a new debugging session, allowing you to debug the code running in the simulator.

_images/wokwi-debug.jpg
Diagram Editor

The diagram.json file, which includes elements and their connections, can be edited on the Wokwi official website. You can add new elements such as extra LEDs or servos. Ensure to copy the modified contents of the diagram.json from the website to your local environment.

Contributing

How you can help

You can contribute to Sming by

  • Providing Pull Requests with new features, bug fixes, new ideas, etc.

  • Testing our latest source code and reporting issues.

  • Supporting us financially to acquire hardware for testing and implementing or out of gratitude.

See Developing for Sming for details.

Financial contributions

We welcome financial contributions in full transparency on our open collective page. They help us improve the project and the community around it. If you would like to support us you can become a backer or a sponsor.

In addition to that anyone who is helping this project can file an expense. If the expense makes sense for the development of the community, it will be “merged” in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed.

Backers and sponsors

Thank you to all the people who have backed Sming or sponsored it.

Tools Integration

Details of Sming support for Integrated Development Environments (IDEs).

Using with CLion

Note

This information is no longer current.

However, if you are able to set up an external make project with CLion that should work.

To get intelliense (or whatever the equivalent for CLion is) will require setting up a list of the correct #include paths. For vscode we have a tool to handle that:

  • run make ide-vscode from a sample project directory

  • examine generated path list in .vscode/c_cpp_properties.json

Something similar could be done for CLion perhaps?

Developing with the Sming framework can also be done in CLion.

  1. Copy and paste Makefiles into the project

  2. Create app folder and add application.cpp there

  3. Edit CMakeLists.txt

## Edit CMakeLists.txt

include_directories("/opt/Sming/Sming")
include_directories("/opt/Sming/Sming/Libraries")
include_directories("/opt/Sming/Sming/system/include")
include_directories("/opt/esp-open-sdk/sdk/include")

set(SOURCE_FILES app/application.cpp)
add_executable(Pathfinder ${SOURCE_FILES})
Build the project using terminal

make && make flash

Using with Eclipse CDT

The Eclipse CDT Project provides a fully functional C and C++ Integrated Development Environment based on the Eclipse platform. Eclipse is a free (as in “free beer”) and Open Source code editor for Windows, Linux and Mac.

For easier integration make sure you have both ESP_HOME and SMING_HOME exported in your working environment.

Software involved
Installation
  • Install Eclipse CDT using your operating system packaging tools.

Configuration

First you should import the Sming project. This can be done by going to menu File -> Import and from there choosing General -> Existing project into Workspace. When asked for the location of the project point Eclipse to the location of your SMING_HOME folder.

Once you have imported the Sming project you can import some of the samples. You need to open in terminal the location of the sample of your interest and type:

cd /path/to/sample/
make ide-eclipse

This will create the minimum required meta files for an Eclipse CDT project which you can import into your Workspace. /path/to/sample/ should be replaced with the actual location of the sample. For the Basic_Blink sample we can use the following commands:

cd $SMING_HOME/../samples/Basic_Blink # Replace $SMING_HOME with %SMING_HOME% if you use Windows
make ide-eclipse

After this you go back to your Eclipse and run again File -> Import and from there choose General -> Existing project into Workspace. Then select the Basic_Blink directory under samples. Once the project is imported and the Eclipse CDT indexes are build you will be able to program like a pro and have code completion and debug your code. For the latter see Live Debug.

You can import also your own Sming-based applications in Eclipse. If your application is not containing .project and .cproject files inside its root folder then you can create such files by going to the root folder of your application and then typing the command below:

make ide-eclipse

Using with MS Visual Studio Code

Microsoft Visual Studio Code is a free (as in “free beer”) and Open Source code editor for Windows, Linux and Mac.

Software involved
Installation
  • Install VS Code, extensions and tools

  • Navigate to project folder and create configuration as described below

  • Open workspace. If vscode is in your system path you can do this:

    code .
    
Configuration

Please make sure you have critical environment variables set globally before starting vscode. See Configuration for details.

One of the strengths of vscode is the use of well-documented configuration files. You can find comprehensive documentation for these online.

However, setting these up is time-consuming so the build system can create them for you. The vscode workspace root directory is your project directory.

Change to your project directory (e.g. samples/Basic_Blink) and run these commands:

make ide-vscode SMING_ARCH=Esp8266
make ide-vscode SMING_ARCH=Host

Now open the workspace in vscode, and open a source file (.c, .cpp, .h). You should now be able to select the architecture from the icon in the bottom-right corner:

_images/vscode1.png

VS Code language selection

A selection of tasks are provided which you can view via Terminal -> Run Task.

To debug your application, follow these steps:

  • Select the appropriate architecture (e.g. Host, Esp8266)

  • Select Terminal -> Run Task -> Full rebuild (with debugging)

  • Confirm that the baud rate (COM_SPEED_GDB) and port (COM_PORT_GDB) are set correctly:

    make gdb SMING_ARCH=Esp8266 COM_PORT_GDB=/dev/ttyUSB0 COM_SPEED_GDB=115200
    
  • Update the vscode configuration:

    make ide-vscode
    
  • In vscode, select the require ‘Run’ task:

_images/vscode2.png

VS Code debug selection

Manual configuration changes

When you run make ide-vscode the configuration files are actually generated using a python script Tools/vscode/setup.py. Configuration variables are passed from the project makefile.

If you make any changes to the configuration files, please note the following behaviour:

  • The Host, Esp32 or Esp8266 intellisense settings will be overwritten.

  • The Esp8266 GDB and Host GDB launch configurations will be overwritten

  • The sming.code-workspace and .vscode/tasks.json files will be created if they do not already exist. To re-create these they must first be deleted.

Ideally the vscode configuration files should not need to be kept under configuration control, but generated when required.

Some settings are necessarily configured via the setup.py script, however most settings can be changed by editing the files in Tools/vscode/template.

If you do this, remember to keep a copy as they’ll be overwritten otherwise.

And, please consider contributing any changes or suggestions to the community!

Known issues / features
  • The vscode configuration files are only updated when you manually run make ide-vscode. If you update change critical build variables or add/remove Components to your project, you may need to run it again to update them.

  • When running make ide-vscode, comments in the configuration files will be discarded.

  • make ide-vscode may overwrite parts of your configuration: be warned!

  • When debugging for esp8266 output in the console is not formatted correctly. Lines appear with @ in front of them.

  • You may find vscode uses powershell instead of cmd.exe to execute tasks. Sming should work OK with either, but you can change this in the sming.code-workspace file via the terminal.integrated.defaultProfile.windows setting.

Troubleshooting

Troubleshooting Windows

If something goes wrong - don’t worry, community will be able to help you out. Don’t forget to check User questions before posting a github issues. Maybe someone else had a similar issue!

If nothing found, please make sure to provide all required information when posting issues. Here’s the minimum that you will need to get: Start cmd.exe and provide output of the following commands:

echo %PATH%
echo %SMING_HOME%
echo %ESP_HOME%
dir %SMING_HOME%
dir %ESP_HOME%
where make
Common issues & solutions
  • SMING_HOME should be set to c:\tools\sming\Sming, with \ as a path separator, and NO backslash \ at the end.

  • ESP_HOME should be set to c:\Espressif, using \ as a path separator, and NO backslash \ at the end.

  • MinGW paths should be at the start of PATH environment variable (before other items).

  • If you update your sming-core source don’t forget to do cd c:\tools\sming\Sming && make clean && make

Random Restarts

First try setting the baud rate to 74880. Example for Linux:

python -m serial.tools.miniterm /dev/ttyUSB0 74880

The random symbols should become readable messages.

If you see repeating messages containing rf_cal[0] !=0x05 then most probably you should initialize the flash memory on your ESP8266 device.

To achieve this do the following:

  1. Check the Hardware configuration especially flash_size setting.

Note

If you’re not sure what size the flash memory is on your device, run make flashid to check.

  1. Run flashinit:

    cd $SMING_HOME/../samples/Basic_Blink
    make flashinit
    

    This resets the flash memory to a default state, erasing any existing application, configuration or data stored there.

  2. Re-program your device:

    make flash
    
  3. Verify flash data has been written successfully

    make verifyflash

Sample Compilation

The first thing that you need to do is to make sure that you have a clean source code. And second if the sample is still not compiling you have to provide us with more information.

Let’s start with the first part: “Clean Source Code State”. If you are familiar with git you can run git status to get more information. Sometimes this won’t be enough therefore we recommend you the following steps ( They are working on Linux with bash shell. If you have another OS and shell you should adjust them accordingly).

cd /tmp
git clone https://github.com/SmingHub/Sming.git SmingForTest
cd /tmp/SmingForTest/Sming
export SMING_HOME=/tmp/SmingForTest/Sming

The commands above will fetch the latest source code in a directory that should be completely different than the Sming directory that you are using on a daily basis. Also it points the SMING_HOME variable to the new temporary directory with the clean source code.

Now let’s go to the second step: “Compile a sample and report issues, if any”. We will use the Basic_Ssl sample. Before we compile a sample we need to compile the Sming library. This can be done calling the following commands:

cd /tmp/SmingForTest/Sming
export SMING_HOME=/tmp/SmingForTest/Sming
make clean

The last makes sure to clean any intermediate files or directories. If we run now make. It will fetch the needed submodules, compile the code and build a library out of it. In our case we need to compile Sming with an optional SSL support. In order to compile Sming with SSL we need to add the ENABLE_SSL =1 directive. The command that we need to run now will look like this:

make ENABLE_SSL=1

On error If the compilation stops with an error, please, copy the output that the command above produces and append it to your bug report. Now run the same command one more time but with the V=1 directive. This will produce a more verbose output that can help the Sming developers figure out the issue.

make ENABLE_SSL=1 V=1

Make sure to append that output too to your bug report. Tell the Sming developers also what is your SDK (esp-open-sdk, esp-alt-sdk, …) and version, operating system & version, git version, make & version, so that the developers can reproduce your problem.

On success It is time to compile the Basic_Ssl sample. Do this by executing the commands below:

cd /tmp/SmingForTest/samples/Basic_Ssl
export SMING_HOME=/tmp/SmingForTest/Sming
make clean
make

On error 2 If that compilation fails make sure to append the output to your bug report. Now compile the sample with the V=1 flags, similar to the compilation of the Sming library.

cd /tmp/SmingForTest/samples/Basic_Ssl
export SMING_HOME=/tmp/SmingForTest/Sming
make V=1

Check User questions before posting a github issues. Maybe someone else had a similar issue!

About

Sming

Sming is an asynchronous C/C++ framework with superb performance and multiple network features. Sming is open source and is tailored towards embedded devices. It supports multiple architectures.

The project was started in 2015 and is actively developed.

Arduino

Arduino is an open-source electronics platform based on easy-to-use hardware and software. Sming is compatible with (most) standard Arduino libraries, which means that you can use any popular hardware in few lines of code.

ESP8266

ESP8266 is a microcontroller with Wi-Fi, manufactured by Espressif Systems. It is the first microcontroller that was supported from Sming. Sming provides access to all ESP8266 functions such as GPIO, timers, WiFi configuration, etc.

ESP32

ESP32 is the second microcontroller by Espressif Systems. There are also a number of more recent variants such as the esp32-s2 and esp32-c3.

Sming currently provides support for these devices but is more limited and should be considered ‘experimental’.

Licenses

Sming Core

LGPL v3

Espressif SDK

ESPRESSIF MIT License (with some closed-source blobs)

Libraries

See each library for details of its own open source license

Indices and tables