Welcome to Sming Framework

Let’s do smart things!!!

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 as ESP8266 for example.

samples

Summary

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

  • Fast and user friendly development

  • Simple yet powerful hardware API wrappers

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

  • Multiple file system support: Installable File System, SPIFFS IFS Library, LittleFS

  • Built-in powerful wireless modules

  • Powerful asynchronous (async) network stack.

  • 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).

  • ESP8266 specific features

  • Linux/Windows features

  • Sming has a host emulator that allows libraries and sample applications to be compiled on a Linux/Windows host system and be tested before uploading them to an actual microcontroller.

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. See /opt/sming/Tools/export.sh for details.

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

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

Proceed to Configuring your device.

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 Configuring your device.

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 Configuring your device.

Windows 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.

However, there are situations where it is highly desirable to build Sming in a Linux environment, such as:

  • Making use of linux-only development tools, such as 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, dubbed WSL1 and WSL2. Either is fine, and you can switch between versions but WSL2 is recommended.

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-win10#manual-installation-steps.

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.

Flashing devices

WSL2 does not currently support access to USB serial devices, so the Sming build system incorporates a workaround which runs the appropriate application (esptool) directly under Windows (via powershell).

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

For example:

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 Configuring your device.

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.

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 Esp8266.

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

You can set these initially on the command line, like this:

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

(For Windows or Windows 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_WIFI

Note

EXPERIMENTAL

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.

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);
                                       ^^^
Parameters
  • objref: When an Object pointer is required, such when defining entries for a Vector or Map, it is usually sufficient to use &objref.

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.

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

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.

See

https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

Template Parameters
  • ObjectType: The object type actually being instantiated

  • ElementType:

Public Functions

Object()

Creates a null object.

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.

size_t length() const

Get the length of the content in elements.

ElementType operator[](unsigned index) const

Array operator[].

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 Value
  • size_t: Number of elements actually read

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 Value
  • size_t: Number of elements actually read

Public Static Functions

static 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:

Parameters

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

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

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

IMPORT_FSTR(name, file)

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

See

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

Parameters
  • name: name of the table

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());

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

describes a counted string stored in flash memory

Public Functions

size_t size() const

Get the number of bytes used to store the String.

Note

Always an integer multiple of 4 bytes

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 Value
  • bool: true if strings are identical

bool equals(const String &str) const

Check for equality with another String.

Parameters
  • str:

Return Value
  • bool: true if strings are identical

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

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

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

ElementType operator[](size_t index) const

Array operator.

Parameters
  • index:

Return Value
  • ElementType:

size_t length() const

Get number of columns.

Return Value
  • size_t:

size_t printTo(Print &p) const

Print a row using Array Printer.

Public Static Functions

static 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

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

Class template to access an associative map.

Template Parameters

Public Functions

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>
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 Value
  • int: If key isn’t found, return -1

template<typename TRefKey, typename T = KeyType>
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 Value
  • int: If key isn’t found, return -1

template<typename TRefKey>
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:

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

Public Functions

operator IfHelperType() const

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

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

Get the key (non-class key types)

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

Get the key (String key type)

const ContentType &content() const

Accessor to get a reference to the content.

Public Static Functions

static 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 in conjuction 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 FlashMemoryStream, 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)
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 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: FlashMemoryStream

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
References
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 communication with the real microcontroller using TCP HOSTED_SERVER_IP=192.168.4.1 instructs the host emulator to connect to IP 192.168.4.1.

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 sub-directory samples inside this directory you will find the sample applications that will turn your microcontroller into 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: /dev/ttyUSB0 or the value of the environment variable COM_PORT if defined

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.

References
Used by
Environment Variables
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.

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:

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 signficant 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.

API
namespace IFS

Access.h Access control definitions

Created on: 6 Jun 2018

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

Attribute.h

Created: April 2021

Copyright 2021 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

Compression.h

Created on: 31 Aug 2018

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

Control.h

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

Directory.h

Created: May 2019

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

File.h

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

FileAttributes.h

Created on: 31 Aug 2018

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

FsBase.h Common base for file system classes

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

ArchiveStream.h

Copyright 2021 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

FileSystem.h FWFS - Firmware File System

Created on: 19 Jul 2018

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

Object.h Basic definitions for FW file system structure.

Created on: 7 Aug 2018

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

A filesystem image is basically:

uint32_t START_MARKER;
Object Objects[];
Object EndObject;
uint32_t END_MARKER;

An FWFS Object is a variable-length structure which can be read as either 1 or 2 words, depending on the type. Objects always start on a word boundary. File and directory objects are both emitted as ‘named’ objects which contain a list of zero or more child objects. When the image is built the child objects are emitted first, followed by the parent. This puts the root directory at the end of the image. This arrangement means an image can be generated without having to back-track and rewrite headers.

Child objects can be of any type. A directory object will mostly contain other file and directory objects. File data is stored in a data object, not in the file object itself. This is usually found following the file object, but it doesn’t have to be. Any object may be referenced by zero or more named object(s). For example, file links/aliases can contain references to the same file data. A file object may contain multiple data objects. These are treated as a contiguous block for file operations. This would potentially allow a file system builder to place common file blocks into shared data objects.

Object names are from 0 to 255 characters, inclusive. The root directory has a zero-length name. Paths lengths are unlimited. ‘/’ is used as the path separator. It informs the filesystem of the parent/child relationship between one directory object and a sub-ordinate. ‘:’ is used as the file stream separator. It performs the equivalent of the path separator for non-directory named objects. For exaample, file object may contain named objects accessible thus: index.html:info opens a handle to a named object called ‘info’ belonging to index.html.

OK, I admit it; this is pinched from NTFS; but it’s such a cool idea. Applications can use it to attach custom data to their files without limitation.

As a side note, of course FWFS is read-only. More precisely, it only supports random reading of files, not random writing. Serial writing is supported in the form of image creation.

For SPIFFS, IFS is a wrapper. The metadata features are supported using SPIFFS metadata. An alternative approach is to implement every named object as a SPIFFS file. We’d then get all the features of FWFS in a rewritable system, with all the benefits of SPIFFS wear-levelling.

Objects are identified by their index, but it’s not stored in the image. Instead, it’s tracked via internal object descriptor.

To optimise lookups, an object table can be stored at the end of the image. This is just an array of 32-bit image offsets so that an object can be located instantly on large volumes. This will be optional as it can consume significant space.

ObjectBuffer.h

Copyright 2021 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

FileSystem.h IFS wrapper for GDB syscall file access

Created on: 1 December 2020

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

Helpers.h Helper functions to assist with standard filesystem creation

Created on: 27 Jan 2019

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

NameBuffer.h

Created on: 31 Aug 2018

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

OpenFlags.h

Created on: 31 Aug 2018

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

IProfiler.h - Abstract interface to implement filesystem profiling

Copyright 2021 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

Stat.h

Created: August 2018

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

TimeStamp.h

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

UserRole.h

Created on: 6 Jun 2018

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

Util.h Various bits for file system implementations to use

Created on: 10 Sep 2018

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

Return compression corresponding to given string

Parameters
  • str:

Return Value
  • Compression::Type:

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

Return the access type value for the given string.

Parameters
  • str:

  • defaultRole: Returned if string isn’t recognsed

Return Value
  • UserRole:

UserRole getUserRole(const char *str, UserRole defaultRole)
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 AttributeTag

Identifies a specific attribute.

Values:

XX
User = 16

First user attribute.

enum ControlCode

See IFS::IFileSystem::fcontrol

These are weakly typed as values may be defined elsewhere.

Values:

FCNTL_GET_MD5_HASH = 1

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.

FCNTL_USER_BASE = 0x8000

Start of user-defined codes.

Codes before this are reserved for system use

enum FileAttribute

Values:

XX
MAX
enum OpenFlag

Values:

XX
MAX
enum UserRole

Values:

XX
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 Value

AttributeTag getUserAttributeTag(uint8_t value)
size_t getAttributeSize(AttributeTag tag)
struct IFS::__attribute__((packed))

A compression descriptor.

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 Value

FileSystem *createFirmwareFilesystem(Storage::Partition partition)

Create a firmware filesystem.

Parameters
  • partition:

Return Value
  • 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 Value
  • FileSystem*: constructed filesystem object

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 Value
  • 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 Value
  • bool: true if path is root directory

void checkStat(Stat &stat)

Variables

constexpr ErrorCode FS_OK = Error::Success
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.

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

Wrapper class for enumerating a directory.

Subclassed by 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 Value
  • 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 Value
  • bool: true on success, false on error

const String &getDirName() const

Name of directory stream is attached to.

Return Value
  • String: invalid if stream isn’t open

bool dirExist() const

Determine if directory exists.

Return Value
  • 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 Value
  • String: invalid if there is no parent directory

class DirectoryTemplate : public SectionTemplate
#include <DirectoryTemplate.h>

Directory stream class.

Subclassed by IFS::HtmlDirectoryTemplate, IFS::JsonDirectoryTemplate

Public Functions

bool nextRecord()

Move to next record.

Return Value
  • bool: true to emit section, false to skip

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

Wraps up all file access methods.

Subclassed by File

Public Functions

bool stat(Stat &stat)

get file information

Parameters
  • stat: structure to return information in, may be null

Return Value
  • bool: true on success

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 Value
  • int: error code or, on success, data size

template<typename T>
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 Value
  • bool: true on success

bool close()

close an open file

Return Value
  • bool: true on success

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 Value
  • int: number of bytes read or error code

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 Value
  • int: number of bytes written or error code

int seek(int 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 Value
  • int: current position or error code

bool eof()

determine if current file position is at end of file

Return Value
  • bool: true if at EOF or file is invalid

int32_t tell()

get current file position

Return Value
  • int32_t: current position relative to start of file, or error code

bool truncate(size_t new_size)

Truncate (reduce) the size of an open file.

Parameters
  • newSize:

Return Value
  • bool: true on success

bool truncate()

Truncate an open file at the current cursor position.

Return Value
  • bool: true on success

bool flush()

flush any buffered data to physical media

Return Value
  • bool: true on success

bool setacl(const ACL &acl)

Set access control information for file.

Parameters
  • acl:

Return Value
  • bool: true on success

bool settime(time_t mtime)

Set modification time for file.

Note

any subsequent writes to file will reset this to current time

Return Value
  • bool: true on success

bool setcompression(const Compression &compression)

Set file compression information.

Parameters
  • compression:

Return Value
  • bool: true on success

bool remove()

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

Return Value
  • bool: true on success

uint32_t getSize()

Get size of file.

Return Value
  • uint32_t: Size of file in bytes, 0 on error

int 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 Value
  • int: Number of bytes processed, or error code

int readContent(ReadContentCallback callback)

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

Parameters
  • callback:

Return Value
  • int: Number of bytes processed, or error code

String getContent()

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 Value
  • String: String variable in to which to read the file content

struct FileNameBuffer : public IFS::NameBuffer
#include <NameBuffer.h>

a quick’n’dirty name buffer with maximum path allocation

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 Value
  • bool: true on success, false on error

bool open(DirHandle dir, const String &name, OpenFlags openFlags = OpenFlag::Read)

Open a file and attach this stream object to it.

Note

call getLastError() to determine cause of failure

Parameters
  • dir: Location of file

  • fileName: Name of file

  • openFlags:

Return Value
  • bool: true on success, false on error

void close()

Close file.

StreamType getStreamType() const

Get the stream type.

Return Value
  • StreamType: The stream type.

size_t write(const uint8_t *buffer, size_t size)

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 Value
  • size_t: Quantity of chars written to stream

int read()

Read one character and moves the stream pointer.

Return Value
  • The: character that was read or -1 if none is available

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.

uint16_t readMemoryBlock(char *data, int bufSize)

Read a block of memory.

Parameters
  • data: Pointer to the data to be read

  • bufSize: Quantity of chars to read

Return Value
  • uint16_t: Quantity of chars read

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 Value
  • New: position, < 0 on error

bool isFinished()

Check if all data has been read.

Return Value
  • bool: True on success.

String fileName() const

Filename of file stream is attached to.

Return Value
  • String: invalid if stream isn’t open

bool fileExist() const

Determine if file exists.

Return Value
  • bool: true if stream contains valid file

String getName() const

Returns name of the resource.

Note

Commonly used to obtain name of file

Return Value

MimeType getMimeType() const

Get MIME type for stream content.

Return Value
  • MimeType:

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 Value
  • bool: true if valid, false if invalid

size_t getPos() const

Get the offset of cursor from beginning of data.

Return Value
  • size_t: Cursor offset

size_t getSize() const

Get the total file size.

Return Value

int available()

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

Return Value
  • int: -1 is returned when the size cannot be determined

String id() const

Returns unique id of the resource.

Return Value
  • String: the unique id of the stream.

bool truncate(size_t newSize)

Reduce the file size.

Parameters
  • newSize:

Return Value
  • bool: true on success

bool truncate()

Truncate file at current position.

Return Value
  • bool: true on success

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

Parameters
  • oldpath:

  • newpath:

Return Value
  • int: error code

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

Callback for readContent method.

Parameters
  • buffer:

  • size:

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

int remove(const String &path)

remove (delete) a file by path

Parameters
  • path:

Return Value
  • int: error code

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

Set access control information for file.

Parameters
  • file: handle or path to file

  • acl:

Return Value
  • int: error code

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

Set file attributes.

Parameters
  • file: handle or path to file

  • attr:

Return Value
  • int: error code

template<typename T>
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 Value
  • int: error code

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

Set file compression information.

Parameters
  • file:

  • compression:

Return Value
  • int: error code

uint32_t getSize(FileHandle file)

Get size of file.

Parameters
Return Value
  • uint32_t: Size of file in bytes, 0 on error

uint32_t getSize(const char *fileName)

Get size of file.

Parameters
  • fileName: Name of file

Return Value
  • uint32_t: Size of file in bytes, 0 on error

int 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 Value
  • int: Number of bytes processed, or error code

int 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 Value
  • int: Number of bytes processed, or error code

int readContent(const String &filename, ReadContentCallback callback)

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

Parameters
  • filename:

  • callback:

Return Value
  • int: Number of bytes processed, or 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!

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 Value
  • 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.

Parameters
  • fileName: Name of file to create or replace

  • content: Pointer to c-string containing content to populate file with

Return Value
  • int: Number of bytes transferred or error code

Parameters
  • length: (optional) number of characters to write

Public Functions

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 Value
  • int: error code

int truncate(const char *fileName, size_t newSize)

Truncate a file to a specific size.

Parameters
  • fileName: File to truncate

Return Value
  • 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 Value
  • String: String variable in to which to read the file content

class FsBase
#include <FsBase.h>

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

Public Functions

int getLastError()

determine if an error occurred during operation

Return Value
  • int: filesystem error code

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

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

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.

Note

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

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.

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

Public Functions

virtual ~IFileSystem()

Filing system implementations should dismount and cleanup here.

virtual int mount() = 0

Mount file system, performing any required initialisation.

Return Value
  • error: code

virtual int getinfo(Info &info) = 0

get filing system information

Parameters
  • info: structure to read information into

Return Value
  • int: error code

virtual int setProfiler(IProfiler *profiler)

Set profiler instance to enable debugging and performance assessment.

Parameters
  • profiler:

Return Value
  • int: error code - profiling may not be supported on all filesystems

virtual String getErrorString(int err)

get the text for a returned error code

Return Value

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 Value
  • 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 Value
  • 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 Value
  • int: error code

virtual int rewinddir(DirHandle dir) = 0

Reset directory read position to start.

Parameters
  • dir:

Return Value
  • int: error code

virtual int closedir(DirHandle dir) = 0

close a directory object

Parameters
  • dir: directory to close

Return Value
  • 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

makedirs() for this purpose.
Parameters
  • path: Path to directory

Return Value
  • int: error code

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

get file information

Parameters
  • path: name or path of file

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

Return Value
  • 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 Value
  • int: error code

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 Value
  • int: error code or, on success, data size

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

open a file by path

Parameters
  • path: full path to file

  • flags: opens for opening file

Return Value
  • FileHandle: file handle or error code

virtual int close(FileHandle file) = 0

close an open file

Parameters
  • file: handle to open file

Return Value
  • 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 Value
  • 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 Value
  • int: number of bytes written or error code

virtual int lseek(FileHandle file, int 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 Value
  • int: 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 Value
  • int: 0 - not EOF, > 0 - at EOF, < 0 - error

virtual int32_t tell(FileHandle file) = 0

get current file position

Parameters
  • file: handle to open file

Return Value
  • int32_t: current position relative to start of file, or error code

virtual int ftruncate(FileHandle 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 Value

virtual int flush(FileHandle file) = 0

flush any buffered data to physical media

Parameters
  • file: handle to open file

Return Value
  • 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 Value
  • 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 Value
  • 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 Value
  • 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 Value
  • 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 Value
  • int: error code, on success returns size of attribute (which may be larger than size)

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

rename a file

Parameters
  • oldpath:

  • newpath:

Return Value
  • int: error code

virtual int remove(const char *path) = 0

remove (delete) a file by path

Parameters
  • path:

Return Value
  • int: error code

virtual int fremove(FileHandle file) = 0

remove (delete) a file by handle

Parameters
  • file: handle to open file

Return Value
  • 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 Value
  • int: error code

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,

Return Value
  • 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.

uint32_t volumeSize = {0}

Size of volume, in bytes.

uint32_t freeSpace = {0}

Available space, in bytes.

struct NameInfo : public IFS::IFileSystem::Info
#include <IFileSystem.h>

Filing system information with buffer for name.

class IProfiler
#include <Profiler.h>

Filesystems may optionally provide performance statistics.

Subclassed by IFS::Profiler

Public Functions

virtual void read(uint32_t address, const void *buffer, size_t size) = 0

Called AFTER reading a block of data.

virtual void write(uint32_t address, const void *buffer, size_t size) = 0

Called BEFORE writing a block of data.

virtual void erase(uint32_t address, size_t size) = 0

Called BEFORE an erase operation.

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

Read-only stream providing directory listing in JSON format.

struct NameBuffer
#include <NameBuffer.h>

defines a ‘safe’ name buffer

There are fancier ways to do this but a structure is transparent and requires no heap allocation.

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.

Note

length always reflects the required name/path length, and may be longer than size.

Subclassed by IFS::FileNameBuffer

Public Functions

NameBuffer(String &s)

Make a NameBuffer point to contents of a String.

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

int addSep()

When building file paths this method simplified appending separators.

Note

if the path is not empty, a separator character is appended

Return Value
  • int: error code

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 Value
  • char*:

uint16_t space()

get the number of free characters available

Note

returns 0 if buffer has overrun

Return Value
  • uint16_t:

void terminate()

ensure the buffer has a nul terminator, even if it means overwriting content

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 NameStat : public IFS::Stat
#include <Stat.h>

version of Stat with integrated name buffer

Note

provide for convenience

class Profiler : public IFS::IProfiler
#include <Profiler.h>

Public Functions

void read(uint32_t address, const void *buffer, size_t size)

Called AFTER reading a block of data.

void write(uint32_t address, const void *buffer, size_t size)

Called BEFORE writing a block of data.

void erase(uint32_t address, size_t size)

Called BEFORE an erase operation.

struct Stat
#include <Stat.h>

File Status structure.

Subclassed by IFS::NameStat

Public Functions

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.

Public Members

IFileSystem *fs = {nullptr}

The filing system owning this file.

NameBuffer name

Name of file.

uint32_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 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.

namespace Error

Enums

enum Value

Values:

XX
MAX

Functions

String toString(int err)

get text for an error code

Parameters
  • err:

Return Value

bool isSystem(int err)

Determine if the given IFS error code is system-specific.

int fromSystem(int syscode)

Translate system error code into IFS error code.

int toSystem(int err)

Translate IFS error code into SYSTEM code.

Variables

constexpr ErrorCode USER = {-100}
constexpr ErrorCode SYSTEM = {-1000}
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

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:

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 Value
  • bool: Return true to process the item, false to skip it

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 Value
  • IBlockEncoder*: Stream to use for file content. Return nullptr for default behaviour.

const String &getCurrentPath() const

Get the current path being processed.

uint16_t readMemoryBlock(char *data, int bufSize)

Read a block of memory.

Parameters
  • data: Pointer to the data to be read

  • bufSize: Quantity of chars to read

Return Value
  • uint16_t: Quantity of chars read

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 Value
  • New: position, < 0 on error

bool isFinished()

Check if all data has been read.

Return Value
  • bool: True on success.

MimeType getMimeType() const

Get MIME type for stream content.

Return Value
  • 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>
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 BasicEncoder : public IFS::FWFS::IBlockEncoder
#include <BlockEncoder.h>

Public Functions

IDataSourceStream *getNextStream()

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 FileSystem : public IFS::IFileSystem
#include <FileSystem.h>

Implementation of firmware filing system using IFS.

Public Functions

int mount()

Mount file system, performing any required initialisation.

Return Value
  • error: code

int getinfo(Info &info)

get filing system information

Parameters
  • info: structure to read information into

Return Value
  • int: error code

String getErrorString(int err)

get the text for a returned error code

Return Value

int setVolume(uint8_t index, IFileSystem *fileSystem)

Set volume for mountpoint.

Parameters
  • index: Volume index

  • fileSystem: The filesystem to root at this mountpoint

Return Value
  • int: error code

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

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 Value
  • int: error code

int readdir(DirHandle dir, Stat &stat)

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 Value
  • int: error code

int rewinddir(DirHandle dir)

Reset directory read position to start.

Parameters
  • dir:

Return Value
  • int: error code

int closedir(DirHandle dir)

close a directory object

Parameters
  • dir: directory to close

Return Value
  • int: error code

int mkdir(const char *path)

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

makedirs() for this purpose.
Parameters
  • path: Path to directory

Return Value
  • int: error code

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

get file information

Parameters
  • path: name or path of file

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

Return Value
  • int: error code

int fstat(FileHandle file, Stat *stat)

get file information

Parameters
  • file: handle to open file

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

Return Value
  • int: error code

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 Value
  • int: error code or, on success, data size

int fsetxattr(FileHandle file, AttributeTag tag, const void *data, size_t size)

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 Value
  • int: error code

int fgetxattr(FileHandle file, AttributeTag tag, void *buffer, size_t size)

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 Value
  • int: error code, on success returns size of attribute (which may be larger than size)

int fenumxattr(FileHandle file, AttributeEnumCallback callback, void *buffer, size_t bufsize)

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 Value
  • int: error code, on success returns number of attributes read

int setxattr(const char *path, AttributeTag tag, const void *data, size_t size)

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 Value
  • int: error code

int getxattr(const char *path, AttributeTag tag, void *buffer, size_t size)

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 Value
  • int: error code, on success returns size of attribute (which may be larger than size)

FileHandle open(const char *path, OpenFlags flags)

open a file by path

Parameters
  • path: full path to file

  • flags: opens for opening file

Return Value
  • FileHandle: file handle or error code

int close(FileHandle file)

close an open file

Parameters
  • file: handle to open file

Return Value
  • int: error code

int read(FileHandle file, void *data, size_t size)

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 Value
  • int: number of bytes read or error code

int write(FileHandle file, const void *data, size_t size)

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 Value
  • int: number of bytes written or error code

int lseek(FileHandle file, int offset, SeekOrigin origin)

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 Value
  • int: current position or error code

int eof(FileHandle file)

determine if current file position is at end of file

Parameters
  • file: handle to open file

Return Value
  • int: 0 - not EOF, > 0 - at EOF, < 0 - error

int32_t tell(FileHandle file)

get current file position

Parameters
  • file: handle to open file

Return Value
  • int32_t: current position relative to start of file, or error code

int ftruncate(FileHandle file, size_t new_size)

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 Value

int flush(FileHandle file)

flush any buffered data to physical media

Parameters
  • file: handle to open file

Return Value
  • int: error code

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

rename a file

Parameters
  • oldpath:

  • newpath:

Return Value
  • int: error code

int remove(const char *path)

remove (delete) a file by path

Parameters
  • path:

Return Value
  • int: error code

int fremove(FileHandle file)

remove (delete) a file by handle

Parameters
  • file: handle to open file

Return Value
  • int: error code

int format()

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 Value
  • int: error code

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,

Return Value
  • int: error code

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 FWObjDesc
#include <Object.h>

FWFS Object Descriptor.

Public Members

Object::ID id

location

Object obj = {}

The object structure.

struct FWVolume
#include <FileSystem.h>

FWFS Volume definition for mount points.

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

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 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 Attribute

Object attributes.

Note

these are bit values

Values:

ReadOnly

Object should not be modified or deleted.

Archive

Object modified flag.

Note

Object has been changed on disk. Typically used by backup applications

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.

MAX
using ID = uint32_t

Object identifier (offset from start of image)

Public Functions

size_t contentOffset() const

return offset to start of object content

uint32_t contentSize() const

return size of object content, excluding header and size fields

Note

must check return error code

Return Value
  • size: or error code

uint32_t size() const

total size this object occupies in the image

Return Value
  • 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.

class ObjectBuffer : public MemoryDataStream
#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

int mount()

Mount file system, performing any required initialisation.

Return Value
  • error: code

int getinfo(Info &info)

get filing system information

Parameters
  • info: structure to read information into

Return Value
  • int: error code

String getErrorString(int err)

get the text for a returned error code

Return Value

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

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 Value
  • int: error code

int rewinddir(DirHandle dir)

Reset directory read position to start.

Parameters
  • dir:

Return Value
  • int: error code

int readdir(DirHandle dir, Stat &stat)

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 Value
  • int: error code

int closedir(DirHandle dir)

close a directory object

Parameters
  • dir: directory to close

Return Value
  • int: error code

int mkdir(const char *path)

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

makedirs() for this purpose.
Parameters
  • path: Path to directory

Return Value
  • int: error code

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

get file information

Parameters
  • path: name or path of file

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

Return Value
  • int: error code

int fstat(FileHandle file, Stat *stat)

get file information

Parameters
  • file: handle to open file

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

Return Value
  • int: error code

int fsetxattr(FileHandle file, AttributeTag tag, const void *data, size_t size)

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 Value
  • int: error code

int fgetxattr(FileHandle file, AttributeTag tag, void *buffer, size_t size)

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 Value
  • int: error code, on success returns size of attribute (which may be larger than size)

int fenumxattr(FileHandle file, AttributeEnumCallback callback, void *buffer, size_t bufsize)

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 Value
  • int: error code, on success returns number of attributes read

int setxattr(const char *path, AttributeTag tag, const void *data, size_t size)

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 Value
  • int: error code

int getxattr(const char *path, AttributeTag tag, void *buffer, size_t size)

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 Value
  • int: error code, on success returns size of attribute (which may be larger than size)

FileHandle open(const char *path, OpenFlags flags)

open a file by path

Parameters
  • path: full path to file

  • flags: opens for opening file

Return Value
  • FileHandle: file handle or error code

int close(FileHandle file)

close an open file

Parameters
  • file: handle to open file

Return Value
  • int: error code

int read(FileHandle file, void *data, size_t size)

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 Value
  • int: number of bytes read or error code

int write(FileHandle file, const void *data, size_t size)

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 Value
  • int: number of bytes written or error code

int lseek(FileHandle file, int offset, SeekOrigin origin)

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 Value
  • int: current position or error code

int eof(FileHandle file)

determine if current file position is at end of file

Parameters
  • file: handle to open file

Return Value
  • int: 0 - not EOF, > 0 - at EOF, < 0 - error

int32_t tell(FileHandle file)

get current file position

Parameters
  • file: handle to open file

Return Value
  • int32_t: current position relative to start of file, or error code

int ftruncate(FileHandle file, size_t new_size)

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 Value

int flush(FileHandle file)

flush any buffered data to physical media

Parameters
  • file: handle to open file

Return Value
  • int: error code

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

rename a file

Parameters
  • oldpath:

  • newpath:

Return Value
  • int: error code

int remove(const char *path)

remove (delete) a file by path

Parameters
  • path:

Return Value
  • int: error code

int fremove(FileHandle file)

remove (delete) a file by handle

Parameters
  • file: handle to open file

Return Value
  • int: error code

int format()

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 Value
  • int: error code

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,

Return Value
  • int: error code

namespace Host

Functions

FileSystem &getFileSystem()

Get the currently active file system, if any.

Return Value
  • IFS::FileSystem*:

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

int mount()

Mount file system, performing any required initialisation.

Return Value
  • error: code

int getinfo(Info &info)

get filing system information

Parameters
  • info: structure to read information into

Return Value
  • int: error code

String getErrorString(int err)

get the text for a returned error code

Return Value

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

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 Value
  • int: error code

int rewinddir(DirHandle dir)

Reset directory read position to start.

Parameters
  • dir:

Return Value
  • int: error code

int readdir(DirHandle dir, Stat &stat)

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 Value
  • int: error code

int closedir(DirHandle dir)

close a directory object

Parameters
  • dir: directory to close

Return Value
  • int: error code

int mkdir(const char *path)

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

makedirs() for this purpose.
Parameters
  • path: Path to directory

Return Value
  • int: error code

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

get file information

Parameters
  • path: name or path of file

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

Return Value
  • int: error code

int fstat(FileHandle file, Stat *stat)

get file information

Parameters
  • file: handle to open file

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

Return Value
  • int: error code

int fsetxattr(FileHandle file, AttributeTag tag, const void *data, size_t size)

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 Value
  • int: error code

int fgetxattr(FileHandle file, AttributeTag tag, void *buffer, size_t size)

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 Value
  • int: error code, on success returns size of attribute (which may be larger than size)

int fenumxattr(FileHandle file, AttributeEnumCallback callback, void *buffer, size_t bufsize)

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 Value
  • int: error code, on success returns number of attributes read

int setxattr(const char *path, AttributeTag tag, const void *data, size_t size)

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 Value
  • int: error code

int getxattr(const char *path, AttributeTag tag, void *buffer, size_t size)

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 Value
  • int: error code, on success returns size of attribute (which may be larger than size)

FileHandle open(const char *path, OpenFlags flags)

open a file by path

Parameters
  • path: full path to file

  • flags: opens for opening file

Return Value
  • FileHandle: file handle or error code

int close(FileHandle file)

close an open file

Parameters
  • file: handle to open file

Return Value
  • int: error code

int read(FileHandle file, void *data, size_t size)

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 Value
  • int: number of bytes read or error code

int write(FileHandle file, const void *data, size_t size)

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 Value
  • int: number of bytes written or error code

int lseek(FileHandle file, int offset, SeekOrigin origin)

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 Value
  • int: current position or error code

int eof(FileHandle file)

determine if current file position is at end of file

Parameters
  • file: handle to open file

Return Value
  • int: 0 - not EOF, > 0 - at EOF, < 0 - error

int32_t tell(FileHandle file)

get current file position

Parameters
  • file: handle to open file

Return Value
  • int32_t: current position relative to start of file, or error code

int ftruncate(FileHandle file, size_t new_size)

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 Value

int flush(FileHandle file)

flush any buffered data to physical media

Parameters
  • file: handle to open file

Return Value
  • int: error code

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

rename a file

Parameters
  • oldpath:

  • newpath:

Return Value
  • int: error code

int remove(const char *path)

remove (delete) a file by path

Parameters
  • path:

Return Value
  • int: error code

int fremove(FileHandle file)

remove (delete) a file by handle

Parameters
  • file: handle to open file

Return Value
  • int: error code

int format()

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 Value
  • int: error code

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,

Return Value
  • int: error code

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

Public Functions

int mount()

Mount file system, performing any required initialisation.

Return Value
  • error: code

int getinfo(Info &info)

get filing system information

Parameters
  • info: structure to read information into

Return Value
  • int: error code

String getErrorString(int err)

get the text for a returned error code

Return Value

int setVolume(uint8_t index, IFileSystem *fileSystem)

Set volume for mountpoint.

Parameters
  • index: Volume index

  • fileSystem: The filesystem to root at this mountpoint

Return Value
  • int: error code

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

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 Value
  • int: error code

int readdir(DirHandle dir, Stat &stat)

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 Value
  • int: error code

int rewinddir(DirHandle dir)

Reset directory read position to start.

Parameters
  • dir:

Return Value
  • int: error code

int closedir(DirHandle dir)

close a directory object

Parameters
  • dir: directory to close

Return Value
  • int: error code

int mkdir(const char *path)

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

makedirs() for this purpose.
Parameters
  • path: Path to directory

Return Value
  • int: error code

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

get file information

Parameters
  • path: name or path of file

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

Return Value
  • int: error code

int fstat(FileHandle file, Stat *stat)

get file information

Parameters
  • file: handle to open file

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

Return Value
  • int: error code

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 Value
  • int: error code or, on success, data size

int fsetxattr(FileHandle file, AttributeTag tag, const void *data, size_t size)

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 Value
  • int: error code

int fgetxattr(FileHandle file, AttributeTag tag, void *buffer, size_t size)

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 Value
  • int: error code, on success returns size of attribute (which may be larger than size)

int fenumxattr(FileHandle file, AttributeEnumCallback callback, void *buffer, size_t bufsize)

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 Value
  • int: error code, on success returns number of attributes read

int setxattr(const char *path, AttributeTag tag, const void *data, size_t size)

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 Value
  • int: error code

int getxattr(const char *path, AttributeTag tag, void *buffer, size_t size)

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 Value
  • int: error code, on success returns size of attribute (which may be larger than size)

FileHandle open(const char *path, OpenFlags flags)

open a file by path

Parameters
  • path: full path to file

  • flags: opens for opening file

Return Value
  • FileHandle: file handle or error code

int close(FileHandle file)

close an open file

Parameters
  • file: handle to open file

Return Value
  • int: error code

int read(FileHandle file, void *data, size_t size)

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 Value
  • int: number of bytes read or error code

int write(FileHandle file, const void *data, size_t size)

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 Value
  • int: number of bytes written or error code

int lseek(FileHandle file, int offset, SeekOrigin origin)

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 Value
  • int: current position or error code

int eof(FileHandle file)

determine if current file position is at end of file

Parameters
  • file: handle to open file

Return Value
  • int: 0 - not EOF, > 0 - at EOF, < 0 - error

int32_t tell(FileHandle file)

get current file position

Parameters
  • file: handle to open file

Return Value
  • int32_t: current position relative to start of file, or error code

int ftruncate(FileHandle file, size_t new_size)

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 Value

int flush(FileHandle file)

flush any buffered data to physical media

Parameters
  • file: handle to open file

Return Value
  • int: error code

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

rename a file

Parameters
  • oldpath:

  • newpath:

Return Value
  • int: error code

int remove(const char *path)

remove (delete) a file by path

Parameters
  • path:

Return Value
  • int: error code

int fremove(FileHandle file)

remove (delete) a file by handle

Parameters
  • file: handle to open file

Return Value
  • int: error code

int format()

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 Value
  • int: error code

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,

Return Value
  • int: error code

References
Used by
Environment Variables
  • FSBUILD

Networking Support

Contains core networking protocol support classes.

DNS: Domain Name System

https://en.m.wikipedia.org/wiki/Domain_Name_System

Server API
enum dnsserver::DnsReplyCode

Values:

NoError = 0
FormError = 1
ServerFailure = 2
NonExistentDomain = 3
NotImplemented = 4
Refused = 5
YXDomain = 6
YXRRSet = 7
NXRRSet = 8
typedef DnsServer DNSServer

DNS_QR_QUERY
DNS_QR_RESPONSE
DNS_OPCODE_QUERY
FTP: File Transfer Protocol

https://en.m.wikipedia.org/wiki/File_Transfer_Protocol

Server API
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.

Support API
namespace ContentType

Obtain content type string from file name or path, with extension

Parameters
  • fileName:

Return Value

String fromFullFileName(const char *fileName)
String fromFullFileName(const String &fileName)

Obtain content type string from file name or path, with extension.

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 Value
  • MimeType:

String fromFileExtension(const char *extension)

Obtain content type string from file extension.

Parameters
  • extension: excluding ‘.’ separator (e.g. “htm”, “json”)

Return Value

String fromFileExtension(const String &extension)

Obtain content type string from file extension.

Parameters
  • extension:

Return Value

MimeType fromString(const char *str)

Get enumerated value for a MIME type string.

Parameters
  • str:

Return Value
  • MimeType: If empty, null or unrecognised returns MIME_UNKNOWN

MimeType fromString(const String &str)

Get enumerated value for a MIME type string.

Parameters
  • str:

Return Value
  • MimeType: If empty, null or unrecognised returns MIME_UNKNOWN

MimeType fromFullFileName(const char *fileName, MimeType unknown)

Obtain MIME type value from file name or path, with extension.

Parameters
  • fileName:

  • unknown: Value to return if type cannot be determined

Return Value
  • MimeType:

MimeType fromFullFileName(const String &fileName, MimeType unknown)
Client API
class HttpClient
#include <HttpClient.h>

Public Functions

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();

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

HttpRequest *request(const String &url)

HttpRequest *createRequest(const Url &url)

Helper function to create a new request on a URL.

Parameters
  • url:

Return Value
  • HttpRequest*:

Public Static Functions

static void cleanup()

Use this method to clean all object pools.

Server API
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.

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:

void setBodyParser(MimeType mimeType, HttpBodyParserDelegate parser)

Allows content-type specific parsing of the body based on content-type.

Parameters
  • mimeType:

  • parser:

void addPath(String path, const HttpPathDelegate &callback)

void addPath(const String &path, const HttpResourceDelegate &onRequestComplete)

void addPath(const String &path, HttpResource *resource)

void setDefaultHandler(const HttpPathDelegate &callback)

void setDefaultResource(HttpResource *resource)

Public Members

HttpResourceTree paths

Maps paths to resources which deal with incoming requests.

MQTT: MQ Telemetry Transport

https://en.m.wikipedia.org/wiki/MQTT

Client API
enum mqttclient::MqttClientState

Values:

eMCS_Ready = 0
eMCS_SendingData
using MqttDelegate = Delegate<int(MqttClient &client, mqtt_message_t *message)>
using MqttRequestQueue = ObjectQueue<mqtt_message_t, MQTT_REQUEST_POOL_SIZE>
using MqttStringSubscriptionCallback = Delegate<void(String topic, String message)>

using MqttMessageDeliveredCallback = Delegate<void(uint16_t msgId, int type)>

MQTT_REQUEST_POOL_SIZE
MQTT_CLIENT_CONNECTED
MQTT_FLAG_RETAINED
MQTT_MAX_BUFFER_SIZE

MQTT_MSG_PUBREC

class MqttClient : protected TcpClient
#include <MqttClient.h>

Public Functions

void setKeepAlive(uint16_t seconds)

Sets keep-alive time. That information is sent during connection to the server.

Parameters
  • seconds:

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 Value
  • 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 Value
  • bool:

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

void setConnectedHandler(MqttDelegate handler)

Sets a handler to be called after successful MQTT connection.

Parameters
  • handler:

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:

void setMessageHandler(MqttDelegate handler)

Sets a handler to be called after receiving a PUBLISH message from the server.

Parameters
  • handler:

void setDisconnectHandler(TcpClientCompleteDelegate handler)

Sets a handler to be called on disconnect from the server.

Parameters
  • handler:

bool setWill(const String &topic, const String &message, int QoS, bool retained = false)

bool publishWithQoS(const String &topic, const String &message, int QoS, bool retained = false, MqttMessageDeliveredCallback onDelivery = nullptr)

bool publish(String& topic, String& message, bool retained = false) Use publish(const String& topic, const String& message, uint8_t flags = 0) instead.

void setCallback(MqttStringSubscriptionCallback subscriptionCallback = nullptr)

Provide a function to be called when a message is received from the broker.

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

NtpClient()

Instantiates NTP client object.

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

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

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 smtpclient::SmtpState

Values:

eSMTP_Banner = 0
eSMTP_Hello
eSMTP_StartTLS
eSMTP_SendAuth
eSMTP_SendingAuthLogin
eSMTP_RequestingAuthChallenge
eSMTP_SendAuthResponse
eSMTP_SendingAuth
eSMTP_Ready
eSMTP_SendMail
eSMTP_SendingMail
eSMTP_SendRcpt
eSMTP_SendingRcpt
eSMTP_SendData
eSMTP_SendingData
eSMTP_SendHeader
eSMTP_SendingHeaders
eSMTP_StartBody
eSMTP_SendingBody
eSMTP_Sent
eSMTP_Quitting
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

MailMessage &setHeader(const String &name, const String &value)

Set a header value.

Parameters
  • name:

  • value:

Return Value
  • MailMessage&:

HttpHeaders &getHeaders()

Get a reference to the current set of headers.

Return Value
  • HttpHeaders&:

MailMessage &setBody(const String &body, MimeType mime = MIME_TEXT)

Sets the body of the email.

Parameters
  • body:

  • mime:

Return Value
  • MailMessage&:

MailMessage &setBody(String &&body, MimeType mime = MIME_TEXT)

Sets the body of the email using move semantics.

Parameters
  • body: Will be moved into message then invalidated

  • mime:

Return Value
  • MailMessage&:

MailMessage &setBody(IDataSourceStream *stream, MimeType mime = MIME_TEXT)

Sets the body of the email.

Parameters
  • stream:

  • mime:

Return Value
  • MailMessage&:

MailMessage &addAttachment(FileStream *stream)

Adds attachment to the email.

Parameters
  • stream:

Return Value
  • MailMessage&:

MailMessage &addAttachment(IDataSourceStream *stream, MimeType mime, const String &filename = "")

Adds attachment to the email.

Parameters
  • stream:

  • mime:

  • filename:

Return Value
  • MailMessage&:

MailMessage &addAttachment(IDataSourceStream *stream, const String &mime, const String &filename = "")

Adds attachment to the email.

Parameters
  • stream:

  • mime:

  • filename:

Return Value
  • 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 Value
  • 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 Value
  • bool: true when the message was queued successfully, false otherwise

MailMessage *getCurrentMessage()

Gets the current message.

Return Value
  • MailMessage*: The message, or NULL if none is scheduled

void quit()

Sends a quit command to the server and closes the TCP conneciton.

SmtpState getState()

Returns the current state of the SmtpClient.

void onMessageSent(SmtpClientCallback callback)

Callback that will be called every time a message is sent successfully.

Parameters
  • callback:

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 tcp::TcpConnectionEvent

Values:

eTCE_Connected = 0

Occurs after connection establishment.

eTCE_Received

Occurs on data receive.

eTCE_Sent
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

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 Value
  • int: negative on error, 0 when retry is needed or possitive on success

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 Value
  • int: negative on error, 0 when retry is needed or possitive 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 Value
  • int: negative on error, 0 when retry is needed or possitive 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 Value
  • int: negative on error, 0 when retry is needed or possitive on success

void setDestroyedDelegate(TcpConnectionDestroyedDelegate destroyedDelegate)

Sets a callback to be called when the object instance is destroyed.

Parameters
  • destroyedDelegate:

void setSslInitHandler(Ssl::Session::InitDelegate handler)

Set the SSL session initialisation callback.

Parameters
  • handler:

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 tcpclient::TcpClientState

Values:

eTCS_Ready
eTCS_Connecting
eTCS_Connected
eTCS_Successful
eTCS_Failed
enum tcpclient::TcpClientCloseAfterSentState

Values:

eTCCASS_None
eTCCASS_AfterSent
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

void setReceiveDelegate(TcpClientDataDelegate receiveCb = nullptr)

Set or clear the callback for received data.

Parameters
  • receiveCb: callback delegate or nullptr

void setCompleteDelegate(TcpClientCompleteDelegate completeCb = nullptr)

Set or clear the callback for connection close.

Parameters
  • completeCb: callback delegate or nullptr

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.

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, TelnetServer

Telnet

https://en.m.wikipedia.org/wiki/Telnet

Server API
using TelnetServerCommandDelegate = Delegate<void(TcpClient *client, char *data, int size)>
TELNETSERVER_MAX_COMMANDSIZE
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, mDNS::Server, NtpClient

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 Value
  • 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 Value
  • true: when LWIP supports this operation, false otherwise

URL: Uniform Resource Locator

https://en.m.wikipedia.org/wiki/URL

typedef Url URL

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

Url(const String &urlString)

Construct a URL object from a regular escaped string.

Parameters
  • urlString: Escaped URL

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

Url &operator=(const char *urlString)

Copy assignment operator, for C-style strings.

Parameters
  • urlString: Escaped URL

String toString() const

Get escaped URL.

Return Value

int getPort() const

Obtain the actual port number to be used.

Note

If not specified, the default scheme port is returned

Return Value
  • int:

String getHostWithPort() const

Get hostname+port part of URL string.

Note

Neither of these is subject to escaping

Return Value

String getRelativePath() const

Get path without leading separator.

Return Value

String getPathWithQuery() const

Get path with any query parameters attached.

Note

Both path and query values are escaped

Return Value

String getFileName() const

Obtain the filename part of the URL path.

Return Value

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 Value
  • 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 websocket::WsConnectionState

Values:

eWSCS_Ready
eWSCS_Open
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
class WebsocketConnection
#include <WebsocketConnection.h>

Subclassed by WebsocketClient

Public Functions

WebsocketConnection(HttpConnection *connection, 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 conneciton

bool bind(HttpRequest &request, HttpResponse &response)

Binds websocket connection to an http server connection.

Parameters
  • request:

  • response:

Return Value
  • bool: true on success, false otherwise

virtual void 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:

void 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

void sendString(const String &message)

Sends a string websocket message.

Parameters
  • message:

void 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.

void setUserData(void *userData)

Attaches a user data to a websocket connection.

Parameters
  • userData:

void *getUserData()

Retrieves user data attached.

Return Value

bool operator==(const WebsocketConnection &rhs) const

Test if another connection refers to the same object.

Parameters
Return Value
  • bool:

void setConnectionHandler(WebsocketDelegate handler)

Sets the callback handler to be called after successful websocket connection.

Parameters
  • handler:

void setMessageHandler(WebsocketMessageDelegate handler)

Sets the callback handler to be called after a websocket message is received.

Parameters
  • handler:

void setBinaryHandler(WebsocketBinaryDelegate handler)

Sets the callback handler to be called after a binary websocket message is received.

Parameters
  • handler:

void setPongHandler(WebsocketDelegate handler)

Sets the callback handler to be called when pong reply received.

Parameters
  • handler:

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 Value
  • bool: true on success

HttpConnection *getConnection()

Gets the underlying HTTP connection.

Return Value
  • 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 conneciton

WsConnectionState getState()

Gets the state of the websocket connection.

Return Value
  • 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 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 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 Value
  • 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
  • url: Url address of websocket server

void sendPing(const String &payload = nullptr)

Send websocket ping to server.

Parameters
  • payload: Maximum 255 bytes

Return Value
  • bool: true if the data can be send, false otherwise

void sendPong(const String &payload = nullptr)

Send websocket ping to server.

Parameters
  • payload: Maximum 255 bytes

Return Value
  • bool: true if the data can be send, false otherwise

void setSslInitHandler(Ssl::Session::InitDelegate handler)

Set the SSL session initialisation callback.

Parameters
  • handler:

void disconnect()

Disconnects websocket client from server.

Other networking libraries:

References
Used by
Environment Variables
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 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 part = ota.getNextBootPartition();
    
       ota.begin(part);
    
       // ... write all the data to the partition
    
       ota.end();
    
       // ...
    }
    

See the Upgrade sample application.

API Documentation

Warning

doxygennamespace: Cannot find namespace “Ota” in doxygen xml output for project “api” from directory: ../api/xml/

References
Used by
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 Upgrade sample application.

API Documentation

Warning

doxygennamespace: Cannot find namespace “Ota” in doxygen xml output for project “api” from directory: ../api/xml/

References
Used by
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.

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 partbuild

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.

Binary partition table

Sming uses the same binary partition table structure as ESP-IDF, located immediately after the boot sector. However, it is organised slighly 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) {
  debugf("Partition '%s' found", part.name().c_str());
} else {
  debugf("Partition NOT found");
}

// Enumerate all partitions
for(auto it = Storage::findPartition(); it; ++it) {
   auto part = *it;
   debugf("Found '%s' at 0x%08x, size 0x%08x", part.name().c_str(), part.address(), part.size());
}

// Enumerate all SPIFFS partitions
for(auto it = Storage::findPartition(Partition::SubType::Data::spiffs; it; it++) {
   debugf("Found '%s' at 0x%08x, size 0x%08x", it->name().c_str(), it->address(), it->size());
}

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 registed 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()) {
   debugf("Found '%s' at 0x%08x, size 0x%08x", part.name().c_str(), part.address(), part.size());
}
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).

API
namespace Storage

FileDevice.h

Copyright 2019 mikee47 mike@sillyhouse.net

This file is part of the IFS 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/.

Functions

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 Value
  • 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.

Iterator findPartition(Partition::Type type = Partition::Type::any, uint8_t subType = Partition::SubType::any)

Find partitions of the given type.

template<typename T>
Iterator 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 CustomDevice : public Storage::Device
#include <CustomDevice.h>

Class to support dynamic partitions.

Call createPartition to add partitions up to a maximum of 16 entries.

Subclassed by Storage::FileDevice, Storage::ProgMem, Storage::StreamDevice, Storage::SysMem

class Device : public LinkedObjectTemplate<Device>
#include <Device.h>

Represents a storage device (e.g. flash memory)

Subclassed by Storage::CustomDevice, Storage::SpiFlash

Public Types

enum Type

Storage type.

Values:

XX

Public Functions

bool loadPartitions(uint32_t tableOffset)

Load partition table entries Location of partition table to read.

Return Value
  • bool: true on success, false on failure

bool loadPartitions(Device &source, uint32_t tableOffset)

Load partition table entries from another table.

Parameters
  • source: Device to load entries from Location of partition table to read

Return Value
  • bool: true on success, false on failure

virtual String getName() const = 0

Obtain unique device name.

virtual uint32_t getId() const

Obtain device ID.

Return Value
  • uint32_t: typically flash chip ID

virtual size_t getBlockSize() const = 0

Obtain smallest allocation unit for erase operations.

virtual size_t getSize() const = 0

Obtain addressable size of this device.

Return Value
  • size_t: Must be at least as large as the value declared in the partition table

virtual Type getType() const = 0

Obtain device type.

virtual bool read(uint32_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 Value
  • bool: true on success, false on error

virtual bool write(uint32_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 Value
  • bool: true on success, false on error

virtual bool erase_range(uint32_t address, 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 Value
  • bool: true on success, false on error

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 identifer for entry.

Storage::Partition::Flags flags

Various option flags.

class FileDevice : public Storage::CustomDevice
#include <FileDevice.h>

Read-only partition on a stream object.

Note

Writes not possible as streams always append data, cannot do random writes

Public Functions

String getName() const

Obtain unique device name.

Type getType() const

Obtain device type.

size_t getSize() const

Obtain addressable size of this device.

Return Value
  • size_t: Must be at least as large as the value declared in the partition table

size_t getBlockSize() const

Obtain smallest allocation unit for erase operations.

bool read(uint32_t address, void *dst, size_t size)

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 Value
  • bool: true on success, false on error

bool write(uint32_t address, const void *src, size_t size)

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 Value
  • bool: true on success, false on error

bool erase_range(uint32_t address, size_t size)

Erase a region of storage in preparation for writing.

Parameters
  • address: Where to start erasing

  • size: Size of region to erase, in bytes

Return Value
  • bool: true on success, false on error

class Partition
#include <Partition.h>

Represents a flash partition.

Confirm partition is of the expected type

Parameters
  • type: Expected partition type

  • subtype: Expected partition sub-type

Return Value
  • bool: true if type is OK, false if not. Logs debug messages on failure.

Public Functions

bool read(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 Value
  • bool: true on success, false on error

bool write(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 Value
  • bool: true on success, false on error

bool erase_range(size_t offset, 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 Value
  • bool: true on success, false on error

Partition::Type type() const

Obtain partition type.

uint8_t subType() const

Obtain partition sub-type.

uint32_t address() const

Obtain partition starting address.

Parameters

uint32_t lastAddress() const

Obtain address of last byte in this this partition.

Parameters

uint32_t size() const

Obtain partition size.

Return Value
  • uint32_t: Size in bytes

String name() const

Get partition name.

Flags flags() const

Get partition flags.

bool isEncrypted() const

Check state of partition encrypted flag.

bool isReadOnly() const

Check state of partition readOnly flag.

bool getDeviceAddress(uint32_t &address, 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 Value
  • 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 Value

Device *getDevice() const

Get storage device containing this partition.

Return Value
  • Device*: null if device isn’t registered

bool contains(uint32_t addr) const

Determine if given address contained within this partition.

size_t getBlockSize() const

Obtain smallest allocation unit for erase operations.

Public Static Functions

static SubType::App apptypeOta(uint8_t i)

Convenience function to get SubType value for the i-th OTA partition.

struct Info
#include <Partition.h>

Partition information.

struct SubType
#include <Partition.h>

Public Types

enum App

Application partition type.

Values:

partitionType = uint8_t(Type::app)
XX
ota_min = ota0
ota_max = ota15
any = 0xff
enum Data

Data partition type.

Values:

partitionType = uint8_t(Type::data)
XX
any = 0xff
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

int available()

Return the total length of the stream.

Return Value
  • int: -1 is returned when the size cannot be determined

uint16_t readMemoryBlock(char *data, int bufSize)

Read a block of memory.

Parameters
  • data: Pointer to the data to be read

  • bufSize: Quantity of chars to read

Return Value
  • uint16_t: Quantity of chars read

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 Value
  • New: position, < 0 on error

size_t write(const uint8_t *buffer, size_t size)

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 Value
  • size_t: Quantity of chars written to stream

bool isFinished()

Check if all data has been read.

Return Value
  • bool: True on success.

class PartitionTable
#include <PartitionTable.h>

Partition search

Find partitions based on one or more parameters

Parameters
Return Value
  • Iterator: Forward-iterator for matching partitions

Public Functions

Partition find(const String &name) const

Find partition by name.

Parameters
  • Name: Name to search for, case-sensitive

Return Value
  • Partition: Names are unique so at most only one match

Partition find(uint32_t address) const

Find partition containing the given address.

Parameters
  • address: Address to search for

Return Value

Partition findOta(uint8_t index)

Find the n’th OTA partition.

class ProgMem : public Storage::CustomDevice
#include <ProgMem.h>

Storage device to access PROGMEM using flash API.

Public Functions

String getName() const

Obtain unique device name.

size_t getBlockSize() const

Obtain smallest allocation unit for erase operations.

size_t getSize() const

Obtain addressable size of this device.

Return Value
  • size_t: Must be at least as large as the value declared in the partition table

Type getType() const

Obtain device type.

bool read(uint32_t address, void *dst, size_t size)

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 Value
  • bool: true on success, false on error

bool write(uint32_t address, const void *src, size_t size)

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 Value
  • bool: true on success, false on error

bool erase_range(uint32_t address, size_t size)

Erase a region of storage in preparation for writing.

Parameters
  • address: Where to start erasing

  • size: Size of region to erase, in bytes

Return Value
  • bool: true on success, false on error

Partition createPartition(const String &name, const void *flashPtr, size_t size, Partition::Type type, uint8_t subtype)

Create partition for PROGMEM data access.

Parameters
  • name: Name for partition

  • flashPtr: PROGMEM pointer

  • size: Size of PROGMEM data

  • type: Partition type

  • subtype: Partition sub-type

Return Value

Partition createPartition(const String &name, const FSTR::ObjectBase &fstr, Partition::Type type, uint8_t subtype)

Create partition for FlashString data access.

class SpiFlash : public Storage::Device
#include <SpiFlash.h>

Main flash storage device.

Public Functions

String getName() const

Obtain unique device name.

size_t getBlockSize() const

Obtain smallest allocation unit for erase operations.

size_t getSize() const

Obtain addressable size of this device.

Return Value
  • size_t: Must be at least as large as the value declared in the partition table

Type getType() const

Obtain device type.

uint32_t getId() const

Obtain device ID.

Return Value
  • uint32_t: typically flash chip ID

bool read(uint32_t address, void *dst, size_t size)

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 Value
  • bool: true on success, false on error

bool write(uint32_t address, const void *src, size_t size)

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 Value
  • bool: true on success, false on error

bool erase_range(uint32_t address, size_t size)

Erase a region of storage in preparation for writing.

Parameters
  • address: Where to start erasing

  • size: Size of region to erase, in bytes

Return Value
  • bool: true on success, false on error

class StreamDevice : public Storage::CustomDevice
#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

Type getType() const

Obtain device type.

bool read(uint32_t address, void *dst, size_t size)

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 Value
  • bool: true on success, false on error

bool write(uint32_t address, const void *src, size_t size)

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 Value
  • bool: true on success, false on error

bool erase_range(uint32_t address, size_t size)

Erase a region of storage in preparation for writing.

Parameters
  • address: Where to start erasing

  • size: Size of region to erase, in bytes

Return Value
  • bool: true on success, false on error

class SysMem : public Storage::CustomDevice
#include <SysMem.h>

Storage device to access system memory, e.g. RAM.

Public Functions

String getName() const

Obtain unique device name.

size_t getBlockSize() const

Obtain smallest allocation unit for erase operations.

size_t getSize() const

Obtain addressable size of this device.

Return Value
  • size_t: Must be at least as large as the value declared in the partition table

Type getType() const

Obtain device type.

bool read(uint32_t address, void *dst, size_t size)

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 Value
  • bool: true on success, false on error

bool write(uint32_t address, const void *src, size_t size)

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 Value
  • bool: true on success, false on error

bool erase_range(uint32_t address, size_t size)

Erase a region of storage in preparation for writing.

Parameters
  • address: Where to start erasing

  • size: Size of region to erase, in bytes

Return Value
  • bool: true on success, false on error

Partition createPartition(const String &name, const FSTR::ObjectBase &fstr, Partition::Type type, uint8_t subtype)

Create partition for FlashString data access.

References
Used by
Environment Variables
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
AXTLS 8266

SSL support using the AXTLS library

References
Used by
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
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

using Blake2s = HashContext<Blake2sEngine<hashsize>>
using Blake2s256 = Blake2s<32>
using Blake2s128 = Blake2s<16>
using HmacBlake2s = HmacContext<Blake2s<hashsize>>
using HmacBlake2s256 = HmacBlake2s<32>
using HmacBlake2s128 = HmacBlake2s<16>
using Secret = Blob

Identifies data which should be treated with care.

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

template<class HashContext>
class HmacContext
#include <HmacContext.h>

HMAC class template.

Implements the HMAC algorithm using any defined hash context

References
Used by
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
Submodule: esptool
esptool.py

A Python-based, open source, platform independent, utility to communicate with the ROM bootloader in Espressif ESP8266 & ESP32 chips.

esptool.py was started by Fredrik Ahlberg (@themadinventor) as an unofficial community project. It is now also supported by Espressif. Current primary maintainer is Angus Gratton (@projectgus).

esptool.py is Free Software under a GPLv2 license.

Build Status
Installation / dependencies
Easy Installation

You will need either Python 2.7 or Python 3.4 or newer installed on your system.

The latest stable esptool.py release can be installed from pypi via pip:

$ pip install esptool

With some Python installations this may not work and you’ll receive an error, try python -m pip install esptool or pip2 install esptool, or consult your Python installation manual for information about how to access pip.

After installing, you will have esptool.py installed into the default Python executables directory and you should be able to run it with the command esptool.py.

Development Mode Installation

Development mode allows you to run the latest development version from this repository.

$ git clone https://github.com/espressif/esptool.git
$ cd esptool
$ pip install --user -e .

This will install esptool’s dependencies and create some executable script wrappers in the user’s bin directory. The wrappers will run the the scripts found in the git working directory directly, so any time the working directory contents change it will pick up the new versions.

It’s also possible to run the scripts directly from the working directory with this Development Mode installation.

(Note: if you actually plan to do development work with esptool itself, see the CONTRIBUTING.md file.)

Usage

Use esptool.py -h to see a summary of all available commands and command line options.

To see all options for a particular command, append -h to the command name. ie esptool.py write_flash -h.

Common Options
Serial Port
  • The serial port is selected using the -p option, like -p /dev/ttyUSB0 (Linux and macOS) or -p COM1 (Windows).

  • A default serial port can be specified by setting the ESPTOOL_PORT environment variable.

  • If no -p option or ESPTOOL_PORT value is specified, esptool.py will enumerate all connected serial ports and try each one until it finds an Espressif device connected (new behaviour in v2.4.0).

Note: Windows and macOS may require drivers to be installed for a particular USB/serial adapter, before a serial port is available. Consult the documentation for your particular device. On macOS, you can also consult System Information’s list of USB devices to identify the manufacturer or device ID when the adapter is plugged in. On Windows, you can use Windows Update or Device Manager to find a driver.

If using Cygwin or WSL on Windows, you have to convert the Windows-style name into an Unix-style path (COM1 -> /dev/ttyS0, and so on). (This is not necessary if using esp-idf for ESP32 with the supplied Windows MSYS2 environment, this environment uses a native Windows Python which accepts COM ports as-is.)

In Linux, the current user may not have access to serial ports and a “Permission Denied” error will appear. On most Linux distributions, the solution is to add the user to the dialout group with a command like sudo usermod -a -G dialout <USERNAME>. Check your Linux distribution’s documentation for more information.

Baud rate

The default esptool.py baud rate is 115200bps. Different rates may be set using -b 921600 (or another baudrate of your choice). A default baud rate can also be specified using the ESPTOOL_BAUD environment variable. This can speed up write_flash and read_flash operations.

The baud rate is limited to 115200 when esptool.py establishes the initial connection, higher speeds are only used for data transfers.

Most hardware configurations will work with -b 230400, some with -b 460800, -b 921600 and/or -b 1500000 or higher.

If you have connectivity problems then you can also set baud rates below 115200. You can also choose 74880, which is the usual baud rate used by the ESP8266 to output boot log information.

Commands
Write binary data to flash: write_flash

Binary data can be written to the ESP’s flash chip via the serial write_flash command:

esptool.py --port COM4 write_flash 0x1000 my_app-0x01000.bin

Multiple flash addresses and file names can be given on the same command line:

esptool.py --port COM4 write_flash 0x00000 my_app.elf-0x00000.bin 0x40000 my_app.elf-0x40000.bin

The --chip argument is optional when writing to flash, esptool will detect the type of chip when it connects to the serial port.

The --port argument is documented under Serial Port.

The next arguments to write_flash are one or more pairs of offset (address) and file name. When generating ESP8266 “version 1” images, the file names created by elf2image include the flash offsets as part of the file name. For other types of images, consult your SDK documentation to determine the files to flash at which offsets.

Numeric values passed to write_flash (and other commands) can be specified either in hex (ie 0x1000), or in decimal (ie 4096).

See the Troubleshooting section if the write_flash command is failing, or the flashed module fails to boot.

Setting flash mode and size

You may also need to specify arguments for flash mode and flash size, if you wish to override the defaults. For example:

esptool.py --port /dev/ttyUSB0 write_flash --flash_mode qio --flash_size 32m 0x0 bootloader.bin 0x1000 my_app.bin

Since esptool v2.0, these options are not often needed as the default is to keep the flash mode and size from the .bin image file, and to detect the flash size. See the Flash Modes section for more details.

Compression

By default, the serial transfer data is compressed for better performance. The -u/--no-compress option disables this behaviour.

Read Flash Contents: read_flash

The read_flash command allows reading back the contents of flash. The arguments to the command are an address, a size, and a filename to dump the output to. For example, to read a full 2MB of attached flash:

./esptool.py -p PORT -b 460800 read_flash 0 0x200000 flash_contents.bin

(Note that if write_flash updated the boot image’s flash mode and flash size during flashing then these bytes may be different when read back.)

Erase Flash: erase_flash & erase region

To erase the entire flash chip (all data replaced with 0xFF bytes):

esptool.py erase_flash

To erase a region of the flash, starting at address 0x20000 with length 0x4000 bytes (16KB):

esptool.py erase_region 0x20000 0x4000

The address and length must both be multiples of the SPI flash erase sector size. This is 0x1000 (4096) bytes for supported flash chips.

Read built-in MAC address: read_mac
esptool.py read_mac
Read SPI flash id: flash_id
esptool.py flash_id

Example output:

Manufacturer: e0
Device: 4016
Detected flash size: 4MB

Refer to flashrom source code for flash chip manufacturer name and part number.

Convert ELF to Binary: elf2image

The elf2image command converts an ELF file (from compiler/linker output) into the binary executable images which can be flashed and then booted into:

esptool.py --chip esp8266 elf2image my_app.elf

This command does not require a serial connection.

elf2image also accepts the Flash Modes arguments --flash_freq and --flash_mode, which can be used to set the default values in the image header. This is important when generating any image which will be booted directly by the chip. These values can also be overwritten via the write_flash command, see the write_flash command for details.

elf2image for ESP8266

The default command output is two binary files: my_app.elf-0x00000.bin and my_app.elf-0x40000.bin. You can alter the firmware file name prefix using the --output/-o option.

elf2image can also produce a “version 2” image file suitable for use with a software bootloader stub such as rboot or the Espressif bootloader program. You can’t flash a “version 2” image without also flashing a suitable bootloader.

esptool.py --chip esp8266 elf2image --version=2 -o my_app-ota.bin my_app.elf
elf2image for ESP32

For esp32, elf2image produces a single output binary “image file”. By default this has the same name as the .elf file, with a .bin extension. ie:

esptool.py --chip esp32 elf2image my_esp32_app.elf

In the above example, the output image file would be called my_esp32_app.bin.

Output .bin image details: image_info

The image_info command outputs some information (load addresses, sizes, etc) about a .bin file created by elf2image.

esptool.py --chip esp32 image_info my_esp32_app.bin

Note that --chip esp32 is required when reading ESP32 images. Otherwise the default is --chip esp8266 and the image will be interpreted as an invalid ESP8266 image.

Advanced Commands

The following commands are less commonly used, or only of interest to advanced users. They are documented on the wiki:

Additional ESP32 Tools

The following tools for ESP32, bundled with esptool.py, are documented on the wiki:

Serial Connections

The ESP8266 & ESP32 ROM serial bootloader uses a 3.3V UART serial connection. Many development boards make the serial connections for you onboard.

However, if you are wiring the chip yourself to a USB/Serial adapter or similar then the following connections must be made:

ESP32/ESP8266 Pin

Serial Port Pin

TX (aka GPIO1)

RX (receive)

RX (aka GPIO3)

TX (transmit)

Ground

Ground

Note that TX (transmit) on the ESP8266 is connected to RX (receive) on the serial port connection, and vice versa.

Do not connect the chip to 5V TTL serial adapters, and especially not to “standard” RS-232 adapters! 3.3V serial only!

Entering the Bootloader

Both ESP8266 and ESP32 have to be reset in a certain way in order to launch the serial bootloader.

On some development boards (including NodeMCU, WeMOS, HUZZAH Feather, Core Board, ESP32-WROVER-KIT), esptool.py can automatically trigger a reset into the serial bootloader - in which case you don’t need to read this section.

For everyone else, three things must happen to enter the serial bootloader - a reset, required pins set correctly, and GPIO0 pulled low:

Boot Mode

Both ESP8266 and ESP32 chooses the boot mode each time it resets. A reset event can happen in one of several ways:

  • Power applied to chip.

  • The nRESET pin was low and is pulled high (on ESP8266 only).

  • The CH_PD/EN pin (“enable”) pin was low and is pulled high.

On ESP8266, both the nRESET and CH_PD pins must be pulled high for the chip to start operating.

For more details on selecting the boot mode, see the following Wiki pages:

Flash Modes

write_flash and some other comands accept command line arguments to set bootloader flash mode, flash size and flash clock frequency. The chip needs correct mode, frequency and size settings in order to run correctly - although there is some flexibility. A header at the beginning of a bootable image contains these values.

To override these values, the options --flash_mode, --flash_size and/or --flash_freq must appear after write_flash on the command line, for example:

esptool.py --port /dev/ttyUSB1 write_flash --flash_mode dio --flash_size 4MB 0x0 bootloader.bin

These options are only consulted when flashing a bootable image to an ESP8266 at offset 0x0, or an ESP32 at offset 0x1000. These are addresses used by the ROM bootloader to load from flash. When flashing at all other offsets, these arguments are not used.

Flash Mode (–flash_mode, -fm)

These set Quad Flash I/O or Dual Flash I/O modes. Valid values are keep, qio, qout, dio, dout. The default is keep, which keeps whatever value is already in the image file. This parameter can also be specified using the environment variable ESPTOOL_FM.

Most boards use qio mode. Some ESP8266 modules, including the ESP-12E modules on some (not all) NodeMCU boards, are dual I/O and the firmware will only boot when flashed with --flash_mode dio. Most ESP32 modules are also dual I/O.

In qio mode, two additional GPIOs (9 and 10) are used for SPI flash communications. If flash mode is set to dio then these pins are available for other purposes.

For a full explanation of these modes, see the SPI Flash Modes wiki page.

Flash Frequency (–flash_freq, -ff)

Clock frequency for SPI flash interactions. Valid values are keep, 40m, 26m, 20m, 80m (MHz). The default is keep, which keeps whatever value is already in the image file. This parameter can also be specified using the environment variable ESPTOOL_FF.

The flash chip connected to most chips works with 40MHz clock speeds, but you can try lower values if the device won’t boot. The highest 80MHz flash clock speed will give best performance, but may cause crashing if the flash or board designis not capable of this speed.

Flash Size (–flash_size, -fs)

Size of the SPI flash, given in megabytes. Valid values vary by chip type:

Chip

flash_size values

ESP32

detect, 1MB, 2MB, 4MB, 8MB, 16MB

ESP8266

detect, 256KB, 512KB, 1MB, 2MB, 4MB, 2MB-c1, 4MB-c1, 8MB, 16MB

For ESP8266, some additional sizes & layouts for OTA “firmware slots” are available.

The default --flash_size parameter is detect, which tries to autodetect size based on SPI flash ID. If detection fails, a warning is printed and a default value of of 4MB (4 megabytes) is used.

If flash size is not successfully detected, you can find the flash size by using the flash_id command and then looking up the ID from the output (see Read SPI flash id). Alternatively, read off the silkscreen labelling of the flash chip and search for its datasheet.

The default flash_size parameter can also be overriden using the environment variable ESPTOOL_FS.

ESP8266 and Flash Size

The ESP8266 SDK stores WiFi configuration at the “end” of flash, and it finds the end using this size. However there is no downside to specifying a smaller flash size than you really have, as long as you don’t need to write an image larger than this size.

ESP-12, ESP-12E and ESP-12F modules (and boards that use them such as NodeMCU, HUZZAH, etc.) usually have at least 4 megabyte / 4MB (sometimes labelled 32 megabit) flash.

If using OTA, some additional sizes & layouts for OTA “firmware slots” are available. If not using OTA updates then you can ignore these extra sizes:

flash_size arg

Number of OTA slots

OTA Slot Size

Non-OTA Space

256KB

1 (no OTA)

256KB

N/A

512KB

1 (no OTA)

512KB

N/A

1MB

2

512KB

0KB

2MB

2

512KB

1024KB

4MB

2

512KB

3072KB

2MB-c1

2

1024KB

0KB

4MB-c1

2

1024KB

2048KB

8MB [^]

2

1024KB

6144KB

16MB [^]

2

1024KB

14336KB

  • [^] Support for 8MB & 16MB flash size is not present in all ESP8266 SDKs. If your SDK doesn’t support these flash sizes, use --flash_size 4MB.

ESP32 and Flash Size

The ESP32 esp-idf flashes a partition table to the flash at offset 0x8000. All of the partitions in this table must fit inside the configured flash size, otherwise the ESP32 will not work correctly.

Advanced Options

See the Advanced Options wiki page for some of the more unusual esptool.py command line options.

Remote Serial Ports

It is possible to connect to any networked remote serial port that supports RFC2217 (Telnet) protocol, or a plain TCP socket. See the Remote Serial Ports wiki page for details.

Troubleshooting

Flashing problems can be fiddly to troubleshoot. Try the suggestions here if you’re having problems:

Bootloader won’t respond

If you see errors like “Failed to connect” then your chip is probably not entering the bootloader properly:

  • Check you are passing the correct serial port on the command line.

  • Check you have permissions to access the serial port, and other software (such as modem-manager on Linux) is not trying to interact with it. A common pitfall is leaving a serial terminal accessing this port open in another window and forgetting about it.

  • Check the chip is receiving 3.3V from a stable power source (see Insufficient Power for more details.)

  • Check that all pins are connected as described in Entering the bootloader. Check the voltages at each pin with a multimeter, “high” pins should be close to 3.3V and “low” pins should be close to 0V.

  • If you have connected other devices to GPIO pins mentioned above section, try removing them and see if esptool.py starts working.

  • Try using a slower baud rate (-b 9600 is a very slow value that you can use to verify it’s not a baud rate problem.)

write_flash operation fails part way through

If flashing fails with random errors part way through, retry with a lower baud rate.

Power stability problems may also cause this (see Insufficient Power.)

write_flash succeeds but program doesn’t run

If esptool.py can flash your module with write_flash but your program doesn’t run, try the following:

Wrong Flash Mode

Some devices only support the dio flash mode. Writing to flash with qio mode will succeed but the chip can’t read the flash back to run - so nothing happens on boot. Try passing the -fm dio option to write_flash.

See the SPI Flash Modes wiki page for a full description of the flash modes and how to determine which ones are supported on your device.

Insufficient Power

The 3.3V power supply for the ESP8266 and ESP32 has to supply large amounts of current (up to 70mA continuous, 200-300mA peak, slightly higher for ESP32). You also need sufficient capacitance on the power circuit to meet large spikes of power demand.

Insufficient Capacitance

If you’re using a pre-made development board or module then the built-in power regulator & capacitors are usually good enough, provided the input power supply is adequate.

This is not true for some very simple pin breakout modules - `similar to this <https://user-images.githubusercontent.com/205573/30140831-9da417a6-93ba-11e7-95c3-f422744967de.jpg>`_. These breakouts do not integrate enough capacitance to work reliably without additional components.. Surface mount OEM modules like ESP-WROOM02 and ESP-WROOM32 require an external bulk capacitor on the PCB to be reliable, consult the module datasheet.

Power Supply Rating

It is possible to have a power supply that supplies enough current for the serial bootloader stage with esptool.py, but not enough for normal firmware operation. You may see the 3.3V VCC voltage droop down if you measure it with a multimeter, but you can have problems even if this isn’t happening.

Try swapping in a 3.3V supply with a higher current rating, add capacitors to the power line, and/or shorten any 3.3V power wires.

The 3.3V output from FTDI FT232R chips/adapters or Arduino boards do not supply sufficient current to power an ESP8266 or ESP32 (it may seem to work sometimes, but it won’t work reliably). Other USB TTL/serial adapters may also be marginal.

Missing bootloader

Recent ESP8266 SDKs and the ESP32 esp-idf both use a small firmware bootloader program. The hardware bootloader in ROM loads this firmware bootloader from flash, and then it runs the program. On ESP8266. firmware bootloader image (with a filename like boot_v1.x.bin) has to be flashed at offset 0. If the firmware bootloader is missing then the ESP8266 will not boot. On ESP32, the bootloader image should be flashed by esp-idf at offset 0x1000.

Refer to SDK or esp-idf documentation for details regarding which binaries need to be flashed at which offsets.

SPI Pins which must be disconnected

Compared to the ROM bootloader that esptool.py talks to, a running firmware uses more of the chip’s pins to access the SPI flash.

If you set “Quad I/O” mode (-fm qio, the esptool.py default) then GPIOs 7, 8, 9 & 10 are used for reading the SPI flash and must be otherwise disconnected.

If you set “Dual I/O” mode (-fm dio) then GPIOs 7 & 8 are used for reading the SPI flash and must be otherwise disconnected.

Try disconnecting anything from those pins (and/or swap to Dual I/O mode if you were previously using Quad I/O mode but want to attach things to GPIOs 9 & 10). Note that if GPIOs 9 & 10 are also connected to input pins on the SPI flash chip, they may still be unsuitable for use as general purpose I/O.

In addition to these pins, GPIOs 6 & 11 are also used to access the SPI flash (in all modes). However flashing will usually fail completely if these pins are connected incorrectly.

Early stage crash

Use a serial terminal program to view the boot log. (ESP8266 baud rate is 74880bps, ESP32 is 115200bps). See if the program is crashing during early startup or outputting an error message.

Serial Terminal Programs

There are many serial terminal programs suitable for debugging & serial interaction. The pyserial module (which is required for esptool.py) includes one such command line terminal program - miniterm.py. For more details see this page or run miniterm -h.

Note that not every serial program supports the unusual ESP8266 74880bps “boot log” baud rate. Support is especially sparse on Linux. miniterm.py supports this baud rate on all platforms. ESP32 uses the more common 115200bps.

Tracing esptool.py interactions

Running esptool.py --trace will dump all serial interactions to the standard output (this is a lot of output). This can be helpful when debugging issues with the serial connection, or when providing information for bug reports.

Using esptool.py from Python

esptool.py can easily be integrated into Python applications or called from other Python scripts.

While it currently does have a poor Python API, something which #208 will address, it allows for passing CLI arguments to esptool.main(). This workaround makes integration very straightforward as you can pass exactly the same arguments as you would on the CLI.

command = ['--baud', '460800', 'read_flash', '0', '0x200000', 'flash_contents.bin']
print('Using command %s' % ' '.join(command))
esptool.main(command)
Internal Technical Documentation

The repository wiki contains some technical documentation regarding the serial protocol and file format used by the ROM bootloader. This may be useful if you’re developing esptool.py or hacking system internals:

About

esptool.py was initially created by Fredrik Ahlberg (@themadinventor, @kongo), and is currently maintained by Angus Gratton (@projectgus). It has also received improvements from many members of the ESP8266 community - including @rojer, @jimparis, @jms19, @pfalcon, @tommie, @0ff, @george-hopkins and others.

This document and the attached source code are released under GNU General Public License Version 2. See the accompanying file LICENSE for a copy.

HTTP Parser
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
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 seperately. 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
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
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 verison.

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 verison.

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

References
Used by
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
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_1"
      }
   }
}

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 Value

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 Value
  • 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 Value
  • 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 Value
  • 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

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.

See

rboot_write_flash

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
References
Used by
Environment Variables
Submodule: rboot
rBoot - An open source boot loader for the ESP8266

by Richard A Burton, richardaburton@gmail.com http://richard.burtons.org/

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 deafult) 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 overriden 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.

Integration into other frameworks

If you wish to integrate rBoot into a development framework (e.g. Sming) you can set the define RBOOT_INTEGRATION and at compile time the file rboot-integration.h will be included into the source. This should allow you to set some platform specific options without having to modify the source of rBoot which makes it easier to integrate and maintain.

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

const SessionId *getSessionId() const

If available, return the current SSL Session ID.

Return Value
  • 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 Value
  • bool: true if the connection may proceed, false to abort

void setConnection(Connection *connection)

Called by TcpConnection to set the established SSL connection.

Parameters
  • connection: The server connection

Connection *getConnection()

Get the currently active SSL connection object.

Return Value
  • Connection*:

bool onConnect(tcp_pcb *tcp)

Handle connection event.

Parameters
  • tcp:

Return Value
  • bool: true on success, false to abort the connection

bool isConnected() const

Determine if an SSL connection has been fully established.

Return Value

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 Value
  • 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 Value
  • 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 Value
  • 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

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 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:

Default = 0

Let SSL implementation decide.

B512

512 bytes

K1

1024 bytes

K2
K4
K8
K16
Cipher Suites
enum Ssl::CipherSuite

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

Refer to CipherSuite.h for defined values.

Values:

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.

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.

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 Type

SSL Certificate fingerprint type.

Values:

CertSha1

SHA1 Fingerprint of entire certificate.

CertSha256

SHA256 Fingerprint of entire certificate.

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

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

constexpr Type type = Type::CertSha256
union Pki
#include <Fingerprints.h>

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

constexpr Type type = Type::PkiSha256
class Types
#include <Fingerprints.h>

Maintains a set of fingerprint types.

Public Functions

void add(Type type)
void remove(Type type)
bool contains(Type type) const

Private Members

uint32_t mask = 0
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 Ssl::Alert

Alert codes defined by the standard.

See

See https://tools.ietf.org/html/rfc8446#page-85

Values:

Invalid = -1

Not an alert code.

XX
Classes
class Factory

Implemented by SSL adapter.

See

https://en.wikipedia.org/wiki/Factory_method_pattern

Public Functions

virtual Context *createContext(Session &session) = 0

Create SSL context that can be used to create new client or server connections.

Return Value
  • 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 Value
  • 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 Value
  • 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 Value
  • 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 Value
  • 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 Value
  • 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 Value
  • 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 Value
  • 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 Value

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 Value
  • Certificate*: Returns NULL if there is no certificate available

size_t printTo(Print &p) const

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 Value
  • Alert: Alert::INVALID if not an alert

References
Used by
Environment Variables
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
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
References
Environment Variables

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.

Requirements

In order to be able to compile for the ESP32 architecture you should have ESP-IDF v4.1 installed. The Sming installers can do this for you - 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 sdk-build

If you want to revert to using the default pre-compiled SDK then issue the following command:

make SMING_ARCH=Esp32 sdk-default

Note

If you have an ESP32-S2 device you’ll need to change ESP_VARIANT:

make ESP_VARIANT=esp32s2

See Esp32 Core Component for further details.

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

Esp32 Drivers

Provides low-level peripheral drivers.

GPIO: General-Purpose I/O

SDK definitions for GPIO.

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::hw_timer_clkdiv_t

Values:

TIMER_CLKDIV_1 = 0
TIMER_CLKDIV_16 = 4
TIMER_CLKDIV_256 = 8
TIMER_CLKDIV_1 = 0
TIMER_CLKDIV_16 = 4
TIMER_CLKDIV_256 = 8
enum hw_timer::hw_timer_intr_type_t

Values:

TIMER_EDGE_INT = 0
TIMER_LEVEL_INT = 1
TIMER_EDGE_INT = 0
TIMER_LEVEL_INT = 1
enum hw_timer::hw_timer_source_type_t

Values:

TIMER_FRC1_SOURCE = 0
TIMER_NMI_SOURCE = 1
TIMER_FRC1_SOURCE = 0
TIMER_NMI_SOURCE = 1
enum hw_timer::hw_timer_clkdiv_t

Values:

TIMER_CLKDIV_1 = 0
TIMER_CLKDIV_16 = 4
TIMER_CLKDIV_256 = 8
TIMER_CLKDIV_1 = 0
TIMER_CLKDIV_16 = 4
TIMER_CLKDIV_256 = 8
enum hw_timer::hw_timer_intr_type_t

Values:

TIMER_EDGE_INT = 0
TIMER_LEVEL_INT = 1
TIMER_EDGE_INT = 0
TIMER_LEVEL_INT = 1
enum hw_timer::hw_timer_source_type_t

Values:

TIMER_FRC1_SOURCE = 0
TIMER_NMI_SOURCE = 1
TIMER_FRC1_SOURCE = 0
TIMER_NMI_SOURCE = 1
typedef void (*hw_timer_callback_t)(void *arg)
typedef void (*hw_timer_callback_t)(void *arg)
constexpr uint32_t HW_TIMER2_CLKDIV = TIMER_CLKDIV_256
constexpr uint32_t HW_TIMER2_CLK = HW_TIMER_BASE_CLK >> HW_TIMER2_CLKDIV
constexpr uint32_t HW_TIMER2_CLKDIV = TIMER_CLKDIV_1
constexpr uint32_t HW_TIMER2_CLK = HW_TIMER_BASE_CLK >> HW_TIMER2_CLKDIV
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:

  • callback: Callback function invoked via timer interrupt

  • arg: Passed to callback function

void hw_timer1_enable(hw_timer_clkdiv_t div, hw_timer_intr_type_t intr_type, bool auto_load)

Enable the timer.

Parameters
  • div:

  • intr_type:

  • auto_load:

void hw_timer1_write(uint32_t ticks)

Set the timer interval.

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 Value
  • uint32_t: Current count value, counts from initial value down to 0

uint32_t hw_timer2_read(void)

Read current timer2 value.

Return Value
  • uint32_t:

void hw_timer2_set_alarm(uint32_t ticks)

Set timer2 alarm count value.

Note

For internal use ONLY; used by software timers

Parameters
  • ticks:

void hw_timer_init(void)

Initialise hardware timers.

Note

Called by startup code

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.

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.

NOW()
I2S: Inter-IC Serial communcations
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 namespace “i2s_driver” in doxygen xml output for project “api” from directory: ../api/xml/

OS Timer
typedef ETSTimerFunc os_timer_func_t
typedef ETSTimer os_timer_t
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
  • ptimer: Timer structure

  • ticks: Tick count duration for the timer

  • repeat_flag: true if timer will automatically repeat

os_timer_arm(ptimer, ms, repeat_flag)
os_timer_arm_us(ptimer, us, repeat_flag)
os_timer_disarm(ptimer)
os_timer_setfn(ptimer, pfunction, parg)
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
UART: Universal Asynchronous Receive/Transmit

Custom asynchronous driver.

Warning

doxygengroup: Cannot find namespace “uart_driver” in doxygen xml output for project “api” from directory: ../api/xml/

References
Used by
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

These are read-only debug variables:

SDK_INTERNAL

READONLY When compiled using the current (version 3+) Espressif SDK this value is set to 1.

SDK_LIBDIR

READONLY Path to the directory containing SDK archive libraries

SDK_INCDIR

READONLY Path to the directory containing SDK header files

Option variables:

ESP_VARIANT

Build for for esp32 or esp32s2 device

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.

References
Used by
Environment Variables
  • SDK_FULL_BUILD

Esp8266 WiFi

All related libraries for WiFi support. Definitions are provided in the Espressif Non-OS SDK.

References
Used by
FAT Filing System

Required by the SDCard library.

http://elm-chan.org/fsw/ff/00index_e.html

References
Used by
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
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
Esp32 LIBC Component

This Component accommodates the differences in runtime libraries for the various supported toolchains.

References
Used by
Esp8266 SPI Flash Support

Provides functions for access to flash memory.

API Documentation
enum spi_flash::SPIFlashMode

Values:

MODE_QIO = 0
MODE_QOUT = 1
MODE_DIO = 2
MODE_DOUT = 15
MODE_SLOW_READ = 0xFE

Not supported.

MODE_FAST_READ = 0xFF

Not supported.

MODE_QIO = ESP_IMAGE_SPI_MODE_QIO
MODE_QOUT = ESP_IMAGE_SPI_MODE_QOUT
MODE_DIO = ESP_IMAGE_SPI_MODE_DIO
MODE_DOUT = ESP_IMAGE_SPI_MODE_DOUT
MODE_FAST_READ = ESP_IMAGE_SPI_MODE_FAST_READ
MODE_SLOW_READ = ESP_IMAGE_SPI_MODE_SLOW_READ
enum spi_flash::SPIFlashSpeed

Values:

SPEED_40MHZ = 0
SPEED_26MHZ = 1
SPEED_20MHZ = 2
SPEED_80MHZ = 15
SPEED_40MHZ = ESP_IMAGE_SPI_SPEED_40M
SPEED_26MHZ = ESP_IMAGE_SPI_SPEED_26M
SPEED_20MHZ = ESP_IMAGE_SPI_SPEED_20M
SPEED_80MHZ = ESP_IMAGE_SPI_SPEED_80M
enum spi_flash::SPIFlashSize

Values:

SIZE_4MBIT = 0
SIZE_2MBIT = 1
SIZE_8MBIT = 2
SIZE_16MBIT = 3
SIZE_32MBIT = 4
SIZE_1MBIT = 0xFF

Not supported.

SIZE_1MBIT = ESP_IMAGE_FLASH_SIZE_1MB
SIZE_2MBIT = ESP_IMAGE_FLASH_SIZE_2MB
SIZE_4MBIT = ESP_IMAGE_FLASH_SIZE_4MB
SIZE_8MBIT = ESP_IMAGE_FLASH_SIZE_8MB
SIZE_16MBIT = ESP_IMAGE_FLASH_SIZE_16MB
SIZE_32MBIT = 0xFF

Not listed.

enum spi_flash::SPIFlashMode

Values:

MODE_QIO = 0
MODE_QOUT = 1
MODE_DIO = 2
MODE_DOUT = 15
MODE_SLOW_READ = 0xFE

Not supported.

MODE_FAST_READ = 0xFF

Not supported.

MODE_QIO = ESP_IMAGE_SPI_MODE_QIO
MODE_QOUT = ESP_IMAGE_SPI_MODE_QOUT
MODE_DIO = ESP_IMAGE_SPI_MODE_DIO
MODE_DOUT = ESP_IMAGE_SPI_MODE_DOUT
MODE_FAST_READ = ESP_IMAGE_SPI_MODE_FAST_READ
MODE_SLOW_READ = ESP_IMAGE_SPI_MODE_SLOW_READ
enum spi_flash::SPIFlashSpeed

Values:

SPEED_40MHZ = 0
SPEED_26MHZ = 1
SPEED_20MHZ = 2
SPEED_80MHZ = 15
SPEED_40MHZ = ESP_IMAGE_SPI_SPEED_40M
SPEED_26MHZ = ESP_IMAGE_SPI_SPEED_26M
SPEED_20MHZ = ESP_IMAGE_SPI_SPEED_20M
SPEED_80MHZ = ESP_IMAGE_SPI_SPEED_80M
enum spi_flash::SPIFlashSize

Values:

SIZE_4MBIT = 0
SIZE_2MBIT = 1
SIZE_8MBIT = 2
SIZE_16MBIT = 3
SIZE_32MBIT = 4
SIZE_1MBIT = 0xFF

Not supported.

SIZE_1MBIT = ESP_IMAGE_FLASH_SIZE_1MB
SIZE_2MBIT = ESP_IMAGE_FLASH_SIZE_2MB
SIZE_4MBIT = ESP_IMAGE_FLASH_SIZE_4MB
SIZE_8MBIT = ESP_IMAGE_FLASH_SIZE_8MB
SIZE_16MBIT = ESP_IMAGE_FLASH_SIZE_16MB
SIZE_32MBIT = 0xFF

Not listed.

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 Value
  • 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 Value
  • 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 Value
  • 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 Value
  • true: on success

SPIFlashInfo flashmem_get_info()

Get flash memory information block.

Return Value

uint8_t flashmem_get_size_type()

Returns a number indicating the size of flash memory chip.

Return Value
  • uint8_t: See SpiFlashInfo.size field for possible values

uint32_t flashmem_get_size_bytes()

get the total flash memory size

Return Value
  • uint32_t: Size in bytes

uint16_t flashmem_get_size_sectors()

Get the total number of flash sectors.

Return Value
  • 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 Value
  • 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 Value
  • uint32_t: sector number

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 Value
  • 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 Value
  • uint32_t: Number of bytes actually read

uint32_t flashmem_get_first_free_block_address()
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
SPI_FLASH_RESULT_OK
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
struct STORE_TYPEDEF_ATTR
#include <esp_spi_flash.h>

SPI Flash memory information block. Stored at the beginning of flash memory.

struct SPIFlashInfo
#include <esp_spi_flash.h>

SPI Flash memory information block. Copied from bootloader header. See esp_image_header_t.

References
Used by

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
Esp8266 Drivers

Provides low-level peripheral drivers.

GPIO: General-Purpose I/O

SDK definitions for GPIO.

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::hw_timer_clkdiv_t

Values:

TIMER_CLKDIV_1 = 0
TIMER_CLKDIV_16 = 4
TIMER_CLKDIV_256 = 8
TIMER_CLKDIV_1 = 0
TIMER_CLKDIV_16 = 4
TIMER_CLKDIV_256 = 8
enum hw_timer::hw_timer_intr_type_t

Values:

TIMER_EDGE_INT = 0
TIMER_LEVEL_INT = 1
TIMER_EDGE_INT = 0
TIMER_LEVEL_INT = 1
enum hw_timer::hw_timer_source_type_t

Values:

TIMER_FRC1_SOURCE = 0
TIMER_NMI_SOURCE = 1
TIMER_FRC1_SOURCE = 0
TIMER_NMI_SOURCE = 1
enum hw_timer::hw_timer_clkdiv_t

Values:

TIMER_CLKDIV_1 = 0
TIMER_CLKDIV_16 = 4
TIMER_CLKDIV_256 = 8
TIMER_CLKDIV_1 = 0
TIMER_CLKDIV_16 = 4
TIMER_CLKDIV_256 = 8
enum hw_timer::hw_timer_intr_type_t

Values:

TIMER_EDGE_INT = 0
TIMER_LEVEL_INT = 1
TIMER_EDGE_INT = 0
TIMER_LEVEL_INT = 1
enum hw_timer::hw_timer_source_type_t

Values:

TIMER_FRC1_SOURCE = 0
TIMER_NMI_SOURCE = 1
TIMER_FRC1_SOURCE = 0
TIMER_NMI_SOURCE = 1
typedef void (*hw_timer_callback_t)(void *arg)
typedef void (*hw_timer_callback_t)(void *arg)
constexpr uint32_t HW_TIMER2_CLKDIV = TIMER_CLKDIV_256
constexpr uint32_t HW_TIMER2_CLK = HW_TIMER_BASE_CLK >> HW_TIMER2_CLKDIV
constexpr uint32_t HW_TIMER2_CLKDIV = TIMER_CLKDIV_1
constexpr uint32_t HW_TIMER2_CLK = HW_TIMER_BASE_CLK >> HW_TIMER2_CLKDIV
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:

  • callback: Callback function invoked via timer interrupt

  • arg: Passed to callback function

void hw_timer1_enable(hw_timer_clkdiv_t div, hw_timer_intr_type_t intr_type, bool auto_load)

Enable the timer.

Parameters
  • div:

  • intr_type:

  • auto_load:

void hw_timer1_write(uint32_t ticks)

Set the timer interval.

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 Value
  • uint32_t: Current count value, counts from initial value down to 0

uint32_t hw_timer2_read(void)

Read current timer2 value.

Return Value
  • uint32_t:

void hw_timer2_set_alarm(uint32_t ticks)

Set timer2 alarm count value.

Note

For internal use ONLY; used by software timers

Parameters
  • ticks:

void hw_timer_init(void)

Initialise hardware timers.

Note

Called by startup code

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.

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.

NOW()
I2S: Inter-IC Serial communcations
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 namespace “i2s_driver” in doxygen xml output for project “api” from directory: ../api/xml/

OS Timer
typedef ETSTimerFunc os_timer_func_t
typedef ETSTimer os_timer_t
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
  • ptimer: Timer structure

  • ticks: Tick count duration for the timer

  • repeat_flag: true if timer will automatically repeat

os_timer_arm(ptimer, ms, repeat_flag)
os_timer_arm_us(ptimer, us, repeat_flag)
os_timer_disarm(ptimer)
os_timer_setfn(ptimer, pfunction, parg)
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
UART: Universal Asynchronous Receive/Transmit

Custom asynchronous driver.

Warning

doxygengroup: Cannot find namespace “uart_driver” in doxygen xml output for project “api” from directory: ../api/xml/

References
Used by
Environment Variables
Submodule: new-pwm
ESP8266_new_pwm

This is a drop-in replacement for the ESP8266 SDK PWM

If you like this project and want to support this and my other works, consider donating on Liberapay

The software PWM provided in the ESP8266 SDK from Espressif has several drawbacks:

  1. Duty cycle limited to 90% (at 1kHz PWM period)

  2. usable PWM period at most ~2KHz.

  3. Incomplete documentation

This replacement allows duty cycles from 0% to 100%, with a stepsize of 200ns. This is 5000 steps for a 1kHz PWM, and 256 steps (8 bit of resolution) at 19kHz.

If all channels are in steady state (either 0% of 100% in any combination), the implementation goes to full idle, e.g. no interrupts.

The code is a drop-in replacement for the SDK, it provides the same functions as the SDK libpwm.a closed binary library. Just add pwm.c to your project.

By default there is one small difference to the SDK. The code uses a unit of 200ns for both period and duty. E.g. for 10% duty cycle at 1kHz you need to specify a period value of 5000 and a duty cycle value of 500, a duty cycle of 5000 or above switches the channel to full on.

To have full compatibility with the SDK, you have to set the SDK_PWM_PERIOD_COMPAT_MODE define to 1. If set, the code will use 1us for PWM period and 40ns for the duty cycle. E.g. 10% duty cycle at 1kHz is set by a period value of 1000 and a duty cycle value of 2500, full duty at 25000 and above.

Example usage:

#define PWM_CHANNELS 5
const uint32_t period = 5000 // * 200ns ^= 1 kHz

// PWM setup
uint32 io_info[PWM_CHANNELS][3] = {
    // MUX, FUNC, PIN
    {PERIPHS_IO_MUX_MTDI_U,  FUNC_GPIO12, 12},
    {PERIPHS_IO_MUX_MTDO_U,  FUNC_GPIO15, 15},
    {PERIPHS_IO_MUX_MTCK_U,  FUNC_GPIO13, 13},
    {PERIPHS_IO_MUX_MTMS_U,  FUNC_GPIO14, 14},
    {PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5 ,  5},
};

// initial duty: all off
uint32 pwm_duty_init[PWM_CHANNELS] = {0, 0, 0, 0, 0};

pwm_init(period, pwm_duty_init, PWM_CHANNELS, io_info);
pwm_start();

// do something like this whenever you want to change duty
pwm_set_duty(500, 1);  // GPIO15: 10%
pwm_set_duty(5000, 1); // GPIO15: 100%
pwm_start();           // commit

CAVEATS

  • To set 100% duty, the duty must be *equal* to the period

  • The code uses the TIMER1 interrupt. If you use e.g. the softtimer, there is a conflict. You can use NM1 for the PWM instead.

Esp8266 LWIP (Espressif)

The Espressif SDK provides a default version of LWIP, enabled by using this Component.

See ENABLE_CUSTOM_LWIP.

References
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
Submodule: esp-open-lwip
Esp8266 Core Component

Contains startup code, crash handling and additional Esp8266-specific support code. Sming may be built using a pre-installed SDK, or by using the current version 3 SDK as a submodule.

Attention

At time of writing, SDK 3 does not appear to support use of devices with 256K or 512K memory, such as the ESP-01. For now, please use the default SDK 1.5.4 or SDK 2.0.0.

SDK_BASE

Points to the location of the Espressif Non-OS SDK. To use the Espressif version 3 SDK, you need only set this variable to point at the Sming repository (SMING_HOME). The actual location will be subsituted by the build system and the SDK pulled in via GIT.

So for Windows you need to do:

set SDK_BASE=%SMING_HOME%

For Linux (bash):

export SDK_BASE="$SMING_HOME"

If you change this value then your application and Sming must both be recompiled:

make components-clean clean
make
SDK_INTERNAL

READONLY When compiled using the current (version 3+) Espressif SDK this value is set to 1.

SDK_LIBDIR

READONLY Path to the directory containing SDK archive libraries

SDK_INCDIR

READONLY Path to the directory containing SDK header files

References
Used by
Environment Variables
Submodule: ESP8266_NONOS_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.

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
Esp8266 WiFi

All related libraries for WiFi support. Definitions are provided in the Espressif Non-OS SDK.

References
Used by
FAT Filing System

Required by the SDCard library.

http://elm-chan.org/fsw/ff/00index_e.html

References
Used by
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 documention 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 neccessary 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
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
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
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
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 spi_flash::SPIFlashMode

Values:

MODE_QIO = 0
MODE_QOUT = 1
MODE_DIO = 2
MODE_DOUT = 15
MODE_SLOW_READ = 0xFE

Not supported.

MODE_FAST_READ = 0xFF

Not supported.

MODE_QIO = ESP_IMAGE_SPI_MODE_QIO
MODE_QOUT = ESP_IMAGE_SPI_MODE_QOUT
MODE_DIO = ESP_IMAGE_SPI_MODE_DIO
MODE_DOUT = ESP_IMAGE_SPI_MODE_DOUT
MODE_FAST_READ = ESP_IMAGE_SPI_MODE_FAST_READ
MODE_SLOW_READ = ESP_IMAGE_SPI_MODE_SLOW_READ
enum spi_flash::SPIFlashSpeed

Values:

SPEED_40MHZ = 0
SPEED_26MHZ = 1
SPEED_20MHZ = 2
SPEED_80MHZ = 15
SPEED_40MHZ = ESP_IMAGE_SPI_SPEED_40M
SPEED_26MHZ = ESP_IMAGE_SPI_SPEED_26M
SPEED_20MHZ = ESP_IMAGE_SPI_SPEED_20M
SPEED_80MHZ = ESP_IMAGE_SPI_SPEED_80M
enum spi_flash::SPIFlashSize

Values:

SIZE_4MBIT = 0
SIZE_2MBIT = 1
SIZE_8MBIT = 2
SIZE_16MBIT = 3
SIZE_32MBIT = 4
SIZE_1MBIT = 0xFF

Not supported.

SIZE_1MBIT = ESP_IMAGE_FLASH_SIZE_1MB
SIZE_2MBIT = ESP_IMAGE_FLASH_SIZE_2MB
SIZE_4MBIT = ESP_IMAGE_FLASH_SIZE_4MB
SIZE_8MBIT = ESP_IMAGE_FLASH_SIZE_8MB
SIZE_16MBIT = ESP_IMAGE_FLASH_SIZE_16MB
SIZE_32MBIT = 0xFF

Not listed.

enum spi_flash::SPIFlashMode

Values:

MODE_QIO = 0
MODE_QOUT = 1
MODE_DIO = 2
MODE_DOUT = 15
MODE_SLOW_READ = 0xFE

Not supported.

MODE_FAST_READ = 0xFF

Not supported.

MODE_QIO = ESP_IMAGE_SPI_MODE_QIO
MODE_QOUT = ESP_IMAGE_SPI_MODE_QOUT
MODE_DIO = ESP_IMAGE_SPI_MODE_DIO
MODE_DOUT = ESP_IMAGE_SPI_MODE_DOUT
MODE_FAST_READ = ESP_IMAGE_SPI_MODE_FAST_READ
MODE_SLOW_READ = ESP_IMAGE_SPI_MODE_SLOW_READ
enum spi_flash::SPIFlashSpeed

Values:

SPEED_40MHZ = 0
SPEED_26MHZ = 1
SPEED_20MHZ = 2
SPEED_80MHZ = 15
SPEED_40MHZ = ESP_IMAGE_SPI_SPEED_40M
SPEED_26MHZ = ESP_IMAGE_SPI_SPEED_26M
SPEED_20MHZ = ESP_IMAGE_SPI_SPEED_20M
SPEED_80MHZ = ESP_IMAGE_SPI_SPEED_80M
enum spi_flash::SPIFlashSize

Values:

SIZE_4MBIT = 0
SIZE_2MBIT = 1
SIZE_8MBIT = 2
SIZE_16MBIT = 3
SIZE_32MBIT = 4
SIZE_1MBIT = 0xFF

Not supported.

SIZE_1MBIT = ESP_IMAGE_FLASH_SIZE_1MB
SIZE_2MBIT = ESP_IMAGE_FLASH_SIZE_2MB
SIZE_4MBIT = ESP_IMAGE_FLASH_SIZE_4MB
SIZE_8MBIT = ESP_IMAGE_FLASH_SIZE_8MB
SIZE_16MBIT = ESP_IMAGE_FLASH_SIZE_16MB
SIZE_32MBIT = 0xFF

Not listed.

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 Value
  • 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 Value
  • 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 Value
  • 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 Value
  • true: on success

SPIFlashInfo flashmem_get_info()

Get flash memory information block.

Return Value

uint8_t flashmem_get_size_type()

Returns a number indicating the size of flash memory chip.

Return Value
  • uint8_t: See SpiFlashInfo.size field for possible values

uint32_t flashmem_get_size_bytes()

get the total flash memory size

Return Value
  • uint32_t: Size in bytes

uint16_t flashmem_get_size_sectors()

Get the total number of flash sectors.

Return Value
  • 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 Value
  • 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 Value
  • uint32_t: sector number

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 Value
  • 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 Value
  • uint32_t: Number of bytes actually read

uint32_t flashmem_get_first_free_block_address()
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
SPI_FLASH_RESULT_OK
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
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.

struct SPIFlashInfo
#include <esp_spi_flash.h>

SPI Flash memory information block. Copied from bootloader header. See esp_image_header_t.

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

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

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
make SMING_ARCH=Host
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.

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
References
Used by
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 Value
  • 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 Value
  • unsigned: long Pulse duration in microseconds

virtual uint16_t analogRead(uint16_t pin)

Sample analogue input.

Parameters
  • pin: GPIO to read

Return Value
  • 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.)

By default, output to UART0 is sent to the console and keyboard input is written to the UART0 receive queue. If emulation is enabled on any ports then this behaviour is disabled.

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.

References
Used by
Environment Variables
Host ESP HAL

Provides implementations for various low-level functions similar to the Esp8266.

  • Task queues

  • Timer queues

  • System functions

References
Used by
Host WiFi

Provides WiFi / network functions. The actual implementations are provided in LWIP

References
Used by
Environment Variables
  • HOST_NETWORK_OPTIONS

fatfs

This Component is required to allow some samples to compile for the Host, however it doesn’t provide any functionality at present.

References
Used by
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
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
Host Library

This Components provides the core funcionality 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, subsquent 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 priviledged 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. 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 pre-empt it.

The set_interrupt_level function is used to ensure that threads running at different interrupt levels do not pre-empty each other, as this would introduce problems that do not exist on real hardware. The main thread is also suspended during interrupt execution.

LWIP_SERVICE_INTERVAL

Default: 2ms

LWIP stack is serviced via polling, this determines the interval.

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

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
libc

Contains implementations of any non-standard C library functions.

References
Used by
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 priviledge:

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
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

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
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

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.

group hardware_timer

Hardware callback timer.

Typedefs

using HardwareTimer1 = CallbackTimer<Timer1Api<clkdiv, mode>>

Hardware Timer class template with selectable divider and interrupt mode.

using HardwareTimer = HardwareTimer1<>

Default hardware Timer class.

typedef HardwareTimer Hardware_Timer

Enums

enum HardwareTimerMode

Hardware Timer interrupt mode.

Values:

eHWT_Maskable
eHWT_NonMaskable

Variables

uint8_t state
uint32_t 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.

Typedefs

typedef std::function<void()> TimerDelegateStdFunction

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>
DelegateCallbackTimer &initializeUs(TimerDelegate delegateFunction)

Initialise timer in microseconds, with static check.

Template Parameters
  • microseconds: Timer interval in microseconds

Parameters
  • delegateFunction: Function to call when timer triggers

Return Value
  • ExtendedCallbackTimer&: Reference to timer

template<uint32_t milliseconds>
DelegateCallbackTimer &initializeMs(TimerDelegate delegateFunction)

Initialise hardware timer in milliseconds, with static check.

Template Parameters
  • milliseconds: Timer interval in milliseconds

Parameters
  • delegateFunction: Function to call when timer triggers

Return Value
  • ExtendedCallbackTimer&: Reference to timer

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

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

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< TimerApi >

Public Functions

template<NanoTime::Unit unit, TimeType time>
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

  • time: Timer interval

Parameters
  • callback: Callback function to call when timer triggers

  • arg: Optional argument passed to callback

Return Value
  • CallbackTimer&: Reference to timer

template<NanoTime::Unit unit>
CallbackTimer &initialize(TimeType time, TimerCallback callback, void *arg = nullptr)

Initialise timer with an interval and callback.

Template Parameters
  • unit: Time unit for interval

Parameters
  • time: Timer interval

  • callback: Callback function to call when timer triggers

  • arg: Optional argument passed to callback

Return Value
  • CallbackTimer&: Reference to timer

template<TimeType microseconds>
CallbackTimer &initializeUs(TimerCallback callback, void *arg = nullptr)

Initialise timer in microseconds (static check) with Timer Callback and optional argument.

template<TimeType microseconds>
CallbackTimer &initializeUs(InterruptCallback callback = nullptr)

Initialise timer in microseconds (static check) with optional Interrupt Callback (no argument)

CallbackTimer &initializeUs(TimeType microseconds, TimerCallback callback, void *arg = nullptr)

Initialise timer in microseconds with Timer Callback and optional argument.

CallbackTimer &initializeUs(TimeType microseconds, InterruptCallback callback = nullptr)

Initialise timer in microseconds with optional Interrupt Callback (no arg)

template<uint32_t milliseconds>
CallbackTimer &initializeMs(TimerCallback callback, void *arg = nullptr)

Initialise hardware timer in milliseconds (static check) with Timer Callback and optional argument.

template<uint32_t milliseconds>
CallbackTimer &initializeMs(InterruptCallback callback = nullptr)

Initialise hardware timer in milliseconds (static check) and optional Interrupt Callback (no arg)

CallbackTimer &initializeMs(uint32_t milliseconds, TimerCallback callback, void *arg = nullptr)

Initialise hardware timer in milliseconds with Timer Callback and optional argument.

CallbackTimer &initializeMs(uint32_t milliseconds, InterruptCallback callback = nullptr)

Initialise hardware timer in milliseconds with optional Interrupt Callback (no arg)

bool start(bool repeating = true)

Start timer running.

Parameters
  • repeating: True to restart timer when it triggers, false for one-shot (Default: true)

Return Value
  • bool: True if timer started

bool startOnce()

Start one-shot timer.

Note

Timer starts and will run for configured period then stop

Return Value
  • bool: True if timer started

void stop()

Stops timer.

bool restart()

Restart timer.

Note

Timer is stopped then started with current configuration

Return Value
  • bool: True if timer started

bool isStarted() const

Check if timer is started.

Return Value
  • bool: True if started

NanoTime::Time<TimeType> getIntervalUs() const

Get timer interval in microseconds.

NanoTime::Time<uint32_t> getIntervalMs() const

Get timer interval in milliseconds.

TickType getInterval() const

Get timer interval in clock ticks.

bool checkInterval(TickType ticks) const

Check timer interval is valid.

Parameters
  • ticks: Interval to check

Return Value
  • bool: true if interval is within acceptable range for this timer

bool setInterval(TickType ticks)

Set timer interval in timer ticks.

Parameters
  • ticks: Interval in timer ticks

template<TimeType ticks>
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>
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>
bool setInterval(TimeType time)

Set timer interval in timer ticks.

Template Parameters
  • unit:

Parameters
  • time: Interval in given units

bool setIntervalUs(TimeType microseconds)

Set timer interval in microseconds.

template<TimeType microseconds>
void setIntervalUs()

Set timer interval in microseconds (static check)

bool setIntervalMs(uint32_t milliseconds)

Set timer interval in milliseconds.

template<uint32_t milliseconds>
void setIntervalMs()

Set timer interval in milliseconds (static check)

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

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 constexpr Millis millis()

Get a millisecond time source.

static constexpr Micros micros()

Get a microsecond time source.

template<uint64_t us>
static constexpr uint64_t usToTicks()

Convert microsecond count into timer ticks.

static TickType usToTicks(TimeType time)

Convert microsecond count into timer ticks.

template<uint64_t ticks>
static constexpr uint64_t ticksToUs()

Convert timer ticks into microseconds.

static TimeType ticksToUs(TickType ticks)

Convert timer ticks into microseconds.

template<uint64_t ticks>
static constexpr void checkInterval()

Check timer interval in ticks is valid (static check)

Note

On error, compilation fails with error message

Template Parameters
  • ticks: Timer interval to check

template<NanoTime::Unit unit, uint64_t time>
static 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 constexpr void checkIntervalMs()

Check timer interval in milliseconds is valid (static check)

template<uint64_t microseconds>
static 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

using OneShotElapseTimer = PolledTimer::OneShot<PolledTimerClock, 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
using OneShotCpuCycleTimer = PolledTimer::OneShot<CpuCycleClockNormal, units>
using PeriodicCpuCycleTimer = PolledTimer::Periodic<CpuCycleClockNormal, units>
using OneShotCpuCycleTimerFast = PolledTimer::OneShot<CpuCycleClockFast, 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.

namespace PolledTimer

Typedefs

using OneShot = Timer<Clock, unit, false, uint32_t>
using Periodic = Timer<Clock, unit, true, uint32_t>
template<class Clock, NanoTime::Unit unit_, bool IsPeriodic, typename TimeType>
class Timer : public NanoTime::TimeSource<Clock, unit_, TimeType>
#include <PolledTimer.h>

Template class to implement a polled timer.

If the interval is set to 0, the timer will expire immediately, and remain so:

canWait() will return false. Call cancel() to prevent the timer from ever expiring: canExpire() will return false. Call start() to set the timer going with the previously set interval. Call reset() to set the timer with a new interval.
Note

Intervals and expiry use ‘tick’ values which are very efficient, whereas ‘time’ values must be computed so are very slow (~100+ cycles for uint32_t, considerably more for uint64_t). The class keeps a note of a ‘start’ time (in ticks) used to determine the elapsed() return value. An ‘interval’ value may be given is specified, then the expired() method returns true.

Template Parameters
  • Clock: Clock source

  • unit: Time unit for tick/time conversions

  • IsPeriodic:

  • TimeType: Variable type to use (uint32_t or uint64_t)

A periodic timer will automatically restart when expired. A one-shot timer will remain expired until reset.

Public Functions

Timer(const TimeType &timeInterval = 0)

Create a Timer with optional expiry time.

Parameters
  • timeInterval: Relative time until expiry

void start()

Start the timer.

template<uint64_t timeInterval>
void reset()

Start the timer with a new expiry interval.

Template Parameters
  • timeInterval: Time to expire after last call to start()

template<uint64_t timeInterval>
constexpr uint32_t checkTime()

Check the given time interval is valid and return the corresponding tick count.

Note

If time interval is invalid fails compilation

Template Parameters
  • timeInterval:

Return Value
  • uint32_t:

bool reset(const TimeType &timeInterval)

Start the timer with a new expiry interval.

See

See resetTicks()

Parameters
  • timeInterval: Time to expire after last call to start()

Return Value
  • bool: true on success, false on failure

bool resetTicks(const TimeType &interval)

Start the timer with a new expiry interval.

Note

If time interval is 0, timer will expire immediately, and if it exceeds the maximum interval the timer will never expire.

Parameters
  • interval: Clock ticks to expire after last call to start()

Return Value
  • bool: true on success, false if interval is out of range

void cancel()

Cancelling a timer means it will never expire.

TickType elapsedTicks() const

Get elapsed ticks since start() was last called.

NanoTime::Time<TimeType> elapsedTime() const

Get elapsed time since start() was last called.

bool expired()

Determine if timer has expired.

Return Value
  • bool:

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

Enums

enum Unit

Identify units for a scalar quantity of time.

Note

Ordered in increasing unit size, e.g. days > seconds

Values:

Nanoseconds
Microseconds
Milliseconds
Seconds
Minutes
Hours
Days
UnitMax = Days

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 Value
  • 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 Value
  • 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 Value
  • TimeType: Converted time, returns TimeType(-1) if calculation overflowed

Variables

constexpr BasicRatio32 NanoTime::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.

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.

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.

See

Use TimeSource to work with a Clock in specific time units

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 Frequency
#include <NanoTime.h>

Class to represent a frequency.

template<typename Clock_, typename T>
struct Ticks
#include <NanoTime.h>

Class to handle a tick value associated with a clock.

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<typename T>
struct Time
#include <NanoTime.h>

Class to handle a simple time value with associated unit.

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_, 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

struct TimeValue
#include <NanoTime.h>

A time time broken into its constituent elements.

Note

Useful for analysing and printing time values

template<Unit unit>
struct UnitTickRatio
#include <NanoTime.h>

Class template to define tick std::ratio type.

Note

This would be preferable: template <Unit unit> using UnitTickRatio = std::ratio<unitTicks[unit].num, unitTicks[unit].den>; But GCC 4.8 doesn’t like it (lvalue required as unary ‘&’ operand)

Template Parameters
  • unit:

Return Value
  • std::ratio: Ticks per second

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 awkard. 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 Value
  • 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 Value
  • 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
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 Value
  • char[]: In flash memory, access using flash functions

_F(str)

Declare and use a flash string inline.

Parameters
  • str:

Return Value
  • char[]: Returns a pointer to a stack-allocated buffer of the precise size required.

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.

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.

Example:

    void testfunc() {
        static DEFINE_PSTR(test, "This is a test string\n");
        m_printf(LOAD_PSTR(test));
    }

_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");
Note

Must not contain embedded NUL characters

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

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
  • S: Storage 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()

Construct empty set.

template<typename S2>
constexpr BitSet(const BitSet<S2, E> &bitset)

Copy constructor.

Parameters
  • bitset: The set to copy

constexpr BitSet(S value)

Construct from a raw set of bits.

Parameters
  • value: Integral type whose bits will be interpreted as set{E}

constexpr BitSet(E e)

Construct set containing a single value.

Parameters
  • e: Value to place in our new BitSet object

bool operator==(const BitSet &other) const

Compare this set with another for equality.

bool operator!=(const BitSet &other) const

Compare this set with another for inequality.

constexpr BitSet operator~() const

Obtain a set containing all elements not in this one.

BitSet &flip()

Flip all bits in the set.

BitSet &flip(E e)

Flip state of the given bit.

size_t count() const

Get the number of elements in the set, i.e. bits set to 1.

BitSet &operator+=(const BitSet &rhs)

Union: Add elements to set.

BitSet &operator-=(const BitSet &rhs)

Remove elements from set.

template<>
BitSet &operator&=(const BitSet &rhs)

Intersection: Leave only elements common to both sets.

BitSet &operator|=(const BitSet &rhs)

Union: Add elements to set.

BitSet &operator^=(const BitSet &rhs)

XOR - toggle state of bits using another set.

bool test(E e) const

Test to see if given element is in the set.

bool operator[](E e) const

Read-only [] operator.

Parameters
  • e: Element to test for

Return Value
  • bool: true if given element is in the set

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 Value
  • BitRef: Temporary object used to do the read or write

bool any() const

Determine if set contains any values.

bool any(const BitSet &other) const

Determine if set contains any values from another set i.e. intersection != [].

bool all() const

Test if set contains all possible values.

bool none() const

Test if set is empty.

BitSet &set()

Add all possible values to the bit set.

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

BitSet &reset()

Remove all values from the set.

BitSet &reset(E e)

Clear the state of the given bit (i.e. remove it from the set)

bool operator==(E e) const

Determine if set consists of only the one given element.

constexpr operator S() const

Allow casts from the native storage type to get a numeric result for this set.

constexpr S value() const

Get stored bits for this bitset.

Public Static Functions

static constexpr size_t size()

Get the number of possible elements in the set.

static constexpr BitSet domain()

Get the set of all possible values.

static constexpr S bitVal(E e)

Get the bitmask corresponding to a given value.

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 alllocation 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");
}
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>
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 Value
  • bool: false on memory allocation error

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 Value
  • 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
  • str: String to find

  • ignoreCase: Whether search is case-sensitive or not

Return Value
  • int: index of given string, -1 if not found

int indexOf(const String &str, bool ignoreCase = true) const

Find the given string and return its index.

Note

Comparison is not case-sensitive

Parameters
  • str: String to find

  • ignoreCase: Whether search is case-sensitive or not

Return Value
  • int: index of given string, -1 if not found

bool contains(const char *str, bool ignoreCase = true) const

Check if array contains a string.

Note

Search is not case-sensitive

Parameters
  • str: String to search for

  • ignoreCase: Whether search is case-sensitive or not

Return Value
  • bool: True if string exists in array

bool contains(const String &str, bool ignoreCase = true) const

Check if array contains a string.

Note

Search is not case-sensitive

Parameters
  • str: String to search for

  • ignoreCase: Whether search is case-sensitive or not

Return Value
  • 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 Value
  • const: char* nullptr if index is not valid

const char *operator[](unsigned index) const

Get string at the given position.

Parameters
  • index: 0-based index of string to obtain

Return Value
  • const: char* nullptr if index is not valid

void clear()

Empty the array.

unsigned count() const

Get quantity of strings in array.

Return Value
  • unsigned: Quantity of strings

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.

API Documentation
IDataSourceStream
class IDataSourceStream : public Stream

Base class for read-only stream.

Subclassed by FSTR::Stream, IFS::FWFS::ArchiveStream, MultiStream, ReadWriteStream, SectionStream, StreamTransformer, TemplateStream, UrlencodedOutputStream

Public Functions

virtual StreamType getStreamType() const

Get the stream type.

Return Value
  • StreamType: The stream type.

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 Value
  • bool: true if valid, false if invalid

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.

virtual uint16_t readMemoryBlock(char *data, int bufSize) = 0

Read a block of memory.

Parameters
  • data: Pointer to the data to be read

  • bufSize: Quantity of chars to read

Return Value
  • uint16_t: Quantity of chars read

int read()

Read one character and moves the stream pointer.

Return Value
  • The: character that was read or -1 if none is available

int peek()

Read a character without advancing the stream pointer.

Return Value
  • int: The character that was read or -1 if none is available

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 Value
  • New: position, < 0 on error

virtual bool seek(int len)

Move read cursor.

Parameters
  • len: Relative cursor adjustment

Return Value
  • bool: True on success.

virtual bool isFinished() = 0

Check if all data has been read.

Return Value
  • bool: True on success.

virtual int available()

Return the total length of the stream.

Return Value
  • int: -1 is returned when the size cannot be determined

size_t write(uint8_t charToWrite)

From Stream class: We don’t write using this stream.

Parameters
  • charToWrite:

int length()

Return the total length of the stream.

Return Value
  • int: -1 is returned when the size cannot be determined

virtual String id() const

Returns unique id of the resource.

Return Value
  • String: the unique id of the stream.

virtual String getName() const

Returns name of the resource.

Note

Commonly used to obtain name of file

Return Value

virtual MimeType getMimeType() const

Get MIME type for stream content.

Return Value
  • MimeType:

String readString(size_t maxLen)

Overrides Stream method for more efficient reading.

Note

Stream position is updated by this call

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.
Parameters
  • s: String object to move data into

Return Value
  • bool: true on success, false if there’s a problem.

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.

enum SeekOrigin

Stream/file seek origins.

Values:

Start = 0

SEEK_SET: Start of file.

Current = 1

SEEK_CUR: Current position in file.

End = 2

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, OtaUpgrade::BasicStream, Storage::PartitionStream

Public Functions

size_t write(uint8_t charToWrite)

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 Value
  • 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 Value
  • size_t: Quantity of chars actually written, may be less than requested

Stream Classes
group stream

Data stream classes.

Unnamed Group

using StreamTransformerCallback = Delegate<size_t(const uint8_t *in, size_t inLength, uint8_t *out, size_t outLength)>

Class that can be used to transform streams of data on the fly.

Callback specification for the stream transformers

See

See StreamTransformer::transform() method for details

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 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 IDataSourceStream : public Stream
#include <DataSourceStream.h>

Base class for read-only stream.

Subclassed by FSTR::Stream, IFS::FWFS::ArchiveStream, MultiStream, ReadWriteStream, SectionStream, StreamTransformer, TemplateStream, UrlencodedOutputStream

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 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 IFS::FWFS::ObjectBuffer, JsonObjectStream

class MultipartStream : public MultiStream
#include <MultipartStream.h>

Read-only stream for creating HTTP multi-part content.

See

See https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html

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 QuotedPrintableOutputStream : public StreamTransformer
#include <QuotedPrintableOutputStream.h>

Read-only stream that transforms bytes of data into quoted printable data stream.

class ReadWriteStream : public IDataSourceStream
#include <ReadWriteStream.h>

Base class for read/write stream.

Subclassed by CircularBuffer, EndlessMemoryStream, HardwareSerial, IFS::FileStream, LimitedMemoryStream, MemoryDataStream, OtaUpgrade::BasicStream, Storage::PartitionStream

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.

Subclassed by FSTR::TemplateStream, SectionTemplate, TemplateFileStream

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

Directory stream class.

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 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.

Platform Support

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 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.

See

WiFi Access Point

See

WiFi Events Interface

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.

Parameters
  • event:

  • info:

Return Value
  • bool: return true to perform default configuration

using WPSConfigDelegate = Delegate<bool(WpsStatus status)>

WPS configuration callback function.

Parameters
  • status:

Return Value
  • bool: return true to perform default configuration

Enums

enum StationConnectionStatus

WiFi station connection states.

Values:

eSCS_Idle

Connection idle.

eSCS_Connecting

Connecting.

eSCS_WrongPassword

Wrong password.

eSCS_AccessPointNotFound

AP not found.

eSCS_ConnectionFailed

Connection failed.

eSCS_GotIP

Got IP address.

enum SmartConfigType

Smart configuration type.

Values:

SCT_None = -1
SCT_EspTouch

ESP Touch.

SCT_AirKiss

Air Kiss.

SCT_EspTouch_AirKiss

ESP Touch and Air Kiss.

enum SmartConfigEvent

Smart configuration event.

Values:

SCE_Wait

Wait.

SCE_FindChannel

Find channel.

SCE_GettingSsid

Getting SSID & password.

SCE_Link

Link established.

SCE_LinkOver

Link-over.

enum WpsStatus

WiFi WPS callback status.

Values:

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 Value
  • bool: True if WiFi station enabled

virtual bool config(const String &ssid, const String &password, bool autoConnectOnStartup = true, bool save = true) = 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)

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 Value
  • bool: True if connected.

bool isConnectionFailed() const

Get WiFi station connection failure status.

Return Value
  • bool: True if connection failed

virtual StationConnectionStatus getConnectionStatus() const = 0

Get WiFi station connection status.

Return Value
  • StationConnectionStatus: Connection status structure

String getConnectionStatusName() const

Get WiFi station connection status name.

Return Value

virtual bool isEnabledDHCP() const = 0

Get WiFi station DHCP enabled status.

Return Value
  • 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 Value
  • WiFi: station DHCP hostname

virtual IpAddress getIP() const = 0

Get WiFi station IP address.

Return Value
  • IpAddress: IP address of WiFi station

virtual MacAddress getMacAddress() const = 0

Get WiFi station MAC address.

Return Value
  • MacAddress:

String getMAC(char sep = '0') const

Get WiFi station MAC address.

Parameters
  • sep: Optional separator between bytes (e.g. ‘:’)

Return Value
  • 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:
Parameters
  • addr: The new MAC address

Return Value
  • bool: true on success

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”

virtual IpAddress getNetworkMask() const = 0

Get WiFi station network mask.

Return Value
  • IpAddress: WiFi station network mask

virtual IpAddress getNetworkGateway() const = 0

Get WiFi station default gateway.

Return Value
  • IpAddress: WiFi station default gateway

virtual IpAddress getNetworkBroadcast() const = 0

GetWiFi station broadcast address.

Return Value
  • IpAddress: WiFi station broadcast address

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 Value
  • bool: true if address is local

bool setIP(IpAddress address)

Set WiFi station IP address.

Parameters
  • address: IP address

Return Value
  • 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 gatway

Return Value
  • bool: True on success

virtual String getSSID() const = 0

Get WiFi station SSID.

Return Value

virtual String getPassword() const = 0

Get WiFi station password.

Return Value
  • String: WiFi station password

virtual int8_t getRssi() const = 0

Get WiFi signal strength.

Return Value
  • int8_t: Value in dBm

virtual uint8_t getChannel() const = 0

Get active WiFi channel.

Return Value
  • uint8_t: channel number

virtual bool startScan(ScanCompletedDelegate scanCompleted) = 0

Start WiFi station network scan.

Parameters
  • scanCompleted: Function to call when scan completes

Return Value
  • bool: True on success

WiFi Access Point
group wifi_ap

Control and monitoring of WiFi access point interface.

Note

The WiFi access point interface provides a WiFi network access point. Control of WiFi AP including WiFi SSID and password and IP address.

See

WiFi Station Interface

See

WiFi Events Interface

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 Value
  • 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 Value
  • bool: True on success

virtual IpAddress getIP() const = 0

Get WiFi AP IP address.

Return Value
  • IpAddress: WiFi AP IP address

virtual bool setIP(IpAddress address) = 0

Set WiFi AP IP addres.

Parameters
  • address: New IP address for WiFi AP

Return Value
  • bool: True on success

virtual MacAddress getMacAddress() const = 0

Get WiFi AP MAC address.

Return Value
  • MacAddress:

String getMAC(char sep = '0') const

Get WiFi AP MAC address.

Parameters
  • sep: separator between bytes (e.g. ‘:’)

Return Value

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:
Parameters
  • addr: The new MAC address

Return Value
  • bool: true on success

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”

virtual IpAddress getNetworkMask() const = 0

Get WiFi AP network mask.

Return Value
  • IpAddress: WiFi AP network mask

virtual IpAddress getNetworkGateway() const = 0

Get WiFi AP default gateway.

Return Value
  • IpAddress: WiFi AP default gateway

virtual IpAddress getNetworkBroadcast() const = 0

Get WiFi AP broadcast address.

Return Value
  • IpAddress: WiFi AP broadcast address

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 Value
  • bool: true if address is local

virtual String getSSID() const = 0

Get WiFi access point SSID.

Return Value
  • String: WiFi access point SSID

virtual String getPassword() const = 0

Get WiFi access point password.

Return Value
  • String: WiFi access point password

WiFi Events
group wifi_ev

Event callback interface for WiFi events.

See

WiFi Station Interface

See

WiFi Access Point

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.

Parameters
  • ssid:

  • bssid:

  • 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.

Parameters
  • ssid: SSID from which we’ve disconnected

  • bssid:

  • 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.

Parameters
  • oldMode:

  • 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.

Parameters
  • ip:

  • netmask:

  • 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.

Parameters
  • mac: MAC address of the station

  • 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.

Parameters
  • mac: MAC address of the station

  • 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 informaton about our Access Point, such as which authentication modes are supported.

Parameters
  • rssi: Signal strength

  • mac:

Enums

enum WifiDisconnectReason

Reason codes for WiFi station disconnection.

See

WIFI_DISCONNECT_REASON_CODES_MAP

Values:

Variables

WifiEventsClass &WifiEvents

Global reference to architecture-specific implementation.

class WifiEventsClass
#include <WifiEvents.h>

WiFi events class.

Public Functions

void onStationConnect(StationConnectDelegate delegateFunction)

Set callback for ‘station connected’ event.

void onStationDisconnect(StationDisconnectDelegate delegateFunction)

Set callback for ‘station disconnected’ event.

void onStationAuthModeChange(StationAuthModeChangeDelegate delegateFunction)

Set callback for ‘station authorisation mode change’ event.

void onStationGotIP(StationGotIPDelegate delegateFunction)

Set callback for ‘station connected with IP address’ event.

void onAccessPointConnect(AccessPointConnectDelegate delegateFunction)

Set callback for ‘access point client connected’ event.

void onAccessPointDisconnect(AccessPointDisconnectDelegate delegateFunction)

Set callback for ‘access point client disconnected’ event.

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 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.

class BeaconInfoList : public Vector<BeaconInfo>
#include <WifiSniffer.h>

For applications to use to manage list of unique beacons.

class ClientInfoList : public Vector<ClientInfo>
#include <WifiSniffer.h>

For applications to use to manage list of unique clients.

class WifiSniffer : public ISystemReadyHandler
#include <WifiSniffer.h>

Public Functions

void begin()

Initialise the sniffer.

void end()

Stop the sniffer.

void onBeacon(WifiBeaconCallback callback)

Register notification for beacon (AP) info.

void onClient(WifiClientCallback callback)

Register notification for client info.

void onSniff(WifiSnifferCallback callback)

Register notification for all incoming data.

Note

Callback invoked for all packet types, including beacon/client

void setChannel(unsigned channel)

Set the channel to listen on.

Parameters
  • channel:

unsigned getChannel()

Get the current channel being listened on.

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

CPU Frequency.

Values:

eCF_80MHz = 80

CPU 80MHz.

eCF_160MHz = 160

CPU 160MHz.

eCF_240MHz = 240

CPU 240MHz.

enum DeepSleepOptions

Deep sleep options.

Values:

eDSO_RF_CAL_BY_INIT_DATA = 0

RF_CAL or not after deep-sleep wake up, depends on init data byte 108.

eDSO_RF_CAL_ALWAYS = 1

RF_CAL after deep-sleep wake up, there will be large current.

eDSO_RF_CAL_NEVER = 2

no RF_CAL after deep-sleep wake up, there will only be small current.

eDSO_DISABLE_RF = 4

disable RF after deep-sleep wake up, just like modem sleep, there will be the smallest current.

enum SystemState

System state.

Values:

eSS_None

System state unknown.

eSS_Intializing

System initialising.

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

bool isReady()

Check if system ready.

Return Value
  • 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

void setCpuFrequency(CpuFrequency freq)

Set the CPU frequency.

Parameters
  • freq: Frequency to set CPU

CpuFrequency getCpuFrequency()

Get the CPU frequency.

Return Value
  • CpuFrequency: The frequency of the CPU

bool deepSleep(uint32_t timeMilliseconds, DeepSleepOptions options = eDSO_RF_CAL_BY_INIT_DATA)

Enter deep sleep mode.

Parameters
  • timeMilliseconds: Quantity of milliseconds to remain in deep sleep mode

  • options: Deep sleep options

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

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 Value
  • 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 Value
  • bool: false if callback could not be queued

static bool queueCallback(TaskCallback callback, void *param = nullptr)

Queue a deferred callback, with optional void* parameter.

static 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 Value
  • bool: false if callback could not be queued

static unsigned getTaskCount()

Get number of tasks currently on queue.

Return Value
  • unsigned:

static 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 Value
  • unsigned:

Services

Command Executor
Introduction

Command handler provides a common command line interface. CLI is available for the following remote access methods:

  • Serial

  • Telnet

  • Websockets

By default, CLI is disabled. Enable CLI by calling “commandProcessing” on the appropriate access class object, e.g:

Serial.commandProcessing(true)

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 character may be defined. An automatic “help” display is available.

Build Variables
ENABLE_CMD_EXECUTOR

Default: 1 (ON)

This feature enables execution of certain commands by registering token handlers for text received via serial, websocket or telnet connection. If this feature is not used additional RAM/Flash can be obtained by setting ENABLE_CMD_EXECUTOR=0. This will save ~1KB RAM and ~3KB of flash memory.

API Documentation
enum commandhandler::VerboseMode

Verbose mode.

Values:

VERBOSE

Verbose mode.

SILENT

Silent mode.

using CommandFunctionDelegate = Delegate<void(String commandLine, CommandOutput *commandOutput)>

Command delegate function.

Note

CommandFunctionDelegate defines the structure of a function that handles individual commands

Note

Can use standard print functions on commandOutput

Parameters
  • commandLine: Command line entered by user at CLI, including command and parameters

  • commandOutput: Pointer to the CLI print stream

typedef CommandFunctionDelegate commandFunctionDelegate

CommandHandler commandHandler

Global instance of CommandHandler.

class CommandDelegate
#include <CommandDelegate.h>

Command delegate class.

Public Functions

CommandDelegate(String reqName, String reqHelp, String reqGroup, CommandFunctionDelegate reqFunction)

Instantiate a command delegate

Parameters
  • reqName: Command name - the text a user types to invoke the command

  • reqHelp: Help message shown by CLI “help” command

  • reqGroup: The command group to which this command belongs

  • reqFunction: Delegate that should be invoked (triggered) when the command is entered by a user

Public Members

String commandName

Command name.

String commandHelp

Command help.

String commandGroup

Command group.

CommandFunctionDelegate commandFunction

Command Delegate (function that is called when command is invoked)

class CommandHandler
#include <CommandHandler.h>

Command handler class.

Public Functions

CommandHandler()

Instantiate a CommandHandler.

bool registerCommand(CommandDelegate reqDelegate)

Add a new command to the command handler.

Note

If command already exists, it will not be replaced and function will fail. Call unregisterCommand first if you want to replace a command.

Parameters
  • reqDelegate: Command delegate to register

Return Value
  • bool: True on success

bool unregisterCommand(CommandDelegate reqDelegate)

Remove a command from the command handler.

reqDelegate Delegate to remove from command handler

void registerSystemCommands()

Register default system commands.

Note

Adds the following system commmands to the command handler

  • status

  • echo

  • help

  • debugon

  • debugoff

  • command

CommandDelegate getCommandDelegate(const String &commandString)

Get the command delegate for a command.

Parameters
  • commandString: Command to query

Return Value

VerboseMode getVerboseMode()

Get the verbose mode.

Return Value
  • VerboseMode: Verbose mode

void setVerboseMode(VerboseMode reqVerboseMode)

Set the verbose mode.

Parameters
  • reqVerboseMode: Verbose mode to set

String getCommandPrompt()

Get the command line prompt.

Note

This is what is shown on the command line before user input Default is Sming>

Return Value
  • String: The command line prompt

void setCommandPrompt(const String &reqPrompt)

Set the command line prompt.

Note

This is what is shown on the command line before user input Default is Sming>

Parameters
  • reqPrompt: The command line prompt

char getCommandEOL()

Get the end of line character.

Note

Only supports one EOL, unlike Windows

Return Value
  • char: The EOL character

void setCommandEOL(char reqEOL)

Set the end of line character.

Note

Only supports one EOL, unlike Windows

Parameters
  • reqEOL: The EOL character

String getCommandWelcomeMessage()

Get the welcome message.

Note

Only if verbose mode is enabled

Return Value
  • String: The welcome message that is shown when clients connect

void setCommandWelcomeMessage(const String &reqWelcomeMessage)

Set the welcome message.

Note

Only if verbose mode is enabled

Parameters
  • reqWelcomeMessage: The welcome message that is shown when clients connect

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 RingTonePlayer 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

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

void reset()

Reset counters to start a new update period.

unsigned getLoopIterations()

Get the number of task callbacks made so far.

uint32_t getElapsedCycles()

Get the total number of CPU cycles since the last call to reset()

uint32_t getMinLoopCycles()

Get the figure used as the baseline cycle count.

unsigned getUtilisation()

Get the CPU utilisation figure in 1/100ths of a percent.

Min-Max
template<typename T>
class MinMax : public Printable

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

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.

HashMap
template<typename K, typename V>
class HashMap

HashMap class template.

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 unsucessful.

Return Value
  • 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.

Return Value
  • 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.

Return Value
  • bool: Returns true if strings are identical

Equality operator ==

Return Value
  • bool: true if Strings are identical

In-equality operator !=

Return Value
  • bool: Returns true if strings are not identical

Test for equality, without case-sensitivity

null strings are treated as empty.

Return Value
  • 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.
Return Value
  • 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.
Return Value
  • 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).

Parameters
  • from: Index of first character to retrieve

  • 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.

Return Value
  • 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

Parameters
  • index: Index of the first character to remove

  • count: Number of characters to remove

Public Functions

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 Value
  • 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 Value
  • true: on success, false on failure

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 Value
  • 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 Value

operator StringIfHelperType() const

Provides safe bool() operator.

Evaluates as false if String is null, otherwise evaluates as true

bool startsWith(const String &prefix) const

Compare the start of a String Comparison is case-sensitive, must match exactly.

Parameters
  • prefix:

Return Value
  • 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 Value
  • bool: true on match

bool endsWith(char suffix) const

Compare the end of a String.

Parameters
  • suffix:

Return Value
  • bool: true on match

bool endsWith(const String &suffix) const

Compare the end of a String.

Parameters
  • suffix:

Return Value
  • bool: true on match

char charAt(size_t index) const

Obtain the character at the given index.

Note

If index is invalid, returns NUL \0

Parameters
  • index:

Return Value
  • 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 Value
  • unsigned: number of bytes copied, excluding nul terminator

void toCharArray(char *buf, size_t bufsize, size_t index = 0) const

Read contents of String into a buffer.

See

See getBytes()

const char *c_str() const

Get a constant (un-modifiable) pointer to String content.

Return Value
  • const: char* Always valid, even for a null string

char *begin()

Get a modifiable pointer to String content.

Note

If String is NUL, returns nullptr.

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(void)

Remove all leading and trailing whitespace characters from the String.

Public Static Attributes

const String nullstr

A null string evaluates to false.

const String empty

An empty string evaluates to true.

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.

Sample Projects

Sming comes with a set of samples to illustrate and test various aspects of the framework. Some are related to specific Libraries.

MMA7455 Accelerometer

Simple demonstration which reads and displays output from an I2C accelerometer.

References

Arducam

A demonstration application for controlling ArduCAM camera modules via web or telnet interface.

References

Basic APA102

Demonstrates use of APA102 library for controlling smart LEDs via SPI.

References

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

Basic Audio

Demonstrates how to set up I2S driver to produce audio output.

References

Basic Capsense

Simple demonstration showing raw output from a capacitive sensor.

References

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

Basic Delegates

Demonstrates the various ways you can use callbacks within Sming.

References

Basic Hardware PWM

Demonstrates how to generate PWM signals on multiple pins using Sming’s HardwarePWM class.

References

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 Filesytem) 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.

References
Environment Variables
  • ENABLE_FLASHSTRING_IMAGE

Basic Interrupts

Simple example of how interrupts can be used within Sming.

References

Basic NFC

Sample application to read an RFID tag using a suitable reader connected via SPI interface.

References

Basic Neopixel

Demonstration of the Adafruit Neopixel library for controlling single-wire-based LED pixels and strip.

References

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 fileystem, 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 acheive this!

Configuration
RBOOT_TWO_ROMS

Default: 1 (enabled)

Allows specifying two different URLs for ROM0 and ROM1.

If not set then only the URL defined in ROM_0_URL will be used.

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

Basic ProgMem

This sample application demonstrates the various ways to access data stored in Flash memory.

References

Basic I2C Scanner

Classic Arduino I2C scanner with adaptations for Sming framework.

References

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

Basic Servo

Demonstrates use of the Servo library for controlling multiple servo actuators.

References

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

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

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

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

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

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

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 provides this by default, but if you have made changes for your project then remove everything except your changes, but be sure to add #include_next <user_config.h> at the top.

  3. Remove any #include <user_config.h> statements from your files. This file is automatically included by Sming.

  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

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

CANBUS

Demonstrates communication with MCP2515-compatible devices via SPI interface.

References

CommandProcessing Debug

Demonstrates Sming’s command handling capability via HTTP, FTP and Telnet interfaces.

References

HMC5883L Compass

HMC5883L sensor reader.

_images/hmc5883l.jpg
References

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

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

TM1637 Display

Demonstrates use of 7-segment LED displays using TM1637 controller. This only requires 2 GPIO connections.

References

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

DNS Captive Portal

Demonstrates how to use Sming to create a default landing page when configured as an Access Point.

References

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

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

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

HTTP Client

See Basic SSL for information on compiling and configuring SSL.

References

HttpClient Instapush

Instapush is an event notification service created in 2014.

For details, see https://pushbots.com/.

References

ThingSpeak Http Client

Example of a HttpClient pushing data to ThingSpeak .

_images/thing-speak.png
References

AJAX Http Server

Demonstration of a simple web server with page updates using AJAX.

References

Bootstrap Http Server

Embedded web server.

This sample will download all requried files from a remote server.

_images/esp8266-web-server.png
References

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

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

HttpServer Websockets

Simple example of how to support persistent websocket connections in a server.

References

AM2321 Humidity Sensor

Example application demonstrating the use of the AM2321 Temperature/Humidity Sensor library.

References

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

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

IR Library

Example application demonstrating how to receive IR remote signals using the ESP8266.

References

WS2812 LEDs

Example application to demonstrate control of WS2812 RGB pixel-strip LEDs.

References

Yeelight LED bulbs

Demonstrates how to control Yeelight WiFi devices.

References

BH1750 Light Sensor

ESP8266 BH1750 sensor reader.

_images/bh1750.jpg
References

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

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

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

MeteoControl MQTT

Similar to MeteoControl sample without LCD interface and instead controlled via MQTT.

References

MQTT Client Hello

Simple MQTT client example.

References

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

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

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

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

RCSwitch Library

Demonstration of how to control wireless (radio-controlled) devices such as switched mains sockets, TVs, etc.

References

nRF24L01 Radio

Example application showing how to interface with the popular nRF24L01 2.4GHz radio transceivers.

References

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

SD Card

SDCard/FAT file usage and write benchmark.

References
Used by

5110 LCD Screen

Demonstration of how to interface to Monochrome Nokia 5110 LCD Displays.

References

SSD1306 OLED Screen

Example of direct work with SSD1306 OLED screen on ESP8266.

Minimal requirements: ESP-03

_images/ssd1306.jpg
References

ILI9163C TFT Screen

Example of direct work with ILI9163C 1.44” TFT screen on ESP8266.

Minimal requirements: ESP-03

_images/ili9163c.jpg
References

ILI9340 and ILI9341 TFT Screens

Demonstration of how to interface with displays such as the Adafruit 2.8” Touch Shield V2 (SPI).

References

ST7735 TFT Screen

Demonstration of how to interface with various SPI displays using the Adafruit ST7735 library.

References

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

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

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

Telnet Server

Demonstrates a simple Telnet server application.

References

DS1820 Temperature Sensor

DS1820/DS18B20 sensor reader.

_images/ds1820.jpg
References

UDP Server Echo

Sets up a UDP server which echoes back incoming packets. By default listens on port 1234.

References

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

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

WebcamServer sample

A sample providing access to webcams via HTTP server.

References

Websocket Client

This is a simple demo of the WebsocketClient class.

The client tries to connect to echo.websocket.org. It sents 10 messages then client connection is closed. It reconnects and sends 25 messages and continues doing same.

This demo shows connecton, closing and reconnection methods of WebsocketClient.

References

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

Hosted RCP 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

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.

WIFI_SSID

Default: PleaseEnterSSID

WIFI Access Point name. If you have enabled CONNECT_TO_WIFI then make sure to set also WIFI_SSID and WIFI_PWD values.

WIFI_PWD

Default: PleaseEnterPass

WIFI Access Point password. If you have enabled CONNECT_TO_WIFI then make sure to set also WIFI_SSID and WIFI_PWD values.

References
Environment Variables

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

CS5460 generic sample

The sample prints the measured RMS voltage each second (with CS5460 voltage and current filters enabled).

The default MISO pin (12) can be changed through the envvar:MISO variable. For example:

make MISO=13

These variables can be listed with:

make list-config
References

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

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

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

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

Basic MDNS

Demonstrates use of the MDNS library to discover local devices.

References

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

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. This way

  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
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_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 enumarating 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.

ENABLE_SSL

Default: unset (disable)

If set to 1 (highly recommended), OTA upgrade files will be trasnferred securely over TLS/SSL.

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 certficate.

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

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

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

Basic ControlPoint

Demonstrates use of UPnP library to create and use control points.

References

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

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

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

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

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

Adafruit BME280 Humidity/Pressure/Temperature sensor

This is a library for the Adafruit BME280 Humidity, Barometric Pressure + Temp sensor

Designed specifically to work with the Adafruit BME280 Breakout

These sensors use I2C or SPI to communicate, up to 4 pins are required to interface

Use of this library also requires Adafruit_Sensor to be installed on your local system.

Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!

Check out the links above for our tutorials and wiring diagrams

Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution

To download. click the DOWNLOAD ZIP button, rename the uncompressed folder Adafruit_BME280. Check that the Adafruit_BME280 folder contains Adafruit_BME280.cpp and Adafruit_BME280.h

Place the Adafruit_BME280 library folder your arduinosketchfolder/libraries/ folder. You may need to create the libraries subfolder if its your first library. Restart the IDE.

We also have a great tutorial on Arduino library installation at: http://learn.adafruit.com/adafruit-all-about-arduino-libraries-install-use

Compatibility

MCU

Tested Works

Doesn’t Work

Not Tested

Notes

Atmega328 @ 16MHz

X

Atmega328 @ 12MHz

X

Atmega32u4 @ 16MHz

X

Use SDA/SCL on pins D2 &amp; D3

Atmega32u4 @ 8MHz

X

Use SDA/SCL on pins D2 &amp; D3

ESP8266

X

I2C: just works, SPI: SDA/SCL default to pins 4 &amp; 5 but any two pins can be assigned as SDA/SCL using Wire.begin(SDA,SCL)

ESP32

X

I2C: just works, SPI: SDA/SCL default to pins 4 &amp; 5 but any two pins can be assigned as SDA/SCL using Wire.begin(SDA,SCL)

Atmega2560 @ 16MHz

X

Use SDA/SCL on pins 20 &amp; 21

ATSAM3X8E

X

Use SDA/SCL on pins 20 &amp; 21

ATSAM21D

X

ATtiny85 @ 16MHz

X

ATtiny85 @ 8MHz

X

Intel Curie @ 32MHz

X

STM32F2

X

  • ATmega328 @ 16MHz : Arduino UNO, Adafruit Pro Trinket 5V, Adafruit Metro 328, Adafruit Metro Mini

  • ATmega328 @ 12MHz : Adafruit Pro Trinket 3V

  • ATmega32u4 @ 16MHz : Arduino Leonardo, Arduino Micro, Arduino Yun, Teensy 2.0

  • ATmega32u4 @ 8MHz : Adafruit Flora, Bluefruit Micro

  • ESP8266 : Adafruit Huzzah

  • ATmega2560 @ 16MHz : Arduino Mega

  • ATSAM3X8E : Arduino Due

  • ATSAM21D : Arduino Zero, M0 Pro

  • ATtiny85 @ 16MHz : Adafruit Trinket 5V

  • ATtiny85 @ 8MHz : Adafruit Gemma, Arduino Gemma, Adafruit Trinket 3V

References

Adafruit GFX

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.

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 /Libraries/ folder. You may need to create the Libraries subfolder if its your first library. Restart the IDE.

Useful Resources
References
Used by

Adafruit ILI9341 Display

This is a Sming port of the library for the Adafruit ILI9341 display products.

This library works with the Adafruit 2.8” Touch Shield V2 (SPI) http://www.adafruit.com/products/1651

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

Configuration variables
TFT_CS_PIN

Pin to use for SPI CS (Chip Select). Active low.

TFT_DC_PIN

Pin for controlling DC (Data / Command) line

TFT_RESET_PIN

Pin for resetting the display. Optional.

References
Used by

Adafruit NeoPixel Library

Build Status

This library edited and adapted for Sming project by alonewolfx2 alonewolf07@gmail.com

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


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)

Check forks for other architectures not listed here!


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.)

References
Used by

Adafruit PCD8544 Display

This is a library for our Monochrome Nokia 5110 LCD Displays

Pick one up today in the adafruit shop! ——> http://www.adafruit.com/products/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 download. click the DOWNLOADS button in the top right corner, rename the uncompressed folder Adafruit_PCD8544. Check that the Adafruit_PCD8544 folder contains Adafruit_PCD8544.cpp and Adafruit_PCD8544.h

Place the Adafruit_PCD8544 library folder your /libraries/ folder. You may need to create the libraries subfolder if its your first library. Restart the IDE.

You will also have to download the Adafruit GFX Graphics core which does all the circles, text, rectangles, etc. You can get it from https://github.com/adafruit/Adafruit-GFX-Library and download/install that library as well

References
Used by

Adafruit SSD1306 Display

Introduction

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 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.
Scrolling code contributed by Michael Gregg 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_SSD1306. Check that the Adafruit_SSD1306 folder contains Adafruit_SSD1306.cpp and Adafruit_SSD1306.h

Place the Adafruit_SSD1306 library folder your /libraries/ folder. You may need to create the libraries subfolder if its your first library. Restart the IDE.

You will also have to download the Adafruit GFX Graphics core which does all the circles, text, rectangles, etc. You can get it from https://github.com/adafruit/Adafruit-GFX-Library and download/install that library as well

Compatibility

MCU

Tested Works

Doesn’t Work

Not Tested

Notes

Atmega328 @ 16MHz

X

Atmega328 @ 12MHz

X

Atmega32u4 @ 16MHz

X

Atmega32u4 @ 8MHz

X

ESP8266

X

change OLED_RESET to different pin if using default I2C pins D4/D5.

Atmega2560 @ 16MHz

X

ATSAM3X8E

X

ATSAM21D

X

ATtiny85 @ 16MHz

X

ATtiny85 @ 8MHz

X

Intel Curie @ 32MHz

X

STM32F2

X

  • ATmega328 @ 16MHz : Arduino UNO, Adafruit Pro Trinket 5V, Adafruit Metro 328, Adafruit Metro Mini

  • ATmega328 @ 12MHz : Adafruit Pro Trinket 3V

  • ATmega32u4 @ 16MHz : Arduino Leonardo, Arduino Micro, Arduino Yun, Teensy 2.0

  • ATmega32u4 @ 8MHz : Adafruit Flora, Bluefruit Micro

  • ESP8266 : Adafruit Huzzah

  • ATmega2560 @ 16MHz : Arduino Mega

  • ATSAM3X8E : Arduino Due

  • ATSAM21D : Arduino Zero, M0 Pro

  • ATtiny85 @ 16MHz : Adafruit Trinket 5V

  • ATtiny85 @ 8MHz : Adafruit Gemma, Arduino Gemma, Adafruit Trinket 3V

References
Used by

Adafruit ST7735 Display

This is a library for the Adafruit 1.8” SPI display. This library works with the Adafruit 1.8” TFT Breakout w/SD card

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

To download. click the DOWNLOADS button in the top right corner, rename the uncompressed folder Adafruit_ST7735. Check that the Adafruit_ST7735 folder contains Adafruit_ST7735.cpp and Adafruit_ST7735.

Place the Adafruit_ST7735 library folder your /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

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

Orientation

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

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

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 moduels.
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 shuld 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

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

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
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
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
  • JSON decoding (comments are supported)

  • JSON encoding (with optional indentation)

  • MessagePack

  • 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]}";

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;

JsonArray data = doc.createNestedArray("data");
data.add(48.756080);
data.add(2.302038);

serializeJson(doc, 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!

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

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

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

BMP180 Pressure/Temperature Sensor

Arduino library for BMP180 Bosch pressure/temperature sensor

Copyright (C) 2012 Love Electronics Ltd (loveelectronics.com)

References
Used by

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

CS5460 energy meter IC

The CS5460(A) is a highly integrated power measurement solution which combines two Analog-to-digital Converters (ADCs), high-speed power calculation functions, and a serial interface on a single chip. It is designed to accurately measure and calculate: Real (True) Energy, Instantaneous Power, I RMS , and V RMS for single phase 2- or 3-wire power metering applications.

References
Used by
Environment Variables
  • MISO

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

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

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

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 DIAL_Client sample application.

API Documentation
namespace Dial

Functions

bool discover(UPnP::ControlPoint &controlPoint, Client::Discovered callback)
class Client : public DeviceControl
#include <Client.h>

Public Functions

App &getApp(const String &applicationId)

Get application object by name.

Parameters
Return Value
  • App&: Application object reference

References
Used by

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

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

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 defintion.

References
Used by

HMC5883L Compass

https://github.com/jrowberg/i2cdevlib.git

References
Used by

HardwareSPI

Asynchronous SPI device stack for Sming.

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 busses 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;
      }

      /*
       *
       */
      onSelectDevice(selectDevice);

      /*
       * Initialise hardware Controller
       */
      auto cs = chipSelect >> 3;
      return HSPI::Controller::startDevice(dev, pinSet, cs);
   }

private:
   void IRAM_ATTR 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(getActivePinSet() == HSPI::PinSet::manual) {
         // Set CS output here
      }
   }

   uint8_t activeChipSelect{0};
};

The application should register a callback function via HSPI::onSelectDevice()

allows 8 (or more) SPI devices to share the same bus.

  • Bits 0-2 of the chipSelect value might be assigned to the GPIO output pins setting

  • the multiplexer address, with bits 3-7 storing the hardware CS setting.

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

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 data phase only. For 8-bit command and 24-bit address, this limits each transaction to 60 bytes.

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 HSPI::ClockMode

SPI clock polarity (CPOL) and phase (CPHA)

Values:

mode0 = 0x00

CPOL: 0 CPHA: 0.

mode1 = 0x01

CPOL: 0 CPHA: 1.

mode2 = 0x10

CPOL: 1 CPHA: 0.

mode3 = 0x11

CPOL: 1 CPHA: 1.

enum HSPI::IoMode

Mode of data transfer.

Values:

SPI

One bit per clock, MISO stage concurrent with MISO (full-duplex)

SPIHD

One bit per clock, MISO stage follows MOSI (half-duplex)

DUAL

Two bits per clock for Data, 1-bit for Command and Address.

DIO

Two bits per clock for Address and Data, 1-bit for Command.

SDI

Two bits per clock for Command, Address and Data.

QUAD

Four bits per clock for Data, 1-bit for Command and Address.

QIO

Four bits per clock for Address and Data, 1-bit for Command.

SQI

Four bits per clock for Command, Address and Data.

enum HSPI::PinSet

How SPI hardware pins are connected.

Values:

none

Disabled.

normal

Standard HSPI pins.

manual

HSPI pins with manual chip select.

overlap

Overlapped with SPI 0.

Warning

doxygenclass: Cannot find class “HSPI::Request” in doxygen xml output for project “api” from directory: ../api/xml/

Warning

doxygenclass: Cannot find class “HSPI::Data” in doxygen xml output for project “api” from directory: ../api/xml/

class Device

Manages a specific SPI device instance attached to a controller.

Subclassed by HSPI::MemoryDevice

Public Functions

bool isReady() const

Determine if the device is initialised.

Return Value
  • bool:

void setSpeed(uint32_t frequency)

Set maximum operating speed for device.

Parameters
  • speed: in Hz

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

Parameters
  • request:

  • address:

void prepareWrite(HSPI::Request &req, uint32_t address, const void *data, size_t len)

Parameters
  • request:

  • address:

  • data:

  • len:

Prepare a read request

virtual void prepareRead(HSPI::Request &req, uint32_t address) = 0

Parameters
  • req:

  • address:

void prepareRead(HSPI::Request &req, uint32_t address, void *buffer, size_t len)

Parameters
  • req:

  • address:

  • data:

  • len:

Public Functions

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:

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:

Warning

doxygenclass: Cannot find class “HSPI::SpiRam” in doxygen xml output for project “api” from directory: ../api/xml/

class Controller

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.
Parameters
  • chipSelect:

  • active: true when transaction is about to start, false when completed

Expanding the SPI bus using a HC138 3:8 multiplexer, for example, can also be handled here, setting the GPIO address lines appropriately.

Public Functions

void end()

Disable HSPI controller.

Note

Reverts HSPI pins to GPIO and disables the controller

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)

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.

uint32_t setSpeed(Device &dev, uint32_t frequency)

Set the clock for a given frequency.

Parameters
  • dev:

  • frequency: The requested clock frequency in Hz

Return Value
  • uint32_t: The actual clock frequency selected

struct Config

Public Members

bool dirty = {true}

Set when values require updating.

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

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.
Parameters
  • device: The device which has been updated

  • attr: A set of flags indicating which attributes were changed

Public Functions

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

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!

const Stats &getStats()

Get bridge statistics.

Return Value
  • const: Stats&

void resetStats()

Clear the bridge statistics.

const UserMap &getUsers() const

Access the list of users.

Return Value
  • const: UserMap&

void getStatusInfo(JsonObject json)

Get bridge status information in JSON format.

Parameters
  • json: Where to write information

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 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

Parameters
  • status: Result of the operation

  • 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 Value
  • Status:

virtual bool getAttribute(Attribute attr, unsigned &value) const = 0

Get the (cached) device attribute value.

Parameters
  • attr:

  • value:

Return Value
  • 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 Value
  • 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.

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 Value
  • Device*: nullptr if not found

virtual Device *find(const String &name)

Lookup device by name.

Note

With default implementation, enumerator position is updated

Return Value
  • 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 Hue::Status

Status of a setAttribute request.

Values:

success

The action was performed immediately without error.

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.

error

Action could not be completed.

If the Attribute not supported by your device, or an internal I/O error occured then return this value.

References
Used by

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

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

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 commerical projects or open-source prjects, without any additional condiitons.

For more information about the Nextion display project, please visit the wiki。
The wiki provdies 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 recommanded 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 shiped with source code can be open in your browser such as Chrome, Firefox or any one you like.

Suppported 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

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 Regiter.

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

LittleFS

Sming IFS integration of the LittleFS filesystem https://github.com/littlefs-project/littlefs.

References
Used by
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

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

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

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 appropiately.

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.

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

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 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 Type

Values:

XX
class A : public mDNS::Resource::Record
#include <Resource.h>

A’ record containing IP4 address

class AAAA : public mDNS::Resource::Record
#include <Resource.h>

AAAA’ record containing 128-bit IPv6 address

class HINFO : public mDNS::Resource::Record
#include <Resource.h>

HINFO’ record containing Host information

class PTR : public mDNS::Resource::Record
#include <Resource.h>

PTR’ record containing pointer to a canonical name

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 SRV : public mDNS::Resource::Record
#include <Resource.h>

SRVService Locator record

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.

References
Used by

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: paritial freeze; just fixes/typos or documentation updates; no extentions 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. Commiting 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

1(1,2)

Configurable, typically defined as RST_PIN in sketch/program.

2(1,2)

Configurable, typically defined as SS_PIN in sketch/program.

3

The SDA pin might be labeled SS on some/older MFRC522 boards.

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 tollerant, 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

MMA-7455 Accelerometer

Library for the MMA-7455 3-axis accelerometer

GIT repository

References
Used by

Mirf for NRF24L01

Arduino port of Mirf for the NRF24L01 modules

References

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
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

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

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.

void shutdown(HttpServerConnection &connection)

Takes care to cleanup the connection.

References
Used by
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

OneWire for Arduino

https://github.com/PaulStoffregen/OneWire.git

References
Used by

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 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

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 defered to BasicStream.

References
Used by
Environment Variables

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 Upgrade 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 enumarating 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.

References
Used by
Environment Variables

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 (altough 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

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 similiar 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

RapidXML

This is a port of https://github.com/dwd/rapidxml with minimal patch, and additional support code for Sming.

References
Used by

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(Type &obj);

Append an element to the buffer. Return true on success, 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 reenable 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

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.

References
Used by

SD Card

Low-level support code for accessing SD Cards using FATFS.

References
Used by

SI7020/SI7021 Environmental Sensors

Arduino library for SI7020 and SI7021 environmental sensors

Examples:

References
Used by

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 reponse
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?

References
Used by
Environment Variables
_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 peform 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-eeb75b7d-9dac-44fe-90be-18aaa6b228c9.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

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

1

Delton T. Horn, Build Your Own Low-Cost Signal Generator, McGraw-Hill/TAB Electronics, 1994, ISBN 0070304289

2

R.A. Penfold, How to Use Oscilloscopes and Other Test Equipment, Bernard Babani Publishing, 1989, ISBN 0859342123

3

Introduction to the Function Generator

4

Using a Basic Function Generator

References
Used by

SmingTest

An extensible test framework for Sming, with integrated profiling and scheduling support.

  • 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

  • Use the REGISTER_TEST macro to name the function

  • Add an entry to the modules.h file

  • 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 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 pending() before returning. When the tests have been completed call complete(). (See test-timers for an example.)

The following macros are added for ease of importing tests from other frameworks:

  • TEST_CASE()

  • REQUIRE()

What happens

The registerGroup<> function creates a factory function which is added to the groupFactories list.

The test runner creates, executes and destroys each group in turn, and deals with scheduling.

Getting started

A Sample test skeleton project is provided which you can copy to provide a starting point.

See the Sming HostTests test application for how to create your own test applications.

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.

References

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

References
Used by

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

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
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
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

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!, expecially 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 unfortunatly 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

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

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

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.

References
Used by

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.

References
Used by

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.

References
Used by
Environment Variables
  • UPNP_SCHEMA

UPnP

Universal Plug and Play.

Introduction

A C++ library for managing UPnP devices and services using the Sming framework.

If you’re not famililar with the mechanics of UPnP See UPnP for some background information.

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 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. You must therefore register all devices and services that you wish to control by calling UPnP::ControlPoint::registerClasses().

This method takes a UPnP::ClassGroup which typically defines all devices and services belonging to a specific domain. See UPnP Schema for further details about the available schema.

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 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 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 Wemo.h:

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
References
Used by

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

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 Compatable 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 Compatable 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 deffered
- (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

WS2812 Neopixel

http://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/

References
Used by

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

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

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
Submodule: src

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 intructions 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
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
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
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.

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

References

Used by

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.

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
make

Should already be available as part of Sming build system

doxygen & graphviz

For Linux:

sudo apt install doxygen graphviz-dev

For Windows, see:

GraphViz is used by Doxygen (and Sphinx) to render graphs from .dot files.

Python 3 + Sphinx

For Linux:

sudo apt install python3-sphinx python3-pip python3-setuptools python3-cairocffi

For Windows, see Sphinx Installation Guide.

Requirements

Install other required packages:

pip3 install -r requirements.txt
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 {todo}

  • Host builds a version of the library for native host debugging on Linux or Windows

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 (Windows)
   |_ .travis.yml                CI testing (Linux)
   |_ .appveyor/                 CI scripts (Windows)
   |_ .travis                    CI scripts (Linux)
   |_ 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
      |_ ...

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 re-use:

  • 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 iamge 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.

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.

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.

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 constrast 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 documention 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.

m2r

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.

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 forceably 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 constrast, 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!

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 flexbility 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

4

Partition table

003000

4

esp_init_data_default.bin

PHY configuration data

004000

12

blank.bin

System parameter area

006000

4

blank.bin

RF Calibration data (Initialised to FFh)

006000

4

Reserved

008000

ROM_0_ADDR

rom0.bin

First ROM image

100000

RBOOT_SPIFFS_0

208000

ROM_1_ADDR

rom1.bin

Second ROM image

300000

RBOOT_SPIFFS_1

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.

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:

Task states

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
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 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 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.

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_rBoot 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:

int slot = rboot_get_current_rom();
// Find the n'th SPIFFS partition
auto part = PartitionTable().find(Partition::SubType::Data::spiffs, slot);
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.

For details, refer to the OtaUpdate() function in the Basic_rBoot sample.

Command Handler

Summary

The Sming CommandHandler implementation adds the possibility to handle system and application commands over a variety of input media.

The CLI provides a framework in which basic system commands can be extended and application specific commands created.

Currently implemented are: - Serial - Telnet Server - Websocket Server

Implementation

The implementation makes use of a global object CommandHandler, which at the start is just a skeleton without any command active.

System commands can be activated using: - Serial.commandProcessing(true) - telnetServer.enableCommand(true) - websockerServer.commandProcessing(true, "command")

The additional parameter for websocket is used to allow multiple connections on one websocket server instance, both with or without command proceesing.

When the websocket open request has query parameter "command=true" command processing is enabled.

Usage

The usage of CommandProcessor is the same for all implementations, except for specific requirements. CommandProcesor options implemented are: - Verbose -> sets display of welcome message - Prompt -> set prompt string - EOL -> set end of line character - welcome message -> set the welcome message

System Commands

The base CommandHandler has the following sysytem commands implemented: - help, Displays all available commands - status, Displays System Information - echo, Displays command entered - debugon, Set Serial debug on - debugoff, Set Serial debug off - command, Usage verbose/silent/prompt for command options

Application options

Applications (and Sming Core functionality) can add commands to the CommandProcesor using a construct like

registerCommand(CommandDelegate(
    "status",
    "Displays System Information",
    "system",
    commandFunctionDelegate(&CommandHandler::procesStatusCommand, this)
));

The added command will then be available over every opened command channel.

Example usage

The capabilities of CommandHandler are shown in the example CommandProcessing_Debug.

The example will create an application which shows CommandProcessing for Telnet, Websocket and Serial.

  • to test Telnet, open telnet client and connect on port 23 to the ESP ip

  • to test the Websocket, open a web browser with the ESP

  • to test Serial use your “Serial program” and make sure “local echo” is activated

For all implementations type “help” to show the available Commands

The possibilities of extending commands with the application are shown in: - the class ExampleCommand - the functions startExampleApplicationCommand() and processApplicationCommands()

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 v4.3 to v4.4

4.3.1
Network support

The core network code has been moved out of Sming/Core/Network and into a separate component at Components/Network. This is to extend the use of the DISABLE_WIFI setting to reduce the code required to be built for applications which do not require networking.

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.

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 overriden 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

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);

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

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 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.

For easier integration make sure you have both ESP_HOME and SMING_HOME exported in your working environment.

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

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.

  • A debugging configuration is not currently provided for ESP32.

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.

  2. 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.

  3. Re-program your device:

    make flash
    

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. At the moment Sming provides partial support for ESP32 functions.

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