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
  • Built-in file system: SPIFFS for Sming
  • 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.
  • Out of the box support for OTA over HTTPS.
  • 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

Sming supports multiple architectures. Choose the architecture of your choice to install the needed development software and toolchain(s).

Getting Started: ESP8266
Development Environment

Please select the appropriate instructions for setting up the development environment for your platform:

Note

See ESP Quick Toolchain for experimental support for the latest compilers.

Toolchains can all be found in the SmingTools repository.

Contents
Linux Installation
Pre-requisites

Ubuntu:

sudo apt-get install make unrar autoconf automake libtool libtool-bin gcc g++ gperf \
   flex bison texinfo gawk ncurses-dev libexpat1-dev python sed python-serial \
   python-dev srecord bc git help2man unzip bzip2

Fedora:

sudo dnf install make autoconf automake libtool gcc gcc-c++ gperf flex bison texinfo \
   gawk ncurses-devel expat-devel python sed pyserial srecord bc git patch \
   unzip help2man python-devel
Prepare directory

We recommend /opt/ but you can use anything you want:

mkdir -p /opt/
cd /opt/
Build/Install Toolchain

This builds the cross-compiler, linker, etc. required for the ESP8266:

git clone --recursive https://github.com/pfalcon/esp-open-sdk.git
cd esp-open-sdk

# be careful this command can do damage if used
# in the wrong directory, try without sudo first!
sudo chown -R [username] ./

# This will take a while...
make STANDALONE=y

You can find pre-compiled toolchains in the SmingTools repository.

Espressif SDK

Sming uses the Espressif 3.0.1 SDK which is pulled in automatically during the build.

Previous versions are no longer officially supported.

Environment Variables

From the command line:

export ESP_HOME=/opt/esp-open-sdk
export SMING_HOME=/opt/Sming/Sming

To set these permanently, add them to your home .profile file.

You can alternatively add them to /etc/environment for all users, like this:

SMING_HOME="/opt/Sming/Sming"

See also Setting up Sming and Netbeans (external page).

Get Sming Core

Clone the Sming develop branch to your working directory:

cd $SMING_HOME/../..
git clone https://github.com/SmingHub/Sming.git
# 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 intended for developers
# and it is the one where all new cool (unstable) features are landing.


cd Sming

If you want to use our stable branch:

git checkout origin/master
Next steps

Proceed to Configuring your Esp8266 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 ~/
curl -L -O https://github.com/SmingHub/SmingTools/releases/download/1.0/esp-open-sdk-macosx.tar.gz
sudo mkdir -p /opt/
sudo tar -zxf esp-open-sdk-macosx.tar.gz -C /opt/
sudo chmod -R 775 /opt/esp-open-sdk

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 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-open-sdk
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 Esp8266 device.

Windows Installation
Install Chocolatey

This is a package manager, like apt-get but for Windows. Official website: https://chocolatey.org

Note

You may prefer a Manual Windows Installation.

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

# Install Latest Chocolatey
@powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin
Upgrade .NET

Choco requires recent version of .NET in order to be able to access successfully HTTPS links. To check if you need upgrade type the following command:

choco --v

If you see a warning similar to the one below then make sure to upgrade your .NET version:

Choco prefers to use TLS v1.2 if it is available, but this client is running on .NET 4.3,
which uses an older SSL. It's using TLS 1.0 or earlier, which makes it susceptible to BEAST
and also doesn't implement the 1/n-1 record splitting mitigation for Cipher-Block Chaining.
Upgrade to at least .NET 4.5 at your earliest convenience.

You can upgrade the .NET version from this direct link or search in internet for “.NET upgrade”.

Add package repository
choco sources add -name smingrepo -source 'https://www.myget.org/F/sming/'
Install Sming
# Powershell - run as Administrator
choco install -y sming

This will install:

  • Unofficial Espressif Development Kit for Windows (source)
  • Mingw & required packages (source)
  • Python
  • Latest stable version of Sming (source)

If for some reason you don’t want UDK, MinGW and Environment configured just run:

choco install sming.core -y -source 'https://www.myget.org/f/sming/'
Configure git for Windows environment

Configure your git client to leave line-endings intact or else patching will fail:

git config --global core.autocrlf input
Install Sming Examples (optional)

This will install:

  • Java Runtime 8
  • Eclipse C/C++ (source)
  • sming package

and will create desktop shortcut Sming Examples:

# Run as Administrator
choco install sming.examples -y
Configuration

You might want to configure your project before building. Edit component.mk to the proper values.

Confirm Environment

Make sure the MinGW make.exe is the only one in the path. This will correct most “make: ** No rule to make target” problems.*:

# should only show one make
where make.exe
"C:\Tools\mingw64\msys\1.0\bin\make.exe"
Update Sming

Sming is very dynamic and updates are usually announced in gitter. The command below will get for you the latest stable release:

choco upgrade sming -y
Force Reinstall Sming

In case something is broken, this will overwrite the current Sming installation:

choco install sming -y -force -source 'https://www.myget.org/F/sming/'
Next steps

Proceed to Configuring your Esp8266 device.

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-udk-win32.7z.

  2. Unzip to default location:

    7z -oC:\Espressif x esp-udk-win32.7z
    
  3. Set ESP_HOME environment variable:

    SETX ESP_HOME C:\Espressif
    

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

Docker Installation
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 single file and then the application can be spinned up using one command.

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

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/docker/ && docker-compose -f sming-ide.yml up -d
Open your browser

http://localhost:10080

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

Attention

This is pre-release documentation and subject to change.

Introduction

In Sming 4.0.1 support was added for the ESP Quick Toolchain for GCC 9.2.0.

This also updates the runtime libraries (NewLib) to version 2.2 with integrated PROGMEM handling code.

See Pull Request #1825 for further details.

The new toolchain is consistent across development platforms and adds support for the latest compiler features, as discussed in What are the new features in C++17?.

Installation

The toolchains are currently at pre-release, available here. Download links for the 3.0.0-gnu5 release (8 December 2019) as follows:

Extract the toolchain to a suitable location, such as:

  • /opt/esp-quick-toolchain
  • C:\tools\esp-quick-toolchain

and set ESP_HOME accordingly.

Configuring your Esp8266 device

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

Note

Arduino has a system which allows these values to be set based on a board selection. At present Sming does not have such a system so this must be done manually.

Here are a few important ones:

  • SPI_MODE Change this if your device fails to program or run
  • SPI_SIZE If using a device with reduced memory you may need to change this value. Note also that samples using SPIFFS for Sming may not work.
  • 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 SPI_MODE=dio SPI_SIZE=4M COM_SPEED_ESPTOOL=921600

Once you’re happy with the settings, you can add them to your project.mk file.

You can list the current set of configuration variables like this:

make list-config

If you want to reset back to default values, do this:

make config-clean
Getting Started: ESP32
Requirements

In order to be able to compile for the ESP32 architecture you should have ESP-IDF v4.0 installed. A detailed installation manual can be found in the ESP-IDF documentation.

Building

Make sure that the IDF_PATH environmental variable is set. Also make sure that the other ESP-IDF environmental variables are set. For example on Linux this can be done using the following command:

source $IDF_PATH/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
Getting Started: Linux/Windows

The latest versions of Sming allow most of components of the library and the sample applications to be compiled on a Linux/Windows host system and be tested before uploading them to the microcontroller.

If you want to try it we have an interactive tutorial that can be run directly from your browser.

Requirements (Linux)
Modern Linux distribution

Most popular modern distributions should be fine. At the moment we develop only with Ubuntu 16.04 and 18.04.

C/C++ 32 bit compiler and libraries

If your OS is 64 bit then you should install also 32 bit C/C++ compiler and libraries. For Ubuntu, install like this:

sudo apt-get install gcc-multilib g++-multilib

You may wish to install the latest compilers for testing alongside the recent ESP Quick Toolchain updates. Here’s a summary of how to do this:

sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt update
sudo apt install gcc-9 g++-9

# For 64-bit systems
sudo apt install gcc-9-multilib g++-9-multilib

# Set GCC 9 as default
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 60 --slave /usr/bin/g++ g++ /usr/bin/g++-9

The current version is GCC 9.2.1.

CMake 3.8 or newer

For the compilation of LWIP CMake version 3.8 or newer is required. In order to get newer CMake version under Ubuntu 16.04, do this:

cd /tmp
wget https://github.com/Kitware/CMake/releases/download/v3.14.4/cmake-3.14.4-Linux-x86_64.sh
sudo mkdir /opt/cmake
sudo sh cmake-3.14.4-Linux-x86_64.sh --prefix=/opt/cmake
sudo mv /usr/bin/cmake /usr/bin/cmake.orig
sudo ln -s /opt/cmake/bin/cmake /usr/bin/cmake
Requirements (Windows)

For Windows, make sure your MinGW distro is up to date. The current version is GCC 8.2.0.

Note

If you don’t already have MinGW installed, see Windows Installation for the Esp8266.

Compilation
Environment variables

SMING_ARCH must be set to use Host as the desired architecture:

export SMING_ARCH=Host
Debug Build

If you plan to use a debugger make sure to set ENABLE_GDB and (optionally) ENABLE_LWIPDEBUG before compiling the code:

export ENABLE_GDB=1
export ENABLE_LWIPDEBUG=1  # <!-- this will compile also LWIP with debug symbols
Initial Clean-up

Make sure that you are working on a clean source code tree. The following commands may help:

cd $SMING_HOME
make dist-clean
Sample compilation

Compile a sample as usual:

cd $SMING_HOME/../samples/Basic_Blink
make
Adapting existing code

You may need to modify your existing applications to work with the emulator. Architecture-specific code should be moved into separate code modules. Code that is specific to the ESP8266 may be conditionally compiled using #ifdef ARCH_ESP8266. Similarly, you can check if the code is compiled for the emulator using #ifdef ARCH_HOST.

Running on the host

Once the sample is compiled, initialise the Virtual Flasher:

make flash

If enabled, this will also create a SPIFFS file system and write it to flash.

To run the application, do this:

make run
Networking

Support is provided via TAP network interface (a virtual network layer operating at the ethernet frame level). A TAP interface must be created first, and requires root privilege. You can use the Sming/Arch/Host/Tools/setup-network-linux.sh. Here is the manual approach:

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

# The following lines are needed if you plan to access Internet
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

This creates the tap0 interface. The emulator will automatically select the first tap interface found. To override this, use the --ifname option.

Troubleshooting
Issue 1: fatal error: sys/cdefs.h: No such file or directory

Make sure to install the 32bit version of the GNU C and C++ compiler, development package and libraries.

Issue 2: fatal error: bits/c++config.h: No such file or directory

Make sure to install the 32bit version of the GNU C and C++ compiler, development package and libraries.

Further reading
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 Manual 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
Environment Variables
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

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 sockets, allowing terminal emulation using telnet.

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.

You should not need to change this for normal use.

Usage

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.

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

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

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.

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

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
todo
  • Add passthrough support for real serial ports to permit connection of physical devices.
  • Consider how this mechanism might be used to support emulation of other devices (SPI, I2C, etc).
  • Development platforms with SPI or I2C (e.g. Raspberry Pi) could be supported.
  • Are there any generic device emulators available? For example, to simulate specific types of SPI slave.

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

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. Once you have completed the installation of the development tools, you can get the latest source code.

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

And check some of the examples.

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 you 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 based on rBoot
void OtaUpdate()
{
  uint8 slot;
  rboot_config bootconf;

  Serial.println("Updating...");

  // need a clean object, otherwise if run before and failed will not run again
  if (otaUpdater) {
    delete otaUpdater;
  }

  otaUpdater = new RbootHttpUpdater();

  // select rom slot to flash
  bootconf = rboot_get_config();
  slot = bootconf.current_rom;
  if (slot == 0) {
    slot = 1;
  }
  else {
    slot = 0;
  }

  // flash rom to position indicated in the rBoot config rom table
  otaUpdater->addItem(bootconf.roms[slot], ROM_0_URL);

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

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

For a complete example take a look at the Basic rBoot 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.

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 Types

template<>
using Iterator = ObjectIterator<ObjectType, 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.

Iterator begin() const
Iterator end() const
size_t length() const

Get the length of the content in elements.

template <typename ValueType>
int indexOf(const ValueType &value) const
ElementType valueAt(unsigned index) const
ElementType operator[](unsigned index) const

Array operator[].

size_t elementSize() const
const ElementType *data() const
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

size_t size() const

Get the object data size in bytes.

Note
Always an integer multiple of 4 bytes

template <class ObjectType>
constexpr const ObjectType &as() const

Cast to a different object type.

Note
example:
    fstr.as<Array<int>>();

size_t read(size_t offset, void *buffer, size_t count) const

Read contents of a String into RAM.

Parameters
  • offset: Zero-based offset from start of flash data to start reading
  • buffer: Where to store data
  • count: How many bytes to read
Return Value
  • size_t: Number of bytes actually read

size_t readFlash(size_t offset, void *buffer, size_t count) const

Read contents of a String into RAM, using flashread()

PROGMEM data is accessed via the CPU data cache, so to avoid degrading performance you can use this method to read data directly from flash memory. This is appropriate for infrequently accessed data, especially if it is large. For example, if storing content using

IMPORT_FSTR instead of SPIFFS then it is generally better to avoid contaminating the cache.
See
See also FlashMemoryStream class.
Parameters
  • offset: Zero-based offset from start of flash data to start reading
  • buffer: Where to store data
  • count: How many bytes to read
Return Value
  • size_t: Number of bytes actually read

bool isCopy() const
bool isNull() const

Indicates an invalid String, used for return value from lookups, etc.

Note
A real String can be zero-length, but it cannot be null

Public Members

uint32_t flashLength_

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 Types

template<>
using Iterator = ObjectIterator<String, char>

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

bool operator==(const char *str) const
bool operator==(const String &str) const
bool operator!=(const char *str) const
bool operator!=(const String &str) const
operator WString() const
bool equals(const WString &str) const
bool equalsIgnoreCase(const WString &str) const
bool operator==(const WString &str) const
bool operator!=(const WString &str) const
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());

size_t printTo(Print &p) const
Iterator begin() const
Iterator end() const
size_t length() const

Get the length of the content in elements.

int indexOf(const ValueType &value) const
char valueAt(unsigned index) const
char operator[](unsigned index) const

Array operator[].

size_t elementSize() const
size_t read(size_t index, char *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 read(size_t offset, void *buffer, size_t count) const

Read contents of a String into RAM.

Parameters
  • offset: Zero-based offset from start of flash data to start reading
  • buffer: Where to store data
  • count: How many bytes to read
Return Value
  • size_t: Number of bytes actually read

size_t readFlash(size_t index, char *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

size_t readFlash(size_t offset, void *buffer, size_t count) const

Read contents of a String into RAM, using flashread()

PROGMEM data is accessed via the CPU data cache, so to avoid degrading performance you can use this method to read data directly from flash memory. This is appropriate for infrequently accessed data, especially if it is large. For example, if storing content using

IMPORT_FSTR instead of SPIFFS then it is generally better to avoid contaminating the cache.
See
See also FlashMemoryStream class.
Parameters
  • offset: Zero-based offset from start of flash data to start reading
  • buffer: Where to store data
  • count: How many bytes to read
Return Value
  • size_t: Number of bytes actually read

template <class ObjectType>
constexpr const ObjectType &as() const

Cast to a different object type.

Note
example:
    fstr.as<Array<int>>();

bool isCopy() const
bool isNull() const

Indicates an invalid String, used for return value from lookups, etc.

Note
A real String can be zero-length, but it cannot be null

Public Members

uint32_t flashLength_

Public Static Functions

static const String &empty()

Return an empty object which evaluates to null.

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 Types

template<>
using Iterator = ObjectIterator<Array<ElementType>, ElementType>

Public Functions

ArrayPrinter<Array> printer() const

Returns a printer object for this array.

Note
ElementType must be supported by Print

size_t printTo(Print &p) const
Iterator begin() const
Iterator end() const
size_t length() const

Get the length of the content in elements.

int indexOf(const ValueType &value) const
ElementType valueAt(unsigned index) const
ElementType operator[](unsigned index) const

Array operator[].

size_t elementSize() const
const ElementType *data() const
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 read(size_t offset, void *buffer, size_t count) const

Read contents of a String into RAM.

Parameters
  • offset: Zero-based offset from start of flash data to start reading
  • buffer: Where to store data
  • count: How many bytes to read
Return Value
  • size_t: Number of bytes 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

size_t readFlash(size_t offset, void *buffer, size_t count) const

Read contents of a String into RAM, using flashread()

PROGMEM data is accessed via the CPU data cache, so to avoid degrading performance you can use this method to read data directly from flash memory. This is appropriate for infrequently accessed data, especially if it is large. For example, if storing content using

IMPORT_FSTR instead of SPIFFS then it is generally better to avoid contaminating the cache.
See
See also FlashMemoryStream class.
Parameters
  • offset: Zero-based offset from start of flash data to start reading
  • buffer: Where to store data
  • count: How many bytes to read
Return Value
  • size_t: Number of bytes actually read

size_t size() const

Get the object data size in bytes.

Note
Always an integer multiple of 4 bytes

template <class ObjectType>
constexpr const ObjectType &as() const

Cast to a different object type.

Note
example:
    fstr.as<Array<int>>();

bool isCopy() const
bool isNull() const

Indicates an invalid String, used for return value from lookups, etc.

Note
A real String can be zero-length, but it cannot be null

Public Members

uint32_t flashLength_

Public Static Functions

static const Array<ElementType> &empty()

Return an empty object which evaluates to null.

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 Members

template<>
ElementType values[Columns]

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:

Public Types

template<>
using Iterator = ObjectIterator<Vector<ObjectType>, ObjectType *>

Public Functions

template <typename ValueType, typename T = ObjectType>
std::enable_if<std::is_same<T, String>::value, int>::type indexOf(const ValueType &value, bool ignoreCase = true) const
const ObjectType &valueAt(unsigned index) const
const ObjectType &operator[](unsigned index) const
ArrayPrinter<Vector> printer() const
size_t printTo(Print &p) const
Iterator begin() const
Iterator end() const
size_t length() const

Get the length of the content in elements.

int indexOf(const ValueType &value) const
size_t elementSize() const
const ObjectType **data() const
size_t read(size_t index, ObjectType **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 read(size_t offset, void *buffer, size_t count) const

Read contents of a String into RAM.

Parameters
  • offset: Zero-based offset from start of flash data to start reading
  • buffer: Where to store data
  • count: How many bytes to read
Return Value
  • size_t: Number of bytes actually read

size_t readFlash(size_t index, ObjectType **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

size_t readFlash(size_t offset, void *buffer, size_t count) const

Read contents of a String into RAM, using flashread()

PROGMEM data is accessed via the CPU data cache, so to avoid degrading performance you can use this method to read data directly from flash memory. This is appropriate for infrequently accessed data, especially if it is large. For example, if storing content using

IMPORT_FSTR instead of SPIFFS then it is generally better to avoid contaminating the cache.
See
See also FlashMemoryStream class.
Parameters
  • offset: Zero-based offset from start of flash data to start reading
  • buffer: Where to store data
  • count: How many bytes to read
Return Value
  • size_t: Number of bytes actually read

size_t size() const

Get the object data size in bytes.

Note
Always an integer multiple of 4 bytes

template <class ObjectType>
constexpr const ObjectType &as() const

Cast to a different object type.

Note
example:
    fstr.as<Array<int>>();

bool isCopy() const
bool isNull() const

Indicates an invalid String, used for return value from lookups, etc.

Note
A real String can be zero-length, but it cannot be null

Public Members

uint32_t flashLength_

Public Static Functions

static const Vector<ObjectType> &empty()

Return an empty object which evaluates to null.

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
DEFINE_FSTR_MAP(name, KeyType, ContentType, ...)
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_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 }

const Pair FSTR::Map::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 FSTR::Map::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 FSTR::Map::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 FSTR::Map::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> FSTR::Map::printer() const

Returns a printer object for this array.

Note
ElementType must be supported by Print

size_t FSTR::Map::printTo(Print &p) const
DECLARE_FSTR_MAP(name, KeyType, ContentType)

Declare a global Map& reference.

Note
Use DEFINE_FSTR_MAP to instantiate the global object
Parameters
  • name:
  • KeyType: Integral type to use for key
  • ContentType: Object type to declare for content

template <typename KeyType, class ContentType, class Pair = MapPair<KeyType, ContentType>>
class Map : public FSTR::Object<Map<KeyType, ContentType>, Pair>
#include <Map.hpp>

Class template to access an associative map.

Template Parameters

Public Types

template<>
using Iterator = ObjectIterator<Map<KeyType, ContentType>, Pair>

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

size_t printTo(Print &p) const
Iterator begin() const
Iterator end() const
size_t length() const

Get the length of the content in elements.

int indexOf(const ValueType &value) const
Pair operator[](unsigned index) const

Array operator[].

size_t elementSize() const
const Pair *data() const
size_t read(size_t index, Pair *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 read(size_t offset, void *buffer, size_t count) const

Read contents of a String into RAM.

Parameters
  • offset: Zero-based offset from start of flash data to start reading
  • buffer: Where to store data
  • count: How many bytes to read
Return Value
  • size_t: Number of bytes actually read

size_t readFlash(size_t index, Pair *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

size_t readFlash(size_t offset, void *buffer, size_t count) const

Read contents of a String into RAM, using flashread()

PROGMEM data is accessed via the CPU data cache, so to avoid degrading performance you can use this method to read data directly from flash memory. This is appropriate for infrequently accessed data, especially if it is large. For example, if storing content using

IMPORT_FSTR instead of SPIFFS then it is generally better to avoid contaminating the cache.
See
See also FlashMemoryStream class.
Parameters
  • offset: Zero-based offset from start of flash data to start reading
  • buffer: Where to store data
  • count: How many bytes to read
Return Value
  • size_t: Number of bytes actually read

size_t size() const

Get the object data size in bytes.

Note
Always an integer multiple of 4 bytes

template <class ObjectType>
constexpr const ObjectType &as() const

Cast to a different object type.

Note
example:
    fstr.as<Array<int>>();

bool isCopy() const
bool isNull() const

Indicates an invalid String, used for return value from lookups, etc.

Note
A real String can be zero-length, but it cannot be null

Public Members

uint32_t flashLength_

Public Static Functions

static const Map<KeyType, ContentType> &empty()

Return an empty object which evaluates to null.

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

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

Template Parameters

Public Types

template<>
typedef void (MapPair::*IfHelperType)() const

Public Functions

void IfHelper() const
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.

operator const ContentType&() const
operator WString() const
size_t printTo(Print &p) const

Public Members

KeyStoreType key_
const ContentType *content_

Public Static Functions

static const MapPair empty()

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

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 Types

template<>
using Iterator = ObjectIterator<Map<KeyType, ContentType>, Pair>

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

size_t printTo(Print &p) const
Iterator begin() const
Iterator end() const
size_t length() const

Get the length of the content in elements.

int indexOf(const ValueType &value) const
Pair operator[](unsigned index) const

Array operator[].

size_t elementSize() const
const Pair *data() const
size_t read(size_t index, Pair *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 read(size_t offset, void *buffer, size_t count) const

Read contents of a String into RAM.

Parameters
  • offset: Zero-based offset from start of flash data to start reading
  • buffer: Where to store data
  • count: How many bytes to read
Return Value
  • size_t: Number of bytes actually read

size_t readFlash(size_t index, Pair *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

size_t readFlash(size_t offset, void *buffer, size_t count) const

Read contents of a String into RAM, using flashread()

PROGMEM data is accessed via the CPU data cache, so to avoid degrading performance you can use this method to read data directly from flash memory. This is appropriate for infrequently accessed data, especially if it is large. For example, if storing content using

IMPORT_FSTR instead of SPIFFS then it is generally better to avoid contaminating the cache.
See
See also FlashMemoryStream class.
Parameters
  • offset: Zero-based offset from start of flash data to start reading
  • buffer: Where to store data
  • count: How many bytes to read
Return Value
  • size_t: Number of bytes actually read

size_t size() const

Get the object data size in bytes.

Note
Always an integer multiple of 4 bytes

template <class ObjectType>
constexpr const ObjectType &as() const

Cast to a different object type.

Note
example:
    fstr.as<Array<int>>();

bool isCopy() const
bool isNull() const

Indicates an invalid String, used for return value from lookups, etc.

Note
A real String can be zero-length, but it cannot be null

Public Members

uint32_t flashLength_

Public Static Functions

static const Map<KeyType, ContentType> &empty()

Return an empty object which evaluates to null.

template <typename KeyType, class ContentType>
class MapPair

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

Template Parameters

Public Types

template<>
typedef void (MapPair::*IfHelperType)() const

Public Functions

void IfHelper() const
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.

operator const ContentType&() const
operator WString() const
size_t printTo(Print &p) const

Public Members

KeyStoreType key_
const ContentType *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)
IROM_SECTION
template <typename T, typename U>
template<>
struct argument_type<T(U)>
#include <Utility.hpp>

Public Types

typedef U type
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
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 Crypto::Blake2s = typedef HashContext<Blake2sEngine<hashsize> >
using Crypto::Blake2s256 = typedef Blake2s<32>
using Crypto::Blake2s128 = typedef Blake2s<16>
using Crypto::HmacBlake2s = typedef HmacContext<Blake2s<hashsize> >
using Crypto::HmacBlake2s256 = typedef HmacBlake2s<32>
using Crypto::HmacBlake2s128 = typedef HmacBlake2s<16>
using Crypto::Secret = typedef Blob

Identifies data which should be treated with care.

using Crypto::ByteArray = typedef 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 Crypto::Md5 = typedef HashContext<Md5Engine>
using Crypto::HmacMd5 = typedef HmacContext<Md5>
using Crypto::Sha1 = typedef HashContext<Sha1Engine>
using Crypto::HmacSha1 = typedef HmacContext<Sha1>
using Crypto::Sha224 = typedef HashContext<Sha224Engine>
using Crypto::Sha256 = typedef HashContext<Sha256Engine>
using Crypto::Sha384 = typedef HashContext<Sha384Engine>
using Crypto::Sha512 = typedef HashContext<Sha512Engine>
using Crypto::HmacSha224 = typedef HmacContext<Sha224>
using Crypto::HmacSha256 = typedef HmacContext<Sha256>
using Crypto::HmacSha384 = typedef HmacContext<Sha384>
using Crypto::HmacSha512 = typedef 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.

Public Functions

Blob(const void *data, size_t size)
Blob(const String &str)
const uint8_t *data() const
size_t size() const
template <class Engine_>
class HashContext
#include <HashContext.h>

Class template for a Hash implementation ‘Context’.

Template Parameters
  • Engine: The HashEngine implementation

Subclassed by OtaUpgrade::ChecksumVerifier

Update hash over a given block of data

HashContext &update(const Blob &blob)
HashContext &update(const FSTR::ObjectBase &obj)
HashContext &update(const void *data, size_t size)
template <size_t size_>
HashContext &update(const ByteArray<size_> &array)

Public Types

template<>
using Engine = Engine_
template<>
using Hash = ByteArray<Engine::hashsize>

Public Functions

template <typename... EngineArgs>
HashContext(EngineArgs&&... engineArgs)
template <typename... EngineArgs>
HashContext &reset(EngineArgs&&... engineArgs)

Reset the context for a new calculation.

template <typename... Ts>
Hash calculate(Ts&&... args)

Calculate hash on some data.

Parameters
Return Value
  • Hash:

Hash getHash()

Finalise and return the final hash value.

Return Value
  • Hash:

State getState()

Get intermediate hash state.

Note
This method is only required for core hashes, used by Bear SSL
Parameters
  • state: OUT: current state
Return Value
  • uint64_t: Number of bytes processed so far

void setState(const State &state)

Restore intermediate hash state.

Parameter values obtained via previous getState() call

Note
This method is only required for core hashes, used by Bear SSL
Parameters
  • state:
  • count:

struct State
#include <HashContext.h>

Public Members

template<>
ByteArray<Engine::statesize> value
template<>
uint64_t count
template <class HashContext>
class HmacContext
#include <HmacContext.h>

HMAC class template.

Implements the HMAC algorithm using any defined hash context

Public Types

template<>
using Engine = typename HashContext::Engine
template<>
using Hash = typename HashContext::Hash

Public Functions

HmacContext()

Default HMAC constructor.

Must call init() first.

HmacContext(const Secret &key)

Initialise HMAC context with key.

HmacContext &init(const Secret &key)

Initialise HMAC with key.

Return Value
  • Reference: to enable method chaining

template <typename... Ts>
HmacContext &update(Ts&&... args)

Update HMAC with some message content.

Parameters
Return Value
  • Reference: to enable method chaining

Hash getHash()
template <typename... Ts>
Hash calculate(Ts&&... args)

Calculate hash for some data.

Use like this:

    auto hash = Crypto::HmacMd5(mySecret).calculate(myData);

Parameters
Return Value
  • Hash:

Public Static Attributes

constexpr size_t blocksize = Engine::blocksize
References
Used by
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.

Sming uses rBoot exclusively because of its flexibility, reliability and ease of use.

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 which is always used.

RBOOT_ROM0_ADDR

default: 0x2000.

This is the start address for slot 0. The default is sector 3, immediately after rBoot and its configuration data.

Except for the use case described in Slot2 below, you should not need to set this value.

Slot 1
RBOOT_ROM1_ADDR

default: disabled

The start address of slot 1. If you don’t need it, leave unconfigured (empty).

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.

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 with the same offset within its 1MB block as RBOOT_ROM0_ADDR, e.g. 0x102000 for the default slot 0 address (0x2000). This way, the same application image can be used for both slots. See Single vs. Dual ROMs for further details.

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

Address for slot 2

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

  1. Create a new Sming project for your recovery application. This will be a simple single-slot project. Set RBOOT_ROM0_ADDR of the recovery project to the value of RBOOT_ROM2_ADDR of the main project.
  2. 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.
  3. 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.
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. The Sming build system takes care of all the details, 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, 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
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.

SPIFFS for Sming

This Component provides additional functionality to support SPIFFS running on both Esp8266 and Host architectures.

DISABLE_SPIFFS
0 (default)
Enable filesystem generation
1
Disable filesystem generation

The value is also #defined for application use.

Note this doesn’t actually disable SPIFFS support in the application!

SPIFF_SIZE

Size of the SPIFFS area in Flash memory.

SPIFF_FILES

default: files

The SPIFFS image is built using files from this directory.

SPIFF_BIN

Path to the generated SPIFFS filesystem image.

SPIFF_FILEDESC_COUNT

Default: 7

Number of file descriptors allocated. This sets the maximum number of files which may be opened at once.

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

  session.fragmentSize = Ssl::eSEFS_16K;
}

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.

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 Types

using InitDelegate = Delegate<void(Session &session)>

Public Functions

~Session()
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

const uint8_t *getValue() const
unsigned getLength() const
bool isValid() const
bool assign(const uint8_t *newValue, unsigned newLength)
String toString() const

Return a string representation of the session ID.

struct Options

Configurable options.

Public Functions

Options()
String toString() const

Public Members

bool sessionResume

Keep a note of session ID for later re-use.

bool clientAuthentication
bool verifyLater

Allow handshake to complete before verifying certificate.

bool freeKeyCertAfterHandshake
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 Ssl::CipherSuites::Array = typedef FSTR::Array<Ssl::CipherSuite>
Certificates
class Certificate

Implemented by SSL adapter to handle certificate operations.

Public Types

enum DN

Distinguished Name type.

Values:

ISSUER
SUBJECT
enum RDN

Relative Distinguished Name type identifying a name component.

Values:

XX
MAX

Public Functions

virtual ~Certificate()
virtual bool getFingerprint(Fingerprint::Type type, Fingerprint &fingerprint) const = 0

Obtain certificate fingerprint.

Parameters
  • type: Which type of fingerprint to return
  • fingerprint: On success, returned fingerprint
Return Value
  • bool: true on success, false if fingerprint not available

virtual String getName(DN dn, RDN rdn) const = 0

Retrieve an X.509 distinguished name component.

Parameters
  • dn: The desired Distinguished Name
  • rdn: The component to return
Return Value
  • String: The requested Distinguished Name component

size_t printTo(Print &p) const

Debugging print support.

class ValidatorList : public Vector<Validator>

Performs certificate validation.

Validators are created in the application’s session initialisation callback. When the certificate has been received, it is checked against each registered validator in turn until successful. All validators are destroyed during this process.

If there are no validators in the list then the certificate will not be checked and the connection accepted.

Public Types

template<>
typedef int (*Comparer)(const Validator &lhs, const Validator &rhs)

Public Functions

bool add(Validator *validator)

Add a validator to the list.

Parameters
  • validator: Must be allocated on the heap

template <class T>
bool pin(const T &fingerprint)

Pin a fingerprint.

Creates and adds a fingerprint validator to the list

bool add(ValidatorCallback callback, void *data = nullptr)

Register a custom validator callback.

Parameters
  • callback:
  • data: User-provided data (optional)

bool validate(const Certificate *certificate)

Validate certificate via registered validators.

We only need one match for a successful result, but we free all the validators. This method must be called no more than ONCE.

Note
Called by SSL framework.
Parameters
  • certificate: When called with nullptr will free all validators, then fail
Return Value
  • bool: true on success, false on failure

unsigned int capacity() const
bool contains(const Validator &elem) const
const Validator &firstElement() const
int indexOf(const Validator &elem) const
bool isEmpty() const
const Validator &lastElement() const
int lastIndexOf(const Validator &elem) const
unsigned int count() const
unsigned int size() const
void copyInto(Validator *array) const
bool add(const Validator &obj)
bool addElement(const Validator &obj)
bool addElement(Validator *objp)
void clear()
bool ensureCapacity(unsigned int minCapacity)
void removeAllElements()
bool removeElement(const Validator &obj)
bool setSize(unsigned int newSize)
void trimToSize()
const Validator &elementAt(unsigned int index) const
bool insertElementAt(const Validator &obj, unsigned int index)
const void remove(unsigned int index)
void removeElementAt(unsigned int index)
bool setElementAt(const Validator &obj, unsigned int index)
const Validator &get(unsigned int index) const
const Validator &operator[](unsigned int index) const
Validator &operator[](unsigned int index)
void sort(Comparer compareFunction)
const Validator &at(unsigned int i) const

Public Members

Fingerprint::Types fingerprintTypes

Contains a list of registered fingerprint types.

Allows implementations to avoid calculating fingerprint values which are not required, as this is computationally expensive.

class Validator

Base validator class.

Validation is performed by invoking each validator in turn until a successful result is obtained.

Custom validators may either override this class, or use a callback.

Subclassed by Ssl::CallbackValidator, Ssl::FingerprintValidator< FP >

Public Functions

virtual ~Validator()
virtual bool validate(const Certificate &certificate) = 0
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
Adapter API

These classes provide the interface between a Ssl::Session and an appropriate adapter.

  • 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. Use getAlert
class Factory

Implemented by SSL adapter.

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

Public Functions

virtual ~Factory()
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

Context(Session &session)
virtual ~Context()
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*:

Public Members

Session &session
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

Connection(Context &context, tcp_pcb *tcp)
virtual ~Connection()
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

virtual void freeCertificate() = 0
size_t printTo(Print &p) const

For debugging.

int writeTcpData(uint8_t *data, size_t length)
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

Public Members

Context &context
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

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.0 installed. A detailed installation manual can be found in the ESP-IDF documentation.

Building

Make sure that the IDF_PATH environmental variable is set. Also make sure that the other ESP-IDF environmental variables are set. For example on Linux this can be done using the following command:

source $IDF_PATH/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
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
Esp32 Drivers

Provides low-level peripheral drivers.

GPIO: General-Purpose I/O

SDK definitions for GPIO.

const esp32_gpioMux_t esp32_gpioMux[40]
const int8_t esp32_adc2gpio[20]
ESP32_LOW
ESP32_HIGH
ESP32_INPUT
ESP32_OUTPUT
ESP32_PULLUP
ESP32_INPUT_PULLUP
ESP32_PULLDOWN
ESP32_INPUT_PULLDOWN
ESP32_OPEN_DRAIN
ESP32_OUTPUT_OPEN_DRAIN
ESP32_SPECIAL
ESP32_FUNCTION_1
ESP32_FUNCTION_2
ESP32_FUNCTION_3
ESP32_FUNCTION_4
ESP32_FUNCTION_5
ESP32_FUNCTION_6
ESP32_ANALOG
ESP32_DISABLED
ESP32_RISING
ESP32_FALLING
ESP32_CHANGE
ESP32_ONLOW
ESP32_ONHIGH
ESP32_ONLOW_WE
ESP32_ONHIGH_WE
digitalPinIsValid(pin)
digitalPinCanOutput(pin)
digitalPinToRtcPin(pin)
digitalPinToAnalogChannel(pin)
digitalPinToTouchChannel(pin)
digitalPinToDacChannel(pin)
enum gpio_driver::GPIO_INT_TYPE

Defines the GPIO interrupt type.

Values:

GPIO_PIN_INTR_DISABLE = 0

Interrupt disabled for this pin

GPIO_PIN_INTR_POSEDGE = 1

Interrupt occurs on positive edge

GPIO_PIN_INTR_NEGEDGE = 2

Interrupt occurs on negative edge

GPIO_PIN_INTR_ANYEDGE = 3

Interrupt occurs on both positive and negative edge

GPIO_PIN_INTR_LOLEVEL = 4

Interrupt occurs when GPIO low

GPIO_PIN_INTR_HILEVEL = 5

Interrupt occurs when GPIO high

struct esp32_gpioMux_t
#include <gpio.h>

Public Members

uint8_t reg

GPIO register offset from DR_REG_IO_MUX_BASE

int8_t rtc

RTC GPIO number (-1 if not RTC GPIO pin)

int8_t adc

ADC Channel number (-1 if not ADC pin)

int8_t touch

Touch Channel number (-1 if not Touch pin)

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

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
enum i2s_driver::i2s_bits_per_sample_t

I2S bit width per sample.

Values:

I2S_BITS_PER_SAMPLE_8BIT = 8

I2S bits per sample: 8-bits.

I2S_BITS_PER_SAMPLE_16BIT = 16

I2S bits per sample: 16-bits.

I2S_BITS_PER_SAMPLE_24BIT = 24

I2S bits per sample: 24-bits.

I2S_BITS_PER_SAMPLE_8BIT = 8

I2S bits per sample: 8-bits.

I2S_BITS_PER_SAMPLE_16BIT = 16

I2S bits per sample: 16-bits.

I2S_BITS_PER_SAMPLE_24BIT = 24

I2S bits per sample: 24-bits.

enum i2s_driver::i2s_channel_t

I2S channel.

Values:

I2S_CHANNEL_MONO = 1

I2S 1 channel (mono)

I2S_CHANNEL_STEREO = 2

I2S 2 channel (stereo)

I2S_CHANNEL_MONO = 1

I2S 1 channel (mono)

I2S_CHANNEL_STEREO = 2

I2S 2 channel (stereo)

enum i2s_driver::i2s_comm_format_t

I2S communication standard format.

Values:

I2S_COMM_FORMAT_I2S = 0x01

I2S communication format I2S.

I2S_COMM_FORMAT_I2S_MSB = 0x01

I2S format MSB.

I2S_COMM_FORMAT_I2S_LSB = 0x03

I2S format LSB.

I2S_COMM_FORMAT_I2S = 0x01

I2S communication format I2S.

I2S_COMM_FORMAT_I2S_MSB = 0x01

I2S format MSB.

I2S_COMM_FORMAT_I2S_LSB = 0x03

I2S format LSB.

enum i2s_driver::i2s_channel_fmt_t

I2S channel format type.

Values:

I2S_CHANNEL_FMT_RIGHT_LEFT = 0x00
I2S_CHANNEL_FMT_ALL_RIGHT
I2S_CHANNEL_FMT_ALL_LEFT
I2S_CHANNEL_FMT_ONLY_RIGHT
I2S_CHANNEL_FMT_ONLY_LEFT
I2S_CHANNEL_FMT_RIGHT_LEFT = 0x00
I2S_CHANNEL_FMT_ALL_RIGHT
I2S_CHANNEL_FMT_ALL_LEFT
I2S_CHANNEL_FMT_ONLY_RIGHT
I2S_CHANNEL_FMT_ONLY_LEFT
enum i2s_driver::i2s_mode_t

I2S Mode, default is I2S_MODE_MASTER.

Values:

I2S_MODE_DISABLED
I2S_MODE_MASTER
I2S_MODE_SLAVE
I2S_MODE_DISABLED
I2S_MODE_MASTER
I2S_MODE_SLAVE
enum i2s_driver::i2s_event_type_t

I2S event types.

Values:

I2S_EVENT_DMA_ERROR
I2S_EVENT_TX_DONE

I2S DMA finish sent 1 buffer

I2S_EVENT_RX_DONE

I2S DMA finish received 1 buffer

I2S_EVENT_MAX

I2S event max index

I2S_EVENT_DMA_ERROR
I2S_EVENT_TX_DONE

I2S DMA finish sent 1 buffer

I2S_EVENT_RX_DONE

I2S DMA finish received 1 buffer

I2S_EVENT_MAX

I2S event max index

enum i2s_driver::i2s_pin_t

I2S pin enable for i2s_set_pin.

Values:

I2S_PIN_BCK_OUT = 0x01

GPIO 15 / TXD2 / D8.

I2S_PIN_WS_OUT = 0x02

GPIO 2 / TXD1 / D4.

I2S_PIN_DATA_OUT = 0x04

GPIO 3 / RXD0 / D9.

I2S_PIN_BC_IN = 0x10

GPIO 13 / RXD2 / D7.

I2S_PIN_WS_IN = 0x20

GPIO 14 / D5.

I2S_PIN_DATA_IN = 0x40

GPIO 12 / D6.

I2S_PIN_BCK_OUT = 0x01

GPIO 15 / TXD2 / D8.

I2S_PIN_WS_OUT = 0x02

GPIO 2 / TXD1 / D4.

I2S_PIN_DATA_OUT = 0x04

GPIO 3 / RXD0 / D9.

I2S_PIN_BC_IN = 0x10

GPIO 13 / RXD2 / D7.

I2S_PIN_WS_IN = 0x20

GPIO 14 / D5.

I2S_PIN_DATA_IN = 0x40

GPIO 12 / D6.

enum i2s_driver::i2s_bits_per_sample_t

I2S bit width per sample.

Values:

I2S_BITS_PER_SAMPLE_8BIT = 8

I2S bits per sample: 8-bits.

I2S_BITS_PER_SAMPLE_16BIT = 16

I2S bits per sample: 16-bits.

I2S_BITS_PER_SAMPLE_24BIT = 24

I2S bits per sample: 24-bits.

I2S_BITS_PER_SAMPLE_8BIT = 8

I2S bits per sample: 8-bits.

I2S_BITS_PER_SAMPLE_16BIT = 16

I2S bits per sample: 16-bits.

I2S_BITS_PER_SAMPLE_24BIT = 24

I2S bits per sample: 24-bits.

enum i2s_driver::i2s_channel_t

I2S channel.

Values:

I2S_CHANNEL_MONO = 1

I2S 1 channel (mono)

I2S_CHANNEL_STEREO = 2

I2S 2 channel (stereo)

I2S_CHANNEL_MONO = 1

I2S 1 channel (mono)

I2S_CHANNEL_STEREO = 2

I2S 2 channel (stereo)

enum i2s_driver::i2s_comm_format_t

I2S communication standard format.

Values:

I2S_COMM_FORMAT_I2S = 0x01

I2S communication format I2S.

I2S_COMM_FORMAT_I2S_MSB = 0x01

I2S format MSB.

I2S_COMM_FORMAT_I2S_LSB = 0x03

I2S format LSB.

I2S_COMM_FORMAT_I2S = 0x01

I2S communication format I2S.

I2S_COMM_FORMAT_I2S_MSB = 0x01

I2S format MSB.

I2S_COMM_FORMAT_I2S_LSB = 0x03

I2S format LSB.

enum i2s_driver::i2s_channel_fmt_t

I2S channel format type.

Values:

I2S_CHANNEL_FMT_RIGHT_LEFT = 0x00
I2S_CHANNEL_FMT_ALL_RIGHT
I2S_CHANNEL_FMT_ALL_LEFT
I2S_CHANNEL_FMT_ONLY_RIGHT
I2S_CHANNEL_FMT_ONLY_LEFT
I2S_CHANNEL_FMT_RIGHT_LEFT = 0x00
I2S_CHANNEL_FMT_ALL_RIGHT
I2S_CHANNEL_FMT_ALL_LEFT
I2S_CHANNEL_FMT_ONLY_RIGHT
I2S_CHANNEL_FMT_ONLY_LEFT
enum i2s_driver::i2s_mode_t

I2S Mode, default is I2S_MODE_MASTER.

Values:

I2S_MODE_DISABLED
I2S_MODE_MASTER
I2S_MODE_SLAVE
I2S_MODE_DISABLED
I2S_MODE_MASTER
I2S_MODE_SLAVE
enum i2s_driver::i2s_event_type_t

I2S event types.

Values:

I2S_EVENT_DMA_ERROR
I2S_EVENT_TX_DONE

I2S DMA finish sent 1 buffer

I2S_EVENT_RX_DONE

I2S DMA finish received 1 buffer

I2S_EVENT_MAX

I2S event max index

I2S_EVENT_DMA_ERROR
I2S_EVENT_TX_DONE

I2S DMA finish sent 1 buffer

I2S_EVENT_RX_DONE

I2S DMA finish received 1 buffer

I2S_EVENT_MAX

I2S event max index

enum i2s_driver::i2s_pin_t

I2S pin enable for i2s_set_pin.

Values:

I2S_PIN_BCK_OUT = 0x01

GPIO 15 / TXD2 / D8.

I2S_PIN_WS_OUT = 0x02

GPIO 2 / TXD1 / D4.

I2S_PIN_DATA_OUT = 0x04

GPIO 3 / RXD0 / D9.

I2S_PIN_BC_IN = 0x10

GPIO 13 / RXD2 / D7.

I2S_PIN_WS_IN = 0x20

GPIO 14 / D5.

I2S_PIN_DATA_IN = 0x40

GPIO 12 / D6.

I2S_PIN_BCK_OUT = 0x01

GPIO 15 / TXD2 / D8.

I2S_PIN_WS_OUT = 0x02

GPIO 2 / TXD1 / D4.

I2S_PIN_DATA_OUT = 0x04

GPIO 3 / RXD0 / D9.

I2S_PIN_BC_IN = 0x10

GPIO 13 / RXD2 / D7.

I2S_PIN_WS_IN = 0x20

GPIO 14 / D5.

I2S_PIN_DATA_IN = 0x40

GPIO 12 / D6.

typedef void (*i2s_callback_t)(void *param, i2s_event_type_t event)

Callback function type.

Note
Function is called in interrupt context, so place in IRAM and keep it brief.

typedef unsigned TickType_t

Defines the wait interval (presently milliseconds)

typedef uint8_t i2s_pin_set_t
typedef void (*i2s_callback_t)(void *param, i2s_event_type_t event)

Callback function type.

Note
Function is called in interrupt context, so place in IRAM and keep it brief.

typedef unsigned TickType_t

Defines the wait interval (presently milliseconds)

typedef uint8_t i2s_pin_set_t
bool i2s_driver_install(const i2s_config_t *config)

Install and start I2S driver.

Note
This function must be called before any I2S driver read/write operations.
Parameters
  • config: I2S configuration
Return Value
  • true: on success, false if already installed or invalid config

void i2s_driver_uninstall()

Uninstall I2S driver.

bool i2s_start()

Start I2S driver.

Note
It is not necessary to call this function after i2s_driver_install() as it is started automatically, unless config.auto_start was set to false.
Return Value
  • bool: true on success, false if driver not initialised

bool i2s_stop()

Stop I2S driver.

Note
Disables I2S TX/RX, until i2s_start() is called
Return Value
  • bool: true on success, false if driver not initialised

bool i2s_set_sample_rates(uint32_t rate)

Parameters
  • rate: Sample rate in Hz (ex 44100, 48000) for TX/RX

bool i2s_set_dividers(uint8_t bck_div, uint8_t mclk_div)

Direct control over output rate

float i2s_get_real_rate()

Return Value
  • float: The actual Sample Rate on output

bool i2s_dma_read(i2s_buffer_info_t *info, size_t max_bytes)

Fetch a DMA buffer containing received data (zero-copy)

Note
On success, info->buffer specifies where to read the data from, and info->size how many bytes are actually available (always > 0).
Note
Returns at most one DMA buffer
Parameters
  • info: Pointer to structure to receive buffer information
  • max_bytes: Number of bytes to read
Return Value
  • bool: true on success, false if no data available or info is null

bool i2s_dma_write(i2s_buffer_info_t *info, size_t max_bytes)

Fetch a DMA buffer for direct writing (zero-copy)

Note
On success, info->buffer specifies where to write the data, and info->size how many bytes should be written - may be less than max_bytes, but always > 0.
Note
Returns at most one DMA buffer
Parameters
  • info: Pointer to structure to receive buffer information
  • max_bytes: Number of bytes required in buffer
Return Value
  • bool: true on success, false if buffer unavailable or info is null

size_t i2s_write(const void *src, size_t size, TickType_t ticks_to_wait)

writes a buffer of frames into the DMA memory, returns the amount of frames written.

Note
Data is copied into DMA buffers
Parameters
  • src: Data to write
  • size: Size in bytes
  • ticks_to_wait: Wait timeout in ticks
Return Value
  • size_t: Data actually written, may be less than size

size_t i2s_read(void *dest, size_t size, TickType_t ticks_to_wait)

Reads a block of received data.

Parameters
  • dest: Buffer to store data
  • size: Max. bytes to read
  • ticks_to_wait: Wait timeout in ticks
Return Value
  • size_t: Number of bytes read

bool i2s_zero_dma_buffer()

Zero the contents of the TX DMA buffer.

Note
Pushes zero-byte samples into the TX DMA buffer, until it is full

void i2s_set_pins(i2s_pin_set_t pins, bool enable)

Configure I2S pins.

You can alternatively use arduino functions.

Note
Call this after initialising driver to specify which pins are required
Parameters
  • pins: Mask of i2s_pin_t values
  • enable: true to enable for I2S use, false to revert to GPIO

Example: i2s_set_pins(_BV(I2S_BCK_OUT), true)

bool i2s_enable_loopback(bool enable)
bool i2s_stat_tx(i2s_buffer_stat_t *stat)

Obtain state information for TX buffers.

Parameters
  • stat:
Return Value
  • bool: true on success

bool i2s_stat_rx(i2s_buffer_stat_t *stat)

Obtain state information for RX buffers.

Parameters
  • stat:
Return Value
  • bool: true on success

union i2s_sample_t
#include <i2s.h>

I2S sample.

An I2S frame can contain various types of data:

8-bit, 16-bit or 24-bit mono samples 8-bit or 16-bit stereo samples

Public Members

uint32_t u32
int16_t left
int16_t right
struct i2s_sample_t::[anonymous] [anonymous]
struct i2s_sample_t::[anonymous] [anonymous]
struct i2s_module_config_t
#include <i2s.h>

I2S module configuration (TX or RX)

Public Members

i2s_mode_t mode

I2S work mode (combination of i2s_mode_t)

i2s_bits_per_sample_t bits_per_sample

I2S bits per sample.

i2s_channel_fmt_t channel_format

I2S channel format.

i2s_comm_format_t communication_format

I2S communication format.

uint16_t dma_buf_len

I2S DMA Buffer Length (in samples)

uint8_t dma_buf_count

I2S DMA Buffer Count.

uint8_t callback_threshold

TX: callback when available buffers > threshold RX: Callback when slc_queue_len > threshold

struct i2s_config_t
#include <i2s.h>

I2S configuration parameters.

Public Members

i2s_module_config_t tx

TX module configuration.

i2s_module_config_t rx

RX module configuration.

unsigned sample_rate

I2S sample rate.

bool tx_desc_auto_clear

I2S auto clear tx descriptor if there is underflow condition (Mutes output)

bool auto_start

Start immediately on successful initialisation.

i2s_callback_t callback

Callback handler.

void *param

Callback parameter.

uint8_t bits_mod

Evaluate what this does (4 bits)

struct i2s_buffer_info_t
#include <i2s.h>

Defines a buffer with available content.

Public Members

void *buffer
i2s_sample_t *samples
union i2s_buffer_info_t::[anonymous] [anonymous]
size_t size

Available space (TX) or data (RX) in bytes.

uint16_t buf
uint16_t pos
union i2s_buffer_info_t::[anonymous] [anonymous]
struct i2s_buffer_stat_t
#include <i2s.h>

Contains I2S buffer status information.

Note
Size excludes buffer in use by DMA

Public Members

uint16_t size
uint16_t used
OS Timer
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

PWM: Pulse-Width Modulation

The driver interface is defined in the ESP8266 SDK.

Build variables
ENABLE_CUSTOM_PWM
undefined
use the Espressif PWM driver
1 (default)
Use the New PWM driver, a drop-in replacement for the version provided in the Espressif SDK.
API Documentation
void pwm_init(uint32 period, uint32 *duty, uint32 pwm_channel_num, uint32 (*pin_info_list)[3])

Initialize PWM function, including GPIO selection, period and duty cycle.

Example:

    uint32 io_info[][3] = {
        {PWM_0_OUT_IO_MUX, PWM_0_OUT_IO_FUNC, PWM_0_OUT_IO_NUM},
        {PWM_1_OUT_IO_MUX, PWM_1_OUT_IO_FUNC, PWM_1_OUT_IO_NUM},
        {PWM_2_OUT_IO_MUX, PWM_2_OUT_IO_FUNC, PWM_2_OUT_IO_NUM}
};

pwm_init(light_param.pwm_period, light_param.pwm_duty, 3, io_info);
Note
This API can be called only once.
Parameters
  • period: PWM period
  • duty: duty cycle of each output
  • pwm_channel_num: PWM channel number
  • pin_info_list: Array containing an entry for each channel giving

void pwm_start(void)

Starts PWM.

This function needs to be called after PWM configuration is changed.

void pwm_set_duty(uint32 duty, uint8 channel)

Sets duty cycle of a PWM output.

Set the time that high-level signal will last. The range of duty depends on PWM period. Its maximum value of which can be Period * 1000 / 45.

Parameters
  • duty: The time that high-level single will last, duty cycle will be (duty*45)/(period*1000)
  • channel: PWM channel, which depends on how many PWM channels are used

For example, for 1-KHz PWM, the duty range is 0 ~ 22222.

uint32 pwm_get_duty(uint8 channel)

Get duty cycle of PWM output.

Duty cycle will be (duty*45) / (period*1000).

Parameters
  • channel: PWM channel, which depends on how many PWM channels are used
Return Value
  • uint32: Duty cycle of PWM output

void pwm_set_period(uint32 period)

Set PWM period.

Parameters
  • period: PWM period in us. For example, 1-KHz PWM period = 1000us.

uint32 pwm_get_period(void)

Get PWM period.

Return Value
  • uint32: Return PWM period in us.

uint32 get_pwm_version(void)

Get version information of PWM.

Return Value
  • uint32: PWM version

UART: Universal Asynchronous Receive/Transmit

Custom asynchronous driver.

enum uart_driver::smg_uart_mode_

values for mode argument of uart_init

Values:

UART_FULL

Both receive and transmit - will revert to TX only if RX not supported.

UART_RX_ONLY

Receive only.

UART_TX_ONLY

Transmit only.

UART_FULL

Both receive and transmit - will revert to TX only if RX not supported.

UART_RX_ONLY

Receive only.

UART_TX_ONLY

Transmit only.

enum uart_driver::smg_uart_option_bits_t

bit values for options argument of uart_init

Note
use _BV(opt) to specify values

Values:

UART_OPT_TXWAIT

If buffers are full then uart_write() will wait for free space.

UART_OPT_CALLBACK_RAW

ISR invokes user callback function with no pre-processing.

UART_OPT_TXWAIT

If buffers are full then uart_write() will wait for free space.

UART_OPT_CALLBACK_RAW

ISR invokes user callback function with no pre-processing.

enum uart_driver::smg_uart_notify_code_t

Indicates notification, parameters refer to uart_notify_info_t structure.

Values:

UART_NOTIFY_AFTER_OPEN

Called when uart has been initialised successfully.

UART_NOTIFY_BEFORE_CLOSE

Called immediately before uart is closed and destroyed.

UART_NOTIFY_AFTER_WRITE

Called after data has been written into tx buffer.

UART_NOTIFY_BEFORE_READ

Called before data is read from rx buffer.

UART_NOTIFY_WAIT_TX

Called to ensure all buffered data is output.

UART_NOTIFY_AFTER_OPEN

Called when uart has been iniitialised successfully.

UART_NOTIFY_BEFORE_CLOSE

Called immediately before uart is closed and destroyed.

UART_NOTIFY_AFTER_WRITE

Called after data has been written into tx buffer.

UART_NOTIFY_BEFORE_READ

Called before data is read from rx buffer.

UART_NOTIFY_WAIT_TX

Called to ensure all buffered data is output.

enum uart_driver::smg_uart_mode_

values for mode argument of uart_init

Values:

UART_FULL

Both receive and transmit - will revert to TX only if RX not supported.

UART_RX_ONLY

Receive only.

UART_TX_ONLY

Transmit only.

UART_FULL

Both receive and transmit - will revert to TX only if RX not supported.

UART_RX_ONLY

Receive only.

UART_TX_ONLY

Transmit only.

enum uart_driver::smg_uart_option_bits_t

bit values for options argument of uart_init

Note
use _BV(opt) to specify values

Values:

UART_OPT_TXWAIT

If buffers are full then uart_write() will wait for free space.

UART_OPT_CALLBACK_RAW

ISR invokes user callback function with no pre-processing.

UART_OPT_TXWAIT

If buffers are full then uart_write() will wait for free space.

UART_OPT_CALLBACK_RAW

ISR invokes user callback function with no pre-processing.

enum uart_driver::smg_uart_notify_code_t

Indicates notification, parameters refer to uart_notify_info_t structure.

Values:

UART_NOTIFY_AFTER_OPEN

Called when uart has been initialised successfully.

UART_NOTIFY_BEFORE_CLOSE

Called immediately before uart is closed and destroyed.

UART_NOTIFY_AFTER_WRITE

Called after data has been written into tx buffer.

UART_NOTIFY_BEFORE_READ

Called before data is read from rx buffer.

UART_NOTIFY_WAIT_TX

Called to ensure all buffered data is output.

UART_NOTIFY_AFTER_OPEN

Called when uart has been iniitialised successfully.

UART_NOTIFY_BEFORE_CLOSE

Called immediately before uart is closed and destroyed.

UART_NOTIFY_AFTER_WRITE

Called after data has been written into tx buffer.

UART_NOTIFY_BEFORE_READ

Called before data is read from rx buffer.

UART_NOTIFY_WAIT_TX

Called to ensure all buffered data is output.

typedef enum smg_uart_mode_ smg_uart_mode_t
typedef uint8_t uart_options_t
typedef struct smg_uart_ smg_uart_t
typedef void (*smg_uart_callback_t)(smg_uart_t *uart, uint32_t status)

callback invoked directly from ISR

Errors can be detected via uart_get_status().

Note
Values can be: UIFE: TX FIFO Empty UIFF: RX FIFO Full UITO: RX FIFO Timeout UIBD: Break Detected
Parameters
  • arg: the UART object
  • status: UART USIS STATUS flag bits indicating cause of interrupt

typedef void (*smg_uart_notify_callback_t)(smg_uart_t *uart, smg_uart_notify_code_t code)

Port notification callback function type.

Parameters
  • info:
Return Value
  • bool: true if callback handled operation, false to default to normal operation

typedef enum smg_uart_mode_ smg_uart_mode_t
typedef uint8_t uart_options_t
typedef struct smg_uart_ smg_uart_t
typedef void (*smg_uart_callback_t)(smg_uart_t *uart, uint32_t status)

callback invoked directly from ISR

Errors can be detected via uart_get_status().

Note
Values can be: UIFE: TX FIFO Empty UIFF: RX FIFO Full UITO: RX FIFO Timeout UIBD: Break Detected
Parameters
  • arg: the UART object
  • status: UART USIS STATUS flag bits indicating cause of interrupt

typedef void (*smg_uart_notify_callback_t)(smg_uart_t *uart, smg_uart_notify_code_t code)

Port notification callback function type.

Parameters
  • info:
Return Value
  • bool: true if callback handled operation, false to default to normal operation

bool smg_uart_set_notify(unsigned uart_nr, smg_uart_notify_callback_t callback)

Set the notification callback function.

Parameters
  • uart_nr: Which uart to register notifications for
  • callback:
Return Value
  • bool: true on success

smg_uart_t *smg_uart_init(uint8_t uart_nr, uint32_t baudrate, uint32_t config, smg_uart_mode_t mode, uint8_t tx_pin, size_t rx_size, size_t tx_size = 0)
smg_uart_t *smg_uart_init_ex(const smg_uart_config &cfg)
void smg_uart_uninit(smg_uart_t *uart)
int smg_uart_get_nr(smg_uart_t *uart)
smg_uart_t *smg_uart_get_uart(uint8_t uart_nr)

Get the uart structure for the given number.

Parameters
  • uart_nr:
Return Value
  • uart_t*: Returns nullptr if uart isn’t initialised

void smg_uart_set_callback(smg_uart_t *uart, smg_uart_callback_t callback, void *param)

Set callback handler for serial port.

Parameters
  • uart:
  • callback: specify nullptr to disable callbacks
  • param: user parameter passed to callback

void *smg_uart_get_callback_param(smg_uart_t *uart)

Get the callback parameter specified by uart_set_callback()

Parameters
  • uart:
Return Value
  • void*: the callback parameter

static void smg_uart_set_options(smg_uart_t *uart, uart_options_t options)

Set option flags.

Parameters
  • uart:
  • options: The option(s) to set

uint8_t smg_uart_get_status(smg_uart_t *uart)

Get error flags and clear them.

Note
To detect errors during a transaction, call at the start to clear the flags, then check the value at the end. Only these values are cleared/returned: UIBD: Break Detected UIOF: RX FIFO OverFlow UIFR: Frame Error UIPE: Parity Error
Parameters
  • uart:
Return Value
  • Status: error bits:

static uart_options_t smg_uart_get_options(smg_uart_t *uart)
void smg_uart_swap(smg_uart_t *uart, int tx_pin)
void smg_uart_set_tx(smg_uart_t *uart, int tx_pin)
void smg_uart_set_pins(smg_uart_t *uart, int tx, int rx)
bool smg_uart_tx_enabled(smg_uart_t *uart)
bool smg_uart_rx_enabled(smg_uart_t *uart)
uint32_t smg_uart_set_baudrate_reg(int uart_nr, uint32_t baud_rate)

set UART baud rate, given the UART number

Parameters
  • uart_nr:
  • baud_rate: requested baud rate
Return Value
  • uint32_t: actual baudrate used, 0 on failure

uint32_t smg_uart_set_baudrate(smg_uart_t *uart, uint32_t baud_rate)

set UART baud rate

Parameters
  • uart:
  • baud_rate: requested baud rate
Return Value
  • uint32_t: actual baudrate used, 0 on failure

uint32_t smg_uart_get_baudrate(smg_uart_t *uart)

get the actual baud rate in use

Parameters
  • uart:
Return Value
  • uint32_t: the baud rate, 0 on failure

size_t smg_uart_resize_rx_buffer(smg_uart_t *uart, size_t new_size)
size_t smg_uart_rx_buffer_size(smg_uart_t *uart)
size_t smg_uart_resize_tx_buffer(smg_uart_t *uart, size_t new_size)
size_t smg_uart_tx_buffer_size(smg_uart_t *uart)
size_t smg_uart_write(smg_uart_t *uart, const void *buffer, size_t size)

write a block of data

Parameters
  • uart:
  • buffer:
  • size:
Return Value
  • size_t: number of bytes buffered for transmission

static size_t smg_uart_write_char(smg_uart_t *uart, char c)

queue a single character for output

Parameters
  • uart:
  • c:
Return Value
  • size_t: 1 if character was written, 0 on failure

size_t smg_uart_read(smg_uart_t *uart, void *buffer, size_t size)

read a block of data

Parameters
  • uart:
  • buffer: where to write the data
  • size: requested quantity of bytes to read
Return Value
  • size_t: number of bytes read

static int smg_uart_read_char(smg_uart_t *uart)

read a received character

Parameters
  • uart:
Return Value
  • the: character, -1 on failure

int smg_uart_peek_char(smg_uart_t *uart)

see what the next character in the rx buffer is

Note
if buffer isn’t allocated data may be in the hardware FIFO, which must be read out using uart_read()
Parameters
  • uart:
Return Value
  • int: returns -1 if buffer is empty or not allocated

int smg_uart_peek_last_char(smg_uart_t *uart)

fetch last character read out of FIFO

Note
this is only useful if an rx buffer has been allocated of sufficient size to contain a message. This function then indicates the terminating character.
Parameters
  • uart:
Return Value
  • int: the character, or -1 if rx buffer is empty or unallocated

int smg_uart_rx_find(smg_uart_t *uart, char c)
size_t smg_uart_rx_available(smg_uart_t *uart)

determine available data which can be read

Note
this obtains a count of data both in the memory buffer and hardware FIFO
Parameters
  • uart:
Return Value
  • size_t:

size_t smg_uart_tx_free(smg_uart_t *uart)

return free space in transmit buffer

void smg_uart_wait_tx_empty(smg_uart_t *uart)

void smg_uart_set_break(smg_uart_t *uart, bool state)

Set or clear a break condition on the TX line.

Parameters
  • uart:
  • state:

void smg_uart_flush(smg_uart_t *uart, smg_uart_mode_t mode = UART_FULL)

discard any buffered data and reset hardware FIFOs

Note
this function does not wait for any transmissions to complete
Parameters
  • uart:
  • mode: Whether to flush TX, RX or both (the default)

void smg_uart_set_debug(int uart_nr)
int smg_uart_get_debug()
void smg_uart_start_isr(smg_uart_t *uart)

enable interrupts for a UART

Parameters
  • uart:

void smg_uart_stop_isr(smg_uart_t *uart)

disable interrupts for a UART

Parameters
  • uart:

void smg_uart_detach(int uart_nr)

detach a UART interrupt service routine

Parameters
  • uart_nr:

void smg_uart_detach_all()

detach all UART interrupt service routines

Note
call at startup to put all UARTs into a known state

uint8_t smg_uart_disable_interrupts()

disable interrupts and return current interrupt state

Return Value
  • state: non-zero if any UART interrupts were active

void smg_uart_restore_interrupts()

re-enable interrupts after calling uart_disable_interrupts()

UART0
UART1
UART2

Virtualised UART0.

UART_NO

No UART specified.

UART_PHYSICAL_COUNT

Number of physical UARTs on the system.

UART_COUNT

Number of UARTs on the system, virtual or otherwise.

UART_NB_BIT_MASK
UART_NB_BIT_5
UART_NB_BIT_6
UART_NB_BIT_7
UART_NB_BIT_8
UART_PARITY_MASK
UART_PARITY_NONE
UART_PARITY_EVEN
UART_PARITY_ODD
UART_NB_STOP_BIT_MASK
UART_NB_STOP_BIT_0
UART_NB_STOP_BIT_1
UART_NB_STOP_BIT_15
UART_NB_STOP_BIT_2
UART_5N1
UART_6N1
UART_7N1
UART_8N1
UART_5N2
UART_6N2
UART_7N2
UART_8N2
UART_5E1
UART_6E1
UART_7E1
UART_8E1
UART_5E2
UART_6E2
UART_7E2
UART_8E2
UART_5O1
UART_6O1
UART_7O1
UART_8O1
UART_5O2
UART_6O2
UART_7O2
UART_8O2
UART_RX_FIFO_SIZE
UART_TX_FIFO_SIZE
UART0
UART1
UART2

Virtualised UART0.

UART_NO

No UART specified.

UART_PHYSICAL_COUNT

Number of physical UARTs on the system.

UART_COUNT

Number of UARTs on the system, virtual or otherwise.

UART_NB_BIT_MASK
UART_NB_BIT_5
UART_NB_BIT_6
UART_NB_BIT_7
UART_NB_BIT_8
UART_PARITY_MASK
UART_PARITY_NONE
UART_PARITY_EVEN
UART_PARITY_ODD
UART_NB_STOP_BIT_MASK
UART_NB_STOP_BIT_0
UART_NB_STOP_BIT_1
UART_NB_STOP_BIT_15
UART_NB_STOP_BIT_2
UART_5N1
UART_6N1
UART_7N1
UART_8N1
UART_5N2
UART_6N2
UART_7N2
UART_8N2
UART_5E1
UART_6E1
UART_7E1
UART_8E1
UART_5E2
UART_6E2
UART_7E2
UART_8E2
UART_5O1
UART_6O1
UART_7O1
UART_8O1
UART_5O2
UART_6O2
UART_7O2
UART_8O2
UART_RX_FIFO_SIZE
UART_TX_FIFO_SIZE
struct smg_uart_
#include <uart.h>

Public Members

uint8_t uart_nr
uint32_t baud_rate
smg_uart_mode_t mode
uint8_t options
uint8_t rx_pin
uint8_t tx_pin
uint8_t rx_headroom

Callback when rx_buffer free space <= headroom.

uint16_t status

All status flags reported to callback since last uart_get_status() call.

struct SerialBuffer *rx_buffer

Optional receive buffer.

struct SerialBuffer *tx_buffer

Optional transmit buffer.

smg_uart_callback_t callback

Optional User callback routine.

void *param

User-supplied callback parameter.

uart_port_t uart_nr
struct smg_uart_config
#include <uart.h>

Public Members

uint8_t uart_nr
uint8_t tx_pin

Specify 2 for alternate pin, otherwise defaults to pin 1.

smg_uart_mode_t mode

Whether to enable receive, transmit or both.

uart_options_t options
uint32_t baudrate

Requested baudrate; actual baudrate may differ.

uint32_t config

UART CONF0 register bits.

size_t rx_size
size_t tx_size
uint8_t rx_pin
References
Used by
Environment Variables
Esp32 Core Component

Contains startup code, crash handling and additional Esp32-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
Host ESP HAL

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

  • Task queues
  • Timer queues
  • System functions
References
Used by
References
Used by
Environment Variables
References
Used by
Environment Variables
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
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
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
  • SPIFlashInfo: Information block

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
INTERNAL_FLASH_START_ADDRESS
struct STORE_TYPEDEF_ATTR
#include <esp_spi_flash.h>

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

Public Types

enum [anonymous]

Values:

MODE_QIO = 0
MODE_QOUT = 1
MODE_DIO = 2
MODE_DOUT = 15
enum [anonymous]

Values:

SPEED_40MHZ = 0
SPEED_26MHZ = 1
SPEED_20MHZ = 2
SPEED_80MHZ = 15
enum [anonymous]

Values:

SIZE_4MBIT = 0
SIZE_2MBIT = 1
SIZE_8MBIT = 2
SIZE_16MBIT = 3
SIZE_32MBIT = 4
enum [anonymous]

Values:

MODE_QIO = 0
MODE_QOUT = 1
MODE_DIO = 2
MODE_DOUT = 15
enum [anonymous]

Values:

SPEED_40MHZ = 0
SPEED_26MHZ = 1
SPEED_20MHZ = 2
SPEED_80MHZ = 15
enum [anonymous]

Values:

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

Public Members

uint8_t unknown0
uint8_t unknown1
STORE_TYPEDEF_ATTR::[anonymous] mode
STORE_TYPEDEF_ATTR::[anonymous] speed
STORE_TYPEDEF_ATTR::[anonymous] size
STORE_TYPEDEF_ATTR::[anonymous] mode
STORE_TYPEDEF_ATTR::[anonymous] speed
STORE_TYPEDEF_ATTR::[anonymous] size
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

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

References
Used by

Sming Esp8266 Architecture

Support building Sming for the Esp8266 architecture.

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
DISABLE_WIFI

If a project does not require WiFi (or networking) bulding with this option enabled reduces code size and memory usage signficantly. 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
Esp8266 Drivers

Provides low-level peripheral drivers.

GPIO: General-Purpose I/O

SDK definitions for GPIO.

const esp32_gpioMux_t esp32_gpioMux[40]
const int8_t esp32_adc2gpio[20]
ESP32_LOW
ESP32_HIGH
ESP32_INPUT
ESP32_OUTPUT
ESP32_PULLUP
ESP32_INPUT_PULLUP
ESP32_PULLDOWN
ESP32_INPUT_PULLDOWN
ESP32_OPEN_DRAIN
ESP32_OUTPUT_OPEN_DRAIN
ESP32_SPECIAL
ESP32_FUNCTION_1
ESP32_FUNCTION_2
ESP32_FUNCTION_3
ESP32_FUNCTION_4
ESP32_FUNCTION_5
ESP32_FUNCTION_6
ESP32_ANALOG
ESP32_DISABLED
ESP32_RISING
ESP32_FALLING
ESP32_CHANGE
ESP32_ONLOW
ESP32_ONHIGH
ESP32_ONLOW_WE
ESP32_ONHIGH_WE
digitalPinIsValid(pin)
digitalPinCanOutput(pin)
digitalPinToRtcPin(pin)
digitalPinToAnalogChannel(pin)
digitalPinToTouchChannel(pin)
digitalPinToDacChannel(pin)
enum gpio_driver::GPIO_INT_TYPE

Defines the GPIO interrupt type.

Values:

GPIO_PIN_INTR_DISABLE = 0

Interrupt disabled for this pin

GPIO_PIN_INTR_POSEDGE = 1

Interrupt occurs on positive edge

GPIO_PIN_INTR_NEGEDGE = 2

Interrupt occurs on negative edge

GPIO_PIN_INTR_ANYEDGE = 3

Interrupt occurs on both positive and negative edge

GPIO_PIN_INTR_LOLEVEL = 4

Interrupt occurs when GPIO low

GPIO_PIN_INTR_HILEVEL = 5

Interrupt occurs when GPIO high

struct esp32_gpioMux_t
#include <gpio.h>

Public Members

uint8_t reg

GPIO register offset from DR_REG_IO_MUX_BASE

int8_t rtc

RTC GPIO number (-1 if not RTC GPIO pin)

int8_t adc

ADC Channel number (-1 if not ADC pin)

int8_t touch

Touch Channel number (-1 if not Touch pin)

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

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
enum i2s_driver::i2s_bits_per_sample_t

I2S bit width per sample.

Values:

I2S_BITS_PER_SAMPLE_8BIT = 8

I2S bits per sample: 8-bits.

I2S_BITS_PER_SAMPLE_16BIT = 16

I2S bits per sample: 16-bits.

I2S_BITS_PER_SAMPLE_24BIT = 24

I2S bits per sample: 24-bits.

I2S_BITS_PER_SAMPLE_8BIT = 8

I2S bits per sample: 8-bits.

I2S_BITS_PER_SAMPLE_16BIT = 16

I2S bits per sample: 16-bits.

I2S_BITS_PER_SAMPLE_24BIT = 24

I2S bits per sample: 24-bits.

enum i2s_driver::i2s_channel_t

I2S channel.

Values:

I2S_CHANNEL_MONO = 1

I2S 1 channel (mono)

I2S_CHANNEL_STEREO = 2

I2S 2 channel (stereo)

I2S_CHANNEL_MONO = 1

I2S 1 channel (mono)

I2S_CHANNEL_STEREO = 2

I2S 2 channel (stereo)

enum i2s_driver::i2s_comm_format_t

I2S communication standard format.

Values:

I2S_COMM_FORMAT_I2S = 0x01

I2S communication format I2S.

I2S_COMM_FORMAT_I2S_MSB = 0x01

I2S format MSB.

I2S_COMM_FORMAT_I2S_LSB = 0x03

I2S format LSB.

I2S_COMM_FORMAT_I2S = 0x01

I2S communication format I2S.

I2S_COMM_FORMAT_I2S_MSB = 0x01

I2S format MSB.

I2S_COMM_FORMAT_I2S_LSB = 0x03

I2S format LSB.

enum i2s_driver::i2s_channel_fmt_t

I2S channel format type.

Values:

I2S_CHANNEL_FMT_RIGHT_LEFT = 0x00
I2S_CHANNEL_FMT_ALL_RIGHT
I2S_CHANNEL_FMT_ALL_LEFT
I2S_CHANNEL_FMT_ONLY_RIGHT
I2S_CHANNEL_FMT_ONLY_LEFT
I2S_CHANNEL_FMT_RIGHT_LEFT = 0x00
I2S_CHANNEL_FMT_ALL_RIGHT
I2S_CHANNEL_FMT_ALL_LEFT
I2S_CHANNEL_FMT_ONLY_RIGHT
I2S_CHANNEL_FMT_ONLY_LEFT
enum i2s_driver::i2s_mode_t

I2S Mode, default is I2S_MODE_MASTER.

Values:

I2S_MODE_DISABLED
I2S_MODE_MASTER
I2S_MODE_SLAVE
I2S_MODE_DISABLED
I2S_MODE_MASTER
I2S_MODE_SLAVE
enum i2s_driver::i2s_event_type_t

I2S event types.

Values:

I2S_EVENT_DMA_ERROR
I2S_EVENT_TX_DONE

I2S DMA finish sent 1 buffer

I2S_EVENT_RX_DONE

I2S DMA finish received 1 buffer

I2S_EVENT_MAX

I2S event max index

I2S_EVENT_DMA_ERROR
I2S_EVENT_TX_DONE

I2S DMA finish sent 1 buffer

I2S_EVENT_RX_DONE

I2S DMA finish received 1 buffer

I2S_EVENT_MAX

I2S event max index

enum i2s_driver::i2s_pin_t

I2S pin enable for i2s_set_pin.

Values:

I2S_PIN_BCK_OUT = 0x01

GPIO 15 / TXD2 / D8.

I2S_PIN_WS_OUT = 0x02

GPIO 2 / TXD1 / D4.

I2S_PIN_DATA_OUT = 0x04

GPIO 3 / RXD0 / D9.

I2S_PIN_BC_IN = 0x10

GPIO 13 / RXD2 / D7.

I2S_PIN_WS_IN = 0x20

GPIO 14 / D5.

I2S_PIN_DATA_IN = 0x40

GPIO 12 / D6.

I2S_PIN_BCK_OUT = 0x01

GPIO 15 / TXD2 / D8.

I2S_PIN_WS_OUT = 0x02

GPIO 2 / TXD1 / D4.

I2S_PIN_DATA_OUT = 0x04

GPIO 3 / RXD0 / D9.

I2S_PIN_BC_IN = 0x10

GPIO 13 / RXD2 / D7.

I2S_PIN_WS_IN = 0x20

GPIO 14 / D5.

I2S_PIN_DATA_IN = 0x40

GPIO 12 / D6.

enum i2s_driver::i2s_bits_per_sample_t

I2S bit width per sample.

Values:

I2S_BITS_PER_SAMPLE_8BIT = 8

I2S bits per sample: 8-bits.

I2S_BITS_PER_SAMPLE_16BIT = 16

I2S bits per sample: 16-bits.

I2S_BITS_PER_SAMPLE_24BIT = 24

I2S bits per sample: 24-bits.

I2S_BITS_PER_SAMPLE_8BIT = 8

I2S bits per sample: 8-bits.

I2S_BITS_PER_SAMPLE_16BIT = 16

I2S bits per sample: 16-bits.

I2S_BITS_PER_SAMPLE_24BIT = 24

I2S bits per sample: 24-bits.

enum i2s_driver::i2s_channel_t

I2S channel.

Values:

I2S_CHANNEL_MONO = 1

I2S 1 channel (mono)

I2S_CHANNEL_STEREO = 2

I2S 2 channel (stereo)

I2S_CHANNEL_MONO = 1

I2S 1 channel (mono)

I2S_CHANNEL_STEREO = 2

I2S 2 channel (stereo)

enum i2s_driver::i2s_comm_format_t

I2S communication standard format.

Values:

I2S_COMM_FORMAT_I2S = 0x01

I2S communication format I2S.

I2S_COMM_FORMAT_I2S_MSB = 0x01

I2S format MSB.

I2S_COMM_FORMAT_I2S_LSB = 0x03

I2S format LSB.

I2S_COMM_FORMAT_I2S = 0x01

I2S communication format I2S.

I2S_COMM_FORMAT_I2S_MSB = 0x01

I2S format MSB.

I2S_COMM_FORMAT_I2S_LSB = 0x03

I2S format LSB.

enum i2s_driver::i2s_channel_fmt_t

I2S channel format type.

Values:

I2S_CHANNEL_FMT_RIGHT_LEFT = 0x00
I2S_CHANNEL_FMT_ALL_RIGHT
I2S_CHANNEL_FMT_ALL_LEFT
I2S_CHANNEL_FMT_ONLY_RIGHT
I2S_CHANNEL_FMT_ONLY_LEFT
I2S_CHANNEL_FMT_RIGHT_LEFT = 0x00
I2S_CHANNEL_FMT_ALL_RIGHT
I2S_CHANNEL_FMT_ALL_LEFT
I2S_CHANNEL_FMT_ONLY_RIGHT
I2S_CHANNEL_FMT_ONLY_LEFT
enum i2s_driver::i2s_mode_t

I2S Mode, default is I2S_MODE_MASTER.

Values:

I2S_MODE_DISABLED
I2S_MODE_MASTER
I2S_MODE_SLAVE
I2S_MODE_DISABLED
I2S_MODE_MASTER
I2S_MODE_SLAVE
enum i2s_driver::i2s_event_type_t

I2S event types.

Values:

I2S_EVENT_DMA_ERROR
I2S_EVENT_TX_DONE

I2S DMA finish sent 1 buffer

I2S_EVENT_RX_DONE

I2S DMA finish received 1 buffer

I2S_EVENT_MAX

I2S event max index

I2S_EVENT_DMA_ERROR
I2S_EVENT_TX_DONE

I2S DMA finish sent 1 buffer

I2S_EVENT_RX_DONE

I2S DMA finish received 1 buffer

I2S_EVENT_MAX

I2S event max index

enum i2s_driver::i2s_pin_t

I2S pin enable for i2s_set_pin.

Values:

I2S_PIN_BCK_OUT = 0x01

GPIO 15 / TXD2 / D8.

I2S_PIN_WS_OUT = 0x02

GPIO 2 / TXD1 / D4.

I2S_PIN_DATA_OUT = 0x04

GPIO 3 / RXD0 / D9.

I2S_PIN_BC_IN = 0x10

GPIO 13 / RXD2 / D7.

I2S_PIN_WS_IN = 0x20

GPIO 14 / D5.

I2S_PIN_DATA_IN = 0x40

GPIO 12 / D6.

I2S_PIN_BCK_OUT = 0x01

GPIO 15 / TXD2 / D8.

I2S_PIN_WS_OUT = 0x02

GPIO 2 / TXD1 / D4.

I2S_PIN_DATA_OUT = 0x04

GPIO 3 / RXD0 / D9.

I2S_PIN_BC_IN = 0x10

GPIO 13 / RXD2 / D7.

I2S_PIN_WS_IN = 0x20

GPIO 14 / D5.

I2S_PIN_DATA_IN = 0x40

GPIO 12 / D6.

typedef void (*i2s_callback_t)(void *param, i2s_event_type_t event)

Callback function type.

Note
Function is called in interrupt context, so place in IRAM and keep it brief.

typedef unsigned TickType_t

Defines the wait interval (presently milliseconds)

typedef uint8_t i2s_pin_set_t
typedef void (*i2s_callback_t)(void *param, i2s_event_type_t event)

Callback function type.

Note
Function is called in interrupt context, so place in IRAM and keep it brief.

typedef unsigned TickType_t

Defines the wait interval (presently milliseconds)

typedef uint8_t i2s_pin_set_t
bool i2s_driver_install(const i2s_config_t *config)

Install and start I2S driver.

Note
This function must be called before any I2S driver read/write operations.
Parameters
  • config: I2S configuration
Return Value
  • true: on success, false if already installed or invalid config

void i2s_driver_uninstall()

Uninstall I2S driver.

bool i2s_start()

Start I2S driver.

Note
It is not necessary to call this function after i2s_driver_install() as it is started automatically, unless config.auto_start was set to false.
Return Value
  • bool: true on success, false if driver not initialised

bool i2s_stop()

Stop I2S driver.

Note
Disables I2S TX/RX, until i2s_start() is called
Return Value
  • bool: true on success, false if driver not initialised

bool i2s_set_sample_rates(uint32_t rate)

Parameters
  • rate: Sample rate in Hz (ex 44100, 48000) for TX/RX

bool i2s_set_dividers(uint8_t bck_div, uint8_t mclk_div)

Direct control over output rate

float i2s_get_real_rate()

Return Value
  • float: The actual Sample Rate on output

bool i2s_dma_read(i2s_buffer_info_t *info, size_t max_bytes)

Fetch a DMA buffer containing received data (zero-copy)

Note
On success, info->buffer specifies where to read the data from, and info->size how many bytes are actually available (always > 0).
Note
Returns at most one DMA buffer
Parameters
  • info: Pointer to structure to receive buffer information
  • max_bytes: Number of bytes to read
Return Value
  • bool: true on success, false if no data available or info is null

bool i2s_dma_write(i2s_buffer_info_t *info, size_t max_bytes)

Fetch a DMA buffer for direct writing (zero-copy)

Note
On success, info->buffer specifies where to write the data, and info->size how many bytes should be written - may be less than max_bytes, but always > 0.
Note
Returns at most one DMA buffer
Parameters
  • info: Pointer to structure to receive buffer information
  • max_bytes: Number of bytes required in buffer
Return Value
  • bool: true on success, false if buffer unavailable or info is null

size_t i2s_write(const void *src, size_t size, TickType_t ticks_to_wait)

writes a buffer of frames into the DMA memory, returns the amount of frames written.

Note
Data is copied into DMA buffers
Parameters
  • src: Data to write
  • size: Size in bytes
  • ticks_to_wait: Wait timeout in ticks
Return Value
  • size_t: Data actually written, may be less than size

size_t i2s_read(void *dest, size_t size, TickType_t ticks_to_wait)

Reads a block of received data.

Parameters
  • dest: Buffer to store data
  • size: Max. bytes to read
  • ticks_to_wait: Wait timeout in ticks
Return Value
  • size_t: Number of bytes read

bool i2s_zero_dma_buffer()

Zero the contents of the TX DMA buffer.

Note
Pushes zero-byte samples into the TX DMA buffer, until it is full

void i2s_set_pins(i2s_pin_set_t pins, bool enable)

Configure I2S pins.

You can alternatively use arduino functions.

Note
Call this after initialising driver to specify which pins are required
Parameters
  • pins: Mask of i2s_pin_t values
  • enable: true to enable for I2S use, false to revert to GPIO

Example: i2s_set_pins(_BV(I2S_BCK_OUT), true)

bool i2s_enable_loopback(bool enable)
bool i2s_stat_tx(i2s_buffer_stat_t *stat)

Obtain state information for TX buffers.

Parameters
  • stat:
Return Value
  • bool: true on success

bool i2s_stat_rx(i2s_buffer_stat_t *stat)

Obtain state information for RX buffers.

Parameters
  • stat:
Return Value
  • bool: true on success

union i2s_sample_t
#include <i2s.h>

I2S sample.

An I2S frame can contain various types of data:

8-bit, 16-bit or 24-bit mono samples 8-bit or 16-bit stereo samples

Public Members

uint32_t u32
int16_t left
int16_t right
struct i2s_sample_t::[anonymous] [anonymous]
struct i2s_sample_t::[anonymous] [anonymous]
struct i2s_module_config_t
#include <i2s.h>

I2S module configuration (TX or RX)

Public Members

i2s_mode_t mode

I2S work mode (combination of i2s_mode_t)

i2s_bits_per_sample_t bits_per_sample

I2S bits per sample.

i2s_channel_fmt_t channel_format

I2S channel format.

i2s_comm_format_t communication_format

I2S communication format.

uint16_t dma_buf_len

I2S DMA Buffer Length (in samples)

uint8_t dma_buf_count

I2S DMA Buffer Count.

uint8_t callback_threshold

TX: callback when available buffers > threshold RX: Callback when slc_queue_len > threshold

struct i2s_config_t
#include <i2s.h>

I2S configuration parameters.

Public Members

i2s_module_config_t tx

TX module configuration.

i2s_module_config_t rx

RX module configuration.

unsigned sample_rate

I2S sample rate.

bool tx_desc_auto_clear

I2S auto clear tx descriptor if there is underflow condition (Mutes output)

bool auto_start

Start immediately on successful initialisation.

i2s_callback_t callback

Callback handler.

void *param

Callback parameter.

uint8_t bits_mod

Evaluate what this does (4 bits)

struct i2s_buffer_info_t
#include <i2s.h>

Defines a buffer with available content.

Public Members

void *buffer
i2s_sample_t *samples
union i2s_buffer_info_t::[anonymous] [anonymous]
size_t size

Available space (TX) or data (RX) in bytes.

uint16_t buf
uint16_t pos
union i2s_buffer_info_t::[anonymous] [anonymous]
struct i2s_buffer_stat_t
#include <i2s.h>

Contains I2S buffer status information.

Note
Size excludes buffer in use by DMA

Public Members

uint16_t size
uint16_t used
OS Timer
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

PWM: Pulse-Width Modulation

The driver interface is defined in the ESP8266 SDK.

Build variables
ENABLE_CUSTOM_PWM
undefined
use the Espressif PWM driver
1 (default)
Use the New PWM driver, a drop-in replacement for the version provided in the Espressif SDK.
API Documentation
void pwm_init(uint32 period, uint32 *duty, uint32 pwm_channel_num, uint32 (*pin_info_list)[3])

Initialize PWM function, including GPIO selection, period and duty cycle.

Example:

    uint32 io_info[][3] = {
        {PWM_0_OUT_IO_MUX, PWM_0_OUT_IO_FUNC, PWM_0_OUT_IO_NUM},
        {PWM_1_OUT_IO_MUX, PWM_1_OUT_IO_FUNC, PWM_1_OUT_IO_NUM},
        {PWM_2_OUT_IO_MUX, PWM_2_OUT_IO_FUNC, PWM_2_OUT_IO_NUM}
};

pwm_init(light_param.pwm_period, light_param.pwm_duty, 3, io_info);
Note
This API can be called only once.
Parameters
  • period: PWM period
  • duty: duty cycle of each output
  • pwm_channel_num: PWM channel number
  • pin_info_list: Array containing an entry for each channel giving

void pwm_start(void)

Starts PWM.

This function needs to be called after PWM configuration is changed.

void pwm_set_duty(uint32 duty, uint8 channel)

Sets duty cycle of a PWM output.

Set the time that high-level signal will last. The range of duty depends on PWM period. Its maximum value of which can be Period * 1000 / 45.

Parameters
  • duty: The time that high-level single will last, duty cycle will be (duty*45)/(period*1000)
  • channel: PWM channel, which depends on how many PWM channels are used

For example, for 1-KHz PWM, the duty range is 0 ~ 22222.

uint32 pwm_get_duty(uint8 channel)

Get duty cycle of PWM output.

Duty cycle will be (duty*45) / (period*1000).

Parameters
  • channel: PWM channel, which depends on how many PWM channels are used
Return Value
  • uint32: Duty cycle of PWM output

void pwm_set_period(uint32 period)

Set PWM period.

Parameters
  • period: PWM period in us. For example, 1-KHz PWM period = 1000us.

uint32 pwm_get_period(void)

Get PWM period.

Return Value
  • uint32: Return PWM period in us.

uint32 get_pwm_version(void)

Get version information of PWM.

Return Value
  • uint32: PWM version

UART: Universal Asynchronous Receive/Transmit

Custom asynchronous driver.

enum uart_driver::smg_uart_mode_

values for mode argument of uart_init

Values:

UART_FULL

Both receive and transmit - will revert to TX only if RX not supported.

UART_RX_ONLY

Receive only.

UART_TX_ONLY

Transmit only.

UART_FULL

Both receive and transmit - will revert to TX only if RX not supported.

UART_RX_ONLY

Receive only.

UART_TX_ONLY

Transmit only.

enum uart_driver::smg_uart_option_bits_t

bit values for options argument of uart_init

Note
use _BV(opt) to specify values

Values:

UART_OPT_TXWAIT

If buffers are full then uart_write() will wait for free space.

UART_OPT_CALLBACK_RAW

ISR invokes user callback function with no pre-processing.

UART_OPT_TXWAIT

If buffers are full then uart_write() will wait for free space.

UART_OPT_CALLBACK_RAW

ISR invokes user callback function with no pre-processing.

enum uart_driver::smg_uart_notify_code_t

Indicates notification, parameters refer to uart_notify_info_t structure.

Values:

UART_NOTIFY_AFTER_OPEN

Called when uart has been initialised successfully.

UART_NOTIFY_BEFORE_CLOSE

Called immediately before uart is closed and destroyed.

UART_NOTIFY_AFTER_WRITE

Called after data has been written into tx buffer.

UART_NOTIFY_BEFORE_READ

Called before data is read from rx buffer.

UART_NOTIFY_WAIT_TX

Called to ensure all buffered data is output.

UART_NOTIFY_AFTER_OPEN

Called when uart has been iniitialised successfully.

UART_NOTIFY_BEFORE_CLOSE

Called immediately before uart is closed and destroyed.

UART_NOTIFY_AFTER_WRITE

Called after data has been written into tx buffer.

UART_NOTIFY_BEFORE_READ

Called before data is read from rx buffer.

UART_NOTIFY_WAIT_TX

Called to ensure all buffered data is output.

enum uart_driver::smg_uart_mode_

values for mode argument of uart_init

Values:

UART_FULL

Both receive and transmit - will revert to TX only if RX not supported.

UART_RX_ONLY

Receive only.

UART_TX_ONLY

Transmit only.

UART_FULL

Both receive and transmit - will revert to TX only if RX not supported.

UART_RX_ONLY

Receive only.

UART_TX_ONLY

Transmit only.

enum uart_driver::smg_uart_option_bits_t

bit values for options argument of uart_init

Note
use _BV(opt) to specify values

Values:

UART_OPT_TXWAIT

If buffers are full then uart_write() will wait for free space.

UART_OPT_CALLBACK_RAW

ISR invokes user callback function with no pre-processing.

UART_OPT_TXWAIT

If buffers are full then uart_write() will wait for free space.

UART_OPT_CALLBACK_RAW

ISR invokes user callback function with no pre-processing.

enum uart_driver::smg_uart_notify_code_t

Indicates notification, parameters refer to uart_notify_info_t structure.

Values:

UART_NOTIFY_AFTER_OPEN

Called when uart has been initialised successfully.

UART_NOTIFY_BEFORE_CLOSE

Called immediately before uart is closed and destroyed.

UART_NOTIFY_AFTER_WRITE

Called after data has been written into tx buffer.

UART_NOTIFY_BEFORE_READ

Called before data is read from rx buffer.

UART_NOTIFY_WAIT_TX

Called to ensure all buffered data is output.

UART_NOTIFY_AFTER_OPEN

Called when uart has been iniitialised successfully.

UART_NOTIFY_BEFORE_CLOSE

Called immediately before uart is closed and destroyed.

UART_NOTIFY_AFTER_WRITE

Called after data has been written into tx buffer.

UART_NOTIFY_BEFORE_READ

Called before data is read from rx buffer.

UART_NOTIFY_WAIT_TX

Called to ensure all buffered data is output.

typedef enum smg_uart_mode_ smg_uart_mode_t
typedef uint8_t uart_options_t
typedef struct smg_uart_ smg_uart_t
typedef void (*smg_uart_callback_t)(smg_uart_t *uart, uint32_t status)

callback invoked directly from ISR

Errors can be detected via uart_get_status().

Note
Values can be: UIFE: TX FIFO Empty UIFF: RX FIFO Full UITO: RX FIFO Timeout UIBD: Break Detected
Parameters
  • arg: the UART object
  • status: UART USIS STATUS flag bits indicating cause of interrupt

typedef void (*smg_uart_notify_callback_t)(smg_uart_t *uart, smg_uart_notify_code_t code)

Port notification callback function type.

Parameters
  • info:
Return Value
  • bool: true if callback handled operation, false to default to normal operation

typedef enum smg_uart_mode_ smg_uart_mode_t
typedef uint8_t uart_options_t
typedef struct smg_uart_ smg_uart_t
typedef void (*smg_uart_callback_t)(smg_uart_t *uart, uint32_t status)

callback invoked directly from ISR

Errors can be detected via uart_get_status().

Note
Values can be: UIFE: TX FIFO Empty UIFF: RX FIFO Full UITO: RX FIFO Timeout UIBD: Break Detected
Parameters
  • arg: the UART object
  • status: UART USIS STATUS flag bits indicating cause of interrupt

typedef void (*smg_uart_notify_callback_t)(smg_uart_t *uart, smg_uart_notify_code_t code)

Port notification callback function type.

Parameters
  • info:
Return Value
  • bool: true if callback handled operation, false to default to normal operation

bool smg_uart_set_notify(unsigned uart_nr, smg_uart_notify_callback_t callback)

Set the notification callback function.

Parameters
  • uart_nr: Which uart to register notifications for
  • callback:
Return Value
  • bool: true on success

smg_uart_t *smg_uart_init(uint8_t uart_nr, uint32_t baudrate, uint32_t config, smg_uart_mode_t mode, uint8_t tx_pin, size_t rx_size, size_t tx_size = 0)
smg_uart_t *smg_uart_init_ex(const smg_uart_config &cfg)
void smg_uart_uninit(smg_uart_t *uart)
int smg_uart_get_nr(smg_uart_t *uart)
smg_uart_t *smg_uart_get_uart(uint8_t uart_nr)

Get the uart structure for the given number.

Parameters
  • uart_nr:
Return Value
  • uart_t*: Returns nullptr if uart isn’t initialised

void smg_uart_set_callback(smg_uart_t *uart, smg_uart_callback_t callback, void *param)

Set callback handler for serial port.

Parameters
  • uart:
  • callback: specify nullptr to disable callbacks
  • param: user parameter passed to callback

void *smg_uart_get_callback_param(smg_uart_t *uart)

Get the callback parameter specified by uart_set_callback()

Parameters
  • uart:
Return Value
  • void*: the callback parameter

static void smg_uart_set_options(smg_uart_t *uart, uart_options_t options)

Set option flags.

Parameters
  • uart:
  • options: The option(s) to set

uint8_t smg_uart_get_status(smg_uart_t *uart)

Get error flags and clear them.

Note
To detect errors during a transaction, call at the start to clear the flags, then check the value at the end. Only these values are cleared/returned: UIBD: Break Detected UIOF: RX FIFO OverFlow UIFR: Frame Error UIPE: Parity Error
Parameters
  • uart:
Return Value
  • Status: error bits:

static uart_options_t smg_uart_get_options(smg_uart_t *uart)
void smg_uart_swap(smg_uart_t *uart, int tx_pin)
void smg_uart_set_tx(smg_uart_t *uart, int tx_pin)
void smg_uart_set_pins(smg_uart_t *uart, int tx, int rx)
bool smg_uart_tx_enabled(smg_uart_t *uart)
bool smg_uart_rx_enabled(smg_uart_t *uart)
uint32_t smg_uart_set_baudrate_reg(int uart_nr, uint32_t baud_rate)

set UART baud rate, given the UART number

Parameters
  • uart_nr:
  • baud_rate: requested baud rate
Return Value
  • uint32_t: actual baudrate used, 0 on failure

uint32_t smg_uart_set_baudrate(smg_uart_t *uart, uint32_t baud_rate)

set UART baud rate

Parameters
  • uart:
  • baud_rate: requested baud rate
Return Value
  • uint32_t: actual baudrate used, 0 on failure

uint32_t smg_uart_get_baudrate(smg_uart_t *uart)

get the actual baud rate in use

Parameters
  • uart:
Return Value
  • uint32_t: the baud rate, 0 on failure

size_t smg_uart_resize_rx_buffer(smg_uart_t *uart, size_t new_size)
size_t smg_uart_rx_buffer_size(smg_uart_t *uart)
size_t smg_uart_resize_tx_buffer(smg_uart_t *uart, size_t new_size)
size_t smg_uart_tx_buffer_size(smg_uart_t *uart)
size_t smg_uart_write(smg_uart_t *uart, const void *buffer, size_t size)

write a block of data

Parameters
  • uart:
  • buffer:
  • size:
Return Value
  • size_t: number of bytes buffered for transmission

static size_t smg_uart_write_char(smg_uart_t *uart, char c)

queue a single character for output

Parameters
  • uart:
  • c:
Return Value
  • size_t: 1 if character was written, 0 on failure

size_t smg_uart_read(smg_uart_t *uart, void *buffer, size_t size)

read a block of data

Parameters
  • uart:
  • buffer: where to write the data
  • size: requested quantity of bytes to read
Return Value
  • size_t: number of bytes read

static int smg_uart_read_char(smg_uart_t *uart)

read a received character

Parameters
  • uart:
Return Value
  • the: character, -1 on failure

int smg_uart_peek_char(smg_uart_t *uart)

see what the next character in the rx buffer is

Note
if buffer isn’t allocated data may be in the hardware FIFO, which must be read out using uart_read()
Parameters
  • uart:
Return Value
  • int: returns -1 if buffer is empty or not allocated

int smg_uart_peek_last_char(smg_uart_t *uart)

fetch last character read out of FIFO

Note
this is only useful if an rx buffer has been allocated of sufficient size to contain a message. This function then indicates the terminating character.
Parameters
  • uart:
Return Value
  • int: the character, or -1 if rx buffer is empty or unallocated

int smg_uart_rx_find(smg_uart_t *uart, char c)
size_t smg_uart_rx_available(smg_uart_t *uart)

determine available data which can be read

Note
this obtains a count of data both in the memory buffer and hardware FIFO
Parameters
  • uart:
Return Value
  • size_t:

size_t smg_uart_tx_free(smg_uart_t *uart)

return free space in transmit buffer

void smg_uart_wait_tx_empty(smg_uart_t *uart)

void smg_uart_set_break(smg_uart_t *uart, bool state)

Set or clear a break condition on the TX line.

Parameters
  • uart:
  • state:

void smg_uart_flush(smg_uart_t *uart, smg_uart_mode_t mode = UART_FULL)

discard any buffered data and reset hardware FIFOs

Note
this function does not wait for any transmissions to complete
Parameters
  • uart:
  • mode: Whether to flush TX, RX or both (the default)

void smg_uart_set_debug(int uart_nr)
int smg_uart_get_debug()
void smg_uart_start_isr(smg_uart_t *uart)

enable interrupts for a UART

Parameters
  • uart:

void smg_uart_stop_isr(smg_uart_t *uart)

disable interrupts for a UART

Parameters
  • uart:

void smg_uart_detach(int uart_nr)

detach a UART interrupt service routine

Parameters
  • uart_nr:

void smg_uart_detach_all()

detach all UART interrupt service routines

Note
call at startup to put all UARTs into a known state

uint8_t smg_uart_disable_interrupts()

disable interrupts and return current interrupt state

Return Value
  • state: non-zero if any UART interrupts were active

void smg_uart_restore_interrupts()

re-enable interrupts after calling uart_disable_interrupts()

UART0
UART1
UART2

Virtualised UART0.

UART_NO

No UART specified.

UART_PHYSICAL_COUNT

Number of physical UARTs on the system.

UART_COUNT

Number of UARTs on the system, virtual or otherwise.

UART_NB_BIT_MASK
UART_NB_BIT_5
UART_NB_BIT_6
UART_NB_BIT_7
UART_NB_BIT_8
UART_PARITY_MASK
UART_PARITY_NONE
UART_PARITY_EVEN
UART_PARITY_ODD
UART_NB_STOP_BIT_MASK
UART_NB_STOP_BIT_0
UART_NB_STOP_BIT_1
UART_NB_STOP_BIT_15
UART_NB_STOP_BIT_2
UART_5N1
UART_6N1
UART_7N1
UART_8N1
UART_5N2
UART_6N2
UART_7N2
UART_8N2
UART_5E1
UART_6E1
UART_7E1
UART_8E1
UART_5E2
UART_6E2
UART_7E2
UART_8E2
UART_5O1
UART_6O1
UART_7O1
UART_8O1
UART_5O2
UART_6O2
UART_7O2
UART_8O2
UART_RX_FIFO_SIZE
UART_TX_FIFO_SIZE
UART0
UART1
UART2

Virtualised UART0.

UART_NO

No UART specified.

UART_PHYSICAL_COUNT

Number of physical UARTs on the system.

UART_COUNT

Number of UARTs on the system, virtual or otherwise.

UART_NB_BIT_MASK
UART_NB_BIT_5
UART_NB_BIT_6
UART_NB_BIT_7
UART_NB_BIT_8
UART_PARITY_MASK
UART_PARITY_NONE
UART_PARITY_EVEN
UART_PARITY_ODD
UART_NB_STOP_BIT_MASK
UART_NB_STOP_BIT_0
UART_NB_STOP_BIT_1
UART_NB_STOP_BIT_15
UART_NB_STOP_BIT_2
UART_5N1
UART_6N1
UART_7N1
UART_8N1
UART_5N2
UART_6N2
UART_7N2
UART_8N2
UART_5E1
UART_6E1
UART_7E1
UART_8E1
UART_5E2
UART_6E2
UART_7E2
UART_8E2
UART_5O1
UART_6O1
UART_7O1
UART_8O1
UART_5O2
UART_6O2
UART_7O2
UART_8O2
UART_RX_FIFO_SIZE
UART_TX_FIFO_SIZE
struct smg_uart_
#include <uart.h>

Public Members

uint8_t uart_nr
uint32_t baud_rate
smg_uart_mode_t mode
uint8_t options
uint8_t rx_pin
uint8_t tx_pin
uint8_t rx_headroom

Callback when rx_buffer free space <= headroom.

uint16_t status

All status flags reported to callback since last uart_get_status() call.

struct SerialBuffer *rx_buffer

Optional receive buffer.

struct SerialBuffer *tx_buffer

Optional transmit buffer.

smg_uart_callback_t callback

Optional User callback routine.

void *param

User-supplied callback parameter.

uart_port_t uart_nr
struct smg_uart_config
#include <uart.h>

Public Members

uint8_t uart_nr
uint8_t tx_pin

Specify 2 for alternate pin, otherwise defaults to pin 1.

smg_uart_mode_t mode

Whether to enable receive, transmit or both.

uart_options_t options
uint32_t baudrate

Requested baudrate; actual baudrate may differ.

uint32_t config

UART CONF0 register bits.

size_t rx_size
size_t tx_size
uint8_t rx_pin
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
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
Esptool

This Component provides Espressif’s tool for reading and writing firmware and other data to hardware.

Options
SPI_SPEED

Clock speed for flash memory (20, 26, 40 or 80). Default is 40.

SPI_MODE

Flash memory operating mode (quot, dio, dout, qio). Default is qio.

SPI_SIZE

Size of flash memory chip (256KB, 512KB, 1MB, 2MB, 4MB). Default is 512K bytes.

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

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.

Manual Installation

Manual installation allows you to run the latest development version from this repository.

esptool.py depends on pySerial version 3.0 or newer for serial communication with the target device.

If you choose to install esptool.py system-wide by running python setup.py install, then this will be taken care of automatically.

If not using setup.py, then you’ll have to install pySerial manually by running something like pip install pyserial, easy_install pyserial or apt-get install python-serial, depending on your platform. (The official pySerial installation instructions are here).

esptool.py also bundles the pyaes & ecdsa Python modules as “vendored” libraries. These modules are required when using the ESP32-only espsecure.py and espefuse.py tools. If you install esptool.py via pip or setup.py as shown above, then versions of these libraries will be installed from pypi. If you run esptool.py from the repository directory directly, it will use the “vendored” versions.

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.

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
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
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
  • SPIFlashInfo: Information block

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
INTERNAL_FLASH_START_ADDRESS
struct STORE_TYPEDEF_ATTR
#include <esp_spi_flash.h>

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

Public Types

enum [anonymous]

Values:

MODE_QIO = 0
MODE_QOUT = 1
MODE_DIO = 2
MODE_DOUT = 15
enum [anonymous]

Values:

SPEED_40MHZ = 0
SPEED_26MHZ = 1
SPEED_20MHZ = 2
SPEED_80MHZ = 15
enum [anonymous]

Values:

SIZE_4MBIT = 0
SIZE_2MBIT = 1
SIZE_8MBIT = 2
SIZE_16MBIT = 3
SIZE_32MBIT = 4
enum [anonymous]

Values:

MODE_QIO = 0
MODE_QOUT = 1
MODE_DIO = 2
MODE_DOUT = 15
enum [anonymous]

Values:

SPEED_40MHZ = 0
SPEED_26MHZ = 1
SPEED_20MHZ = 2
SPEED_80MHZ = 15
enum [anonymous]

Values:

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

Public Members

uint8_t unknown0
uint8_t unknown1
STORE_TYPEDEF_ATTR::[anonymous] mode
STORE_TYPEDEF_ATTR::[anonymous] speed
STORE_TYPEDEF_ATTR::[anonymous] size
STORE_TYPEDEF_ATTR::[anonymous] mode
STORE_TYPEDEF_ATTR::[anonymous] speed
STORE_TYPEDEF_ATTR::[anonymous] size
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

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

References
Used by

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

Public Types

template<>
using Clock = Timer1Clock<clkdiv>
template<>
using TickType = uint32_t
template<>
using TimeType = uint32_t

Public Functions

Timer1Api()
~Timer1Api()
String name() const
String toString() const
operator String() const

Public Static Functions

static constexpr const char *typeName()
static constexpr TickType minTicks()
static constexpr TickType maxTicks()
static TickType ticks()
static void setCallback(TimerCallback callback, void *arg)
static void setInterval(TickType interval)
static TickType getInterval()
static bool isArmed()
static void arm(bool repeating)
static void disarm()
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.

Public Types

template<>
using Clock = OsTimerApi::Clock
template<>
using TickType = uint64_t
template<>
using TimeType = uint64_t

Public Functions

OsTimer64Api()
~OsTimer64Api()
bool isArmed() const
TickType ticks() const
void setCallback(TimerCallback callback, void *arg)
void setCallback(TimerDelegate delegateFunction)
void setInterval(TickType interval)
TickType getInterval() const
void arm(bool repeating)
void disarm()
String name() const
String toString() const
operator String() const

Public Members

TimerCallback func = nullptr
void *arg = nullptr

Public Static Functions

static constexpr const char *typeName()
static constexpr TickType minTicks()
static constexpr TickType maxTicks()
template <typename TimerApi>
class DelegateCallbackTimer : public CallbackTimer<TimerApi>
#include <Timer.h>

Class template adding delegate callback method support to the basic CallbackTimer template.

Public Types

template<>
using Millis = NanoTime::TimeSource<Clock, NanoTime::Milliseconds, uint32_t>
template<>
using Micros = NanoTime::TimeSource<Clock, NanoTime::Microseconds, TimeType>

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

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)

class Timer : public DelegateCallbackTimer<OsTimer64Api<Timer>>
#include <Timer.h>

Callback timer class.

Subclassed by Profiling::MinMaxTimes< Timer >

Public Types

template<>
using Millis = NanoTime::TimeSource<Clock, NanoTime::Milliseconds, uint32_t>
template<>
using Micros = NanoTime::TimeSource<Clock, NanoTime::Microseconds, TimeType>

Public Functions

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

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

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

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

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)

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

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

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

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)

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

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

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

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

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

void setInterval()

Set timer interval in timer ticks (static check)

Note
On error, compilation fails with error message
Template Parameters
  • ticks: Interval in ticks

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

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.

void setIntervalUs()

Set timer interval in microseconds (static check)

bool setIntervalMs(uint32_t milliseconds)

Set timer interval in milliseconds.

void setIntervalMs()

Set timer interval in milliseconds (static check)

Public Static Functions

static constexpr Millis millis()

Get a millisecond time source.

static constexpr Micros micros()

Get a microsecond time source.

static constexpr uint64_t usToTicks()

Convert microsecond count into timer ticks.

static TickType usToTicks(TimeType time)

Convert microsecond count into timer ticks.

static constexpr uint64_t ticksToUs()

Convert timer ticks into microseconds.

static TimeType ticksToUs(TickType ticks)

Convert timer ticks into microseconds.

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

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

static constexpr void checkIntervalMs()

Check timer interval in milliseconds is valid (static check)

static constexpr void checkIntervalUs()

Check timer interval in microseconds is valid (static check)

Protected Types

template<>
using Clock = OsTimerApi::Clock
template<>
using TickType = uint64_t
template<>
using TimeType = uint64_t

Protected Functions

bool isArmed() const
TickType ticks() const
void arm(bool repeating)
void disarm()
String name() const
String toString() const
operator String() const

Protected Attributes

TimerCallback func
void *arg

Protected Static Functions

static constexpr const char *typeName()
static constexpr TickType minTicks()
static constexpr TickType maxTicks()
class AutoDeleteTimer : public DelegateCallbackTimer<OsTimer64Api<AutoDeleteTimer>>
#include <Timer.h>

Auto-delete callback timer class.

Public Types

template<>
using Millis = NanoTime::TimeSource<Clock, NanoTime::Milliseconds, uint32_t>
template<>
using Micros = NanoTime::TimeSource<Clock, NanoTime::Microseconds, TimeType>

Public Functions

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

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

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

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

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)

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

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

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

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)

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

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

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

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

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

void setInterval()

Set timer interval in timer ticks (static check)

Note
On error, compilation fails with error message
Template Parameters
  • ticks: Interval in ticks

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

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.

void setIntervalUs()

Set timer interval in microseconds (static check)

bool setIntervalMs(uint32_t milliseconds)

Set timer interval in milliseconds.

void setIntervalMs()

Set timer interval in milliseconds (static check)

Public Static Functions

static constexpr Millis millis()

Get a millisecond time source.

static constexpr Micros micros()

Get a microsecond time source.

static constexpr uint64_t usToTicks()

Convert microsecond count into timer ticks.

static TickType usToTicks(TimeType time)

Convert microsecond count into timer ticks.

static constexpr uint64_t ticksToUs()

Convert timer ticks into microseconds.

static TimeType ticksToUs(TickType ticks)

Convert timer ticks into microseconds.

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

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

static constexpr void checkIntervalMs()

Check timer interval in milliseconds is valid (static check)

static constexpr void checkIntervalUs()

Check timer interval in microseconds is valid (static check)

Protected Types

template<>
using Clock = OsTimerApi::Clock
template<>
using TickType = uint64_t
template<>
using TimeType = uint64_t

Protected Functions

bool isArmed() const
TickType ticks() const
void arm(bool repeating)
void disarm()
String name() const
String toString() const
operator String() const

Protected Attributes

TimerCallback func
void *arg

Protected Static Functions

static constexpr const char *typeName()
static constexpr TickType minTicks()
static constexpr TickType maxTicks()
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.

Public Types

using Clock = Timer2Clock
using TickType = uint32_t
using TimeType = uint32_t

Public Functions

bool isArmed() const
TickType ticks() const
~OsTimerApi()
void setCallback(TimerCallback callback, void *arg)
void setInterval(TickType interval)
TickType getInterval() const
void arm(bool repeating)
void disarm()
String name() const
String toString() const
operator String() const

Public Static Functions

static constexpr const char *typeName()
static constexpr TickType minTicks()
static constexpr TickType maxTicks()
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 Types

template<>
using Millis = NanoTime::TimeSource<Clock, NanoTime::Milliseconds, uint32_t>
template<>
using Micros = NanoTime::TimeSource<Clock, NanoTime::Microseconds, TimeType>

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<Timer2Clock, unit>
using PeriodicElapseTimer = PolledTimer::Periodic<Timer2Clock, unit>
using OneShotFastMs = OneShotElapseTimer<NanoTime::Milliseconds>
using OneShotFastUs = OneShotElapseTimer<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 OneShotFastUs oneShotFastUs

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 PolledTimer::OneShot = typedef Timer<Clock, unit, false, uint32_t>
using PolledTimer::Periodic = typedef 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 Types

template<>
using TickType = typename Clock::TickType
template<>
using Margin = NanoTime::TimeConst<Clock, NanoTime::Microseconds, POLLED_TIMER_MARGIN_US>
template<>
using Clock = Clock
template<>
using TimeType = TimeType
template<>
using TicksPerUnit = typename Clock::template TicksPerUnit<unit_>
template<>
using TimeConst = TimeConst<Clock, unit_, time>
template<>
using TicksConst = TicksConst<Clock, ticks>
template<>
using MaxClockTime = typename Clock::template MaxTime<unit_>

Get the time corresponding to the maximum clock tick value.

template<>
using TimeSource = TimeSource<Clock, unit, TimeType>
template<>
using Ticks = Ticks<Clock, T>
template<>
using MaxTicks = TicksConst<maxTicks_>
template<>
using MaxTime = typename MaxTicks::template TimeConst<unit>

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 canExpire() const
bool canWait() const
bool expired()

Determine if timer has expired.

Return Value
  • bool:

Public Static Functions

static constexpr NanoTime::Unit unit()
static constexpr TickType maxInterval()
static constexpr Margin margin()
static constexpr BasicRatio32 ticksPerUnit()

Number of clock ticks per unit of time.

Return Value
  • BasicRatio32: Result as a rational fraction

static Ratio32 ticksPerUnit(Unit unit)

Get ticks per unit as a Ratio object.

Return Value
  • BasicRatio32:

static constexpr MaxClockTime maxClockTime()
static constexpr TimeConst<time> timeConst()

Obtain a TimeConst type representing the given time quantity.

Note
Use methods of TimeConst to obtain corresponding tick count, etc.
Template Parameters
  • time:
Return Value
  • TimeConst:

static constexpr TicksConst<ticks> ticksConst()

Class template defining a fixed tick quantity.

Note
Use methods of TickConst to obtain corresponding time values, etc.
Template Parameters
  • ticks:
Return Value
  • TicksConst:

static constexpr Time<TimeType> maxCalcTime()

The maximum time value supported by timeToTicks without overflowing.

Return Value
  • TimeType: Passing values larger than this to timeToTicks() will truncate at maximum value

static constexpr Ticks<Clock, TimeType> maxCalcTicks()

The maximum tick value supported by ticksToTime without overflowing.

Return Value
  • TimeType: Passing values larger than this to ticksToTime() will truncate at maximum value

static Ticks<Clock, TimeType> timeToTicks(TimeType time)

Get the number of ticks for a given time.

Parameters
  • time:
Return Value
  • TimeType: Tick count, rounded to the nearest tick

static constexpr uint64_t timeToTicks()

Get the number of ticks for a given time.

Template Parameters
  • time:
Return Value
  • uint64_t: Tick count, rounded to the nearest tick

static constexpr uint64_t ticksToTime()

Get the time for a given number of clock ticks.

Template Parameters
  • ticks:
Return Value
  • TimeType: Time count, rounded to the nearest unit

static Time<TimeType> ticksToTime(TimeType ticks)

Get the time for a given number of clock ticks.

Parameters
  • ticks:
Return Value
  • TimeType: Time count, rounded to the nearest unit

static String toString()
static constexpr const char *typeName()
static constexpr uint32_t frequency()
static constexpr MaxTicks maxTicks()
template <Unit unit>
static constexpr MaxTime<unit> maxTime()
template <Unit unit, typename TimeType>
static constexpr TimeSource<unit, TimeType> timeSource()

Create a Time Source for this Clock.

Template Parameters
  • unit:
  • TimeType:

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

Public Types

template<>
using TickType = uint32_t
template<>
using TicksConst = TicksConst<Clock, ticks>
template<>
using TimeConst = TimeConst<Clock, unit, time>
template<>
using TicksPerUnit = std::ratio_divide<std::ratio<frequency_>, UnitTickRatio<unit>>
template<>
using TimeSource = TimeSource<Clock, unit, TimeType>
template<>
using Ticks = Ticks<Clock, T>
template<>
using MaxTicks = TicksConst<maxTicks_>
template<>
using MaxTime = typename MaxTicks::template TimeConst<unit>

Public Static Functions

static constexpr uint32_t prescale()
static constexpr const char *typeName()
static uint32_t ticks()
static constexpr uint32_t frequency()
static constexpr MaxTicks maxTicks()
static constexpr MaxTime<unit> maxTime()
static Ratio32 ticksPerUnit(Unit unit)

Get ticks per unit as a Ratio object.

Return Value
  • BasicRatio32:

static constexpr TimeConst<unit, time> timeConst()

Class template defining a fixed time quantity.

Template Parameters
  • time:
Return Value
  • TimeConst:

static constexpr TicksConst<ticks> ticksConst()

Class template defining a fixed tick quantity.

Template Parameters
  • ticks:
Return Value
  • TicksConst<Clockticks>:

static constexpr TimeSource<unit, TimeType> timeSource()

Create a Time Source for this Clock.

Template Parameters
  • unit:
  • TimeType:

static Ticks<TimeType> timeToTicks(TimeType time)

Get the number of ticks for a given time.

Parameters
  • time:
Return Value
  • TimeType: Tick count, rounded to the nearest tick

static Time<TimeType> ticksToTime(TimeType ticks)

Get the time for a given number of clock ticks.

Parameters
  • ticks:
Return Value
  • TimeType: Time count, rounded to the nearest unit

static String toString()
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 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 >

Public Types

template<>
using TickType = TickType_
template<>
using TicksConst = TicksConst<Clock, ticks>
template<>
using TimeConst = TimeConst<Clock, unit, time>
template<>
using TicksPerUnit = std::ratio_divide<std::ratio<frequency_>, UnitTickRatio<unit>>
template<>
using TimeSource = TimeSource<Clock, unit, TimeType>
template<>
using Ticks = Ticks<Clock, T>
template<>
using MaxTicks = TicksConst<maxTicks_>
template<>
using MaxTime = typename MaxTicks::template TimeConst<unit>

Public Static Functions

static constexpr const char *typeName()
static constexpr uint32_t frequency()
static constexpr MaxTicks maxTicks()
template <Unit unit>
static constexpr MaxTime<unit> maxTime()
static Ratio32 ticksPerUnit(Unit unit)

Get ticks per unit as a Ratio object.

Return Value
  • BasicRatio32:

template <Unit unit, uint64_t time>
static constexpr TimeConst<unit, time> timeConst()

Class template defining a fixed time quantity.

Template Parameters
  • time:
Return Value

template <uint64_t ticks>
static constexpr TicksConst<ticks> ticksConst()

Class template defining a fixed tick quantity.

Template Parameters
  • ticks:
Return Value
  • TicksConst<Clockticks>:

template <Unit unit, typename TimeType>
static constexpr TimeSource<unit, TimeType> timeSource()

Create a Time Source for this Clock.

Template Parameters
  • unit:
  • TimeType:

template <Unit unit, typename TimeType>
static Ticks<TimeType> timeToTicks(TimeType time)

Get the number of ticks for a given time.

Parameters
  • time:
Return Value
  • TimeType: Tick count, rounded to the nearest tick

template <Unit unit, typename TimeType>
static Time<TimeType> ticksToTime(TimeType ticks)

Get the time for a given number of clock ticks.

Parameters
  • ticks:
Return Value
  • TimeType: Time count, rounded to the nearest unit

static String toString()
struct Frequency
#include <NanoTime.h>

Class to represent a frequency.

Public Functions

Frequency(uint32_t frequency)
operator uint32_t()
String toString() const

Get frequency as compact string.

Note
Drop trailing zeroes to produce a more compact string, e.g. 0 -> 0Hz 1000 -> 1KHz 4553000 -> 4553KHz 1000000000 -> 1GHz

Public Members

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

Class to handle a tick value associated with a clock.

Public Types

template<>
using Clock = Clock_

Public Functions

Ticks(T ticks)
operator T() const
String toString() const
template <Unit unit>
Time<T> as()

Public Members

T ticks

Public Static Functions

static constexpr Clock 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_:

Public Types

template<>
using Clock = Clock_
template<>
using TickType = uint64_t
template<>
using TimeType = uint64_t
template<>
using TimeConst = TimeConst<Clock, unit, TimeType(round(double(ticks_) * Clock::template TicksPerUnit<unit>::den / Clock::template TicksPerUnit<unit>::num))>

Public Functions

constexpr operator TickType()

Public Static Functions

static constexpr TickType ticks()
static constexpr void check()

Obtain the tick count with a static range check against Clock maxTicks.

Return Value
  • TimeType:

template <Unit unit>
static constexpr TimeConst<unit> as()

Get the time for the tick count in a specific time unit.

Template Parameters
  • unit:
Return Value
  • Time<TimeType>:

static String toString()
template <typename T>
struct Time
#include <NanoTime.h>

Class to handle a simple time value with associated unit.

Public Functions

Time()
Time(Unit unit, T time)
void set(Unit unit, T time)
operator T() const
String toString() const
Time &operator+=(Time<T> rhs)
TimeValue value() const
template <Unit unitTo>
Time as() const
Time as(Unit unitTo) const

Public Members

Unit unit = Seconds
T time = 0

Friends

Time &operator+(Time lhs, const Time &rhs)
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_:

Public Types

template<>
using Clock = Clock_
template<>
using TicksPerUnit = typename Clock::template TicksPerUnit<unit_>

Public Functions

constexpr operator uint64_t()

Public Static Functions

static constexpr Clock clock()
static constexpr Unit unit()
static constexpr uint64_t time()
static Ratio32 ticksPerUnit()

Get ticks per unit as a Ratio object.

Return Value
  • BasicRatio32:

static constexpr uint64_t ticks()

Return the corresponding tick value for the time interval.

Return Value
  • TimeType:

static constexpr void check()

Use this function to perform a static (compile-time) range check against Clock maxTicks.

Return Value
  • TimeType:

static constexpr uint64_t clockTime()

Obtain the actual Clock time by converting tick value.

Return Value
  • TimeType:

template <Unit unit>
static constexpr uint64_t as()

Obtain the time in a different set of units.

Template Parameters
  • unit:
Return Value
  • TimeType:

static TimeValue value()
static TimeValue clockValue()
static String toString()
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

Public Types

template<>
using Clock = Clock_
template<>
using TimeType = TimeType_
template<>
using TicksPerUnit = typename Clock::template TicksPerUnit<unit_>
template<>
using TimeConst = TimeConst<Clock, unit_, time>
template<>
using TicksConst = TicksConst<Clock, ticks>
template<>
using MaxClockTime = typename Clock::template MaxTime<unit_>

Get the time corresponding to the maximum clock tick value.

Public Static Functions

static constexpr Unit unit()
static constexpr BasicRatio32 ticksPerUnit()

Number of clock ticks per unit of time.

Return Value
  • BasicRatio32: Result as a rational fraction

static constexpr MaxClockTime maxClockTime()
template <uint64_t time>
static constexpr TimeConst<time> timeConst()

Obtain a TimeConst type representing the given time quantity.

Note
Use methods of TimeConst to obtain corresponding tick count, etc.
Template Parameters
  • time:
Return Value

template <uint64_t ticks>
static constexpr TicksConst<ticks> ticksConst()

Class template defining a fixed tick quantity.

Note
Use methods of TickConst to obtain corresponding time values, etc.
Template Parameters
  • ticks:
Return Value

static constexpr Time<TimeType_> maxCalcTime()

The maximum time value supported by timeToTicks without overflowing.

Return Value
  • TimeType: Passing values larger than this to timeToTicks() will truncate at maximum value

static constexpr Ticks<Clock_, TimeType_> maxCalcTicks()

The maximum tick value supported by ticksToTime without overflowing.

Return Value
  • TimeType: Passing values larger than this to ticksToTime() will truncate at maximum value

static Ticks<Clock_, TimeType_> timeToTicks(TimeType time)

Get the number of ticks for a given time.

Parameters
  • time:
Return Value
  • TimeType: Tick count, rounded to the nearest tick

template <uint64_t time>
static constexpr uint64_t timeToTicks()

Get the number of ticks for a given time.

Template Parameters
  • time:
Return Value
  • uint64_t: Tick count, rounded to the nearest tick

template <uint64_t ticks>
static constexpr uint64_t ticksToTime()

Get the time for a given number of clock ticks.

Template Parameters
  • ticks:
Return Value
  • TimeType: Time count, rounded to the nearest unit

static Time<TimeType_> ticksToTime(TimeType ticks)

Get the time for a given number of clock ticks.

Parameters
  • ticks:
Return Value
  • TimeType: Time count, rounded to the nearest unit

static String toString()
struct TimeValue
#include <NanoTime.h>

A time time broken into its constituent elements.

Note
Useful for analysing and printing time values

Public Functions

TimeValue()
template <typename TimeType>
TimeValue(Unit unit, TimeType time)

Resolve a time value into constituent components.

Parameters
  • time: The time to resolve
  • unit: Units for given time

template <typename TimeType>
void set(Unit unit, TimeType time)
uint32_t getMicroseconds() const

Get sub-second time entirely in microseconds.

uint32_t getNanoseconds() const

Get sub-second time entirely in nanoseconds.

String toString() const
operator String() const

Public Members

bool overflow = false
Unit unit = Seconds

Time unit passed to set() call.

uint32_t days = 0
uint8_t hours = 0
uint8_t minutes = 0
uint8_t seconds = 0
uint16_t milliseconds = 0
uint32_t microseconds = 0
uint32_t nanoseconds = 0
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

Public Static Attributes

constexpr uint64_t num = unitTicks[unit].num
constexpr uint64_t den = unitTicks[unit].den

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

FLASH_MEMORY_START_ADDR
isFlashPtr(ptr)

Simple check to determine if a pointer refers to flash memory.

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

An example of use can be found in Sming/Core/Network/Http/HttpHeaders.h.

API Documentation
class CStringArray : String

Class to manage a double null-terminated list of strings, such as “one\0two\0three\0”.

Constructors

CStringArray(const String &str)
CStringArray(const char *cstr = nullptr)
CStringArray(const char *cstr, unsigned int length)
CStringArray(flash_string_t pstr, int length = -1)
CStringArray(const FlashString &fstr)

Concatenation operators

CStringArray &operator+=(const String &str)
CStringArray &operator+=(const char *cstr)
template <typename T>
CStringArray &operator+=(T value)

Append numbers, etc. to the array.

Parameters
  • value: char, int, float, etc. as supported by String

Iterator support (forward only)

Iterator begin() const
Iterator end() const

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

bool concat(const String &str)
bool concat(const FlashString &fstr)
bool concat(const char *cstr)
bool concat(const char *cstr, size_t length)
bool concat(char c)
bool concat(unsigned char num)
bool concat(int num)
bool concat(unsigned int num)
bool concat(long num)
bool concat(long long num)
bool concat(unsigned long num)
bool concat(unsigned long long num)
bool concat(float num)
bool concat(double num)

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)

String &operator+=(const FlashString &rhs)
String &operator+=(char c)
String &operator+=(unsigned char num)
String &operator+=(int num)
String &operator+=(unsigned int num)
String &operator+=(long num)
String &operator+=(long long num)
String &operator+=(unsigned long num)
String &operator+=(unsigned long long num)
String &operator+=(float num)
String &operator+=(double num)

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

int compareTo(const char *cstr, size_t length) const
int compareTo(const String &s) const

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

bool equals(const String &s) const
bool equals(const char *cstr) const
bool equals(const char *cstr, size_t length) const
bool equals(const FlashString &fstr) const

Equality operator ==

Return Value
  • bool: true if Strings are identical

bool operator==(const String &rhs) const
bool operator==(const char *cstr) const
bool operator==(const FlashString &fstr) const

In-equality operator !=

Return Value
  • bool: Returns true if strings are not identical

bool operator!=(const String &rhs) const
bool operator!=(const char *cstr) const

Comparison operators

bool operator<(const String &rhs) const
bool operator>(const String &rhs) const
bool operator<=(const String &rhs) const
bool operator>=(const String &rhs) const

Test for equality, without case-sensitivity

null strings are treated as empty.

Return Value
  • bool: true if strings are considered the same

bool equalsIgnoreCase(const char *cstr) const
bool equalsIgnoreCase(const char *cstr, size_t length) const
bool equalsIgnoreCase(const String &s2) const
bool equalsIgnoreCase(const FlashString &fstr) const

Array operators

If index is invalid, returns NUL \0

char operator[](size_t index) const
char &operator[](size_t index)

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 indexOf(char ch, size_t fromIndex = 0) const
int indexOf(const char *s2_buf, size_t fromIndex, size_t s2_len) const
int indexOf(const char *s2_buf, size_t fromIndex = 0) const
int indexOf(const String &s2, size_t fromIndex = 0) const

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

int lastIndexOf(char ch) const
int lastIndexOf(char ch, size_t fromIndex) const
int lastIndexOf(const String &s2) const
int lastIndexOf(const String &s2, size_t fromIndex) const
int lastIndexOf(const char *s2_buf, size_t fromIndex, size_t s2_len) const

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

String substring(size_t from, size_t to) const
String substring(size_t from) const

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.

void replace(char find, char replace)
bool replace(const String &find, const String &replace)
bool replace(const char *find_buf, size_t find_len, const char *replace_buf, size_t replace_len)

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

void remove(size_t index)
void remove(size_t index, size_t count)

Public Functions

CStringArray &operator=(const char *cstr)
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

Private Functions

void setString(const char *cstr, int length = -1)
void setString(flash_string_t pstr, int length = -1)
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.

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

long toInt(void) const
float toFloat(void) const

Private Members

PtrBuf ptr
SsoBuf sso

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

class Iterator

Public Types

using const_iterator = Iterator

Public Functions

Iterator()
Iterator(const Iterator&)
Iterator(const CStringArray *array, uint16_t offset, uint16_t index)
operator bool() const
bool equals(const char *rhs) const
bool equalsIgnoreCase(const char *rhs) const
bool operator==(const Iterator &rhs) const
bool operator!=(const Iterator &rhs) const
bool operator==(const char *rhs) const
bool operator!=(const char *rhs) const
bool equals(const String &rhs) const
bool equalsIgnoreCase(const String &rhs) const
bool operator==(const String &rhs) const
bool operator!=(const String &rhs) const
const char *str() const
const char *operator*() const
uint16_t index() const
uint16_t offset() const
Iterator &operator++()
Iterator operator++(int)
void next()
Networking Protocols
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
struct DnsHeader
#include <DnsServer.h>

Public Members

uint16_t ID
char RD
char TC
char AA
char OPCode
char QR
char RCode
char Z
char RA
uint16_t QDCount
uint16_t ANCount
uint16_t NSCount
uint16_t ARCount
class DnsServer : public UdpConnection
#include <DnsServer.h>

Public Functions

DnsServer()
void setErrorReplyCode(DnsReplyCode replyCode)
void setTTL(uint32_t ttl)
bool start(uint16_t port, const String &domainName, const IpAddress &resolvedIP)
void stop()
virtual bool listen(int port)
virtual bool connect(IpAddress ip, uint16_t port)
virtual void close()
virtual bool send(const char *data, int length)
bool sendString(const char *data)
bool sendString(const String &data)
virtual bool sendTo(IpAddress remoteIP, uint16_t remotePort, const char *data, int length)
bool sendStringTo(IpAddress remoteIP, uint16_t remotePort, const char *data)
bool sendStringTo(IpAddress remoteIP, uint16_t remotePort, const String &data)
FTP: File Transfer Protocol

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

Server API
typedef FtpServer FTPServer

class FtpServer : public TcpServer
#include <FtpServer.h>

Public Functions

FtpServer()
void addUser(const String &login, const String &pass)
bool checkUser(const String &login, const String &pass)
virtual bool listen(int port, bool useSsl = false)
void setKeepAlive(uint16_t seconds)
void shutdown()
const Vector<TcpConnection *> &getConnections() const
virtual bool connect(const String &server, int port, bool useSsl = false)
virtual bool connect(IpAddress addr, uint16_t port, bool useSsl = false)
virtual void close()
int writeString(const char *data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)
int writeString(const String &data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)
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: -1 on error

int write(IDataSourceStream *stream)
uint16_t getAvailableWriteSize()
void flush()
void setTimeOut(uint16_t waitTimeOut)
IpAddress getRemoteIp() const
uint16_t getRemotePort() const
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:

bool setSslConnection(Ssl::Connection *connection)
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.

Public Members

uint16_t activeClients = 0
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

Functions

String fromFileExtension(const char *extension)

Obtain content type string from file extension.

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

static String fromFileExtension(const String &extension)

Obtain content type string from file extension.

Parameters
  • extension:
Return Value

String toString(enum MimeType m)

Get textual representation for a MIME type.

Parameters
  • m: the MIME type
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

String fromFullFileName(const char *fileName)

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

Parameters
  • fileName:
Return Value

static String fromFullFileName(const String &fileName)

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

Parameters
  • fileName:
Return Value

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 sendRequest(const Url &url, RequestCompletedDelegate requestComplete)
bool sendRequest(const HttpMethod method, const Url &url, const HttpHeaders &headers, RequestCompletedDelegate requestComplete)
bool sendRequest(const HttpMethod method, const Url &url, const HttpHeaders &headers, const String &body, RequestCompletedDelegate requestComplete)
bool downloadString(const Url &url, RequestCompletedDelegate requestComplete)
bool downloadFile(const Url &url, RequestCompletedDelegate requestComplete = nullptr)
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

bool send(HttpRequest *request)
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 request queues and 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

HttpServer()
HttpServer(const HttpServerSettings &settings)
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)

virtual bool listen(int port, bool useSsl = false)
void setKeepAlive(uint16_t seconds)
void shutdown()
const Vector<TcpConnection *> &getConnections() const
virtual bool connect(const String &server, int port, bool useSsl = false)
virtual bool connect(IpAddress addr, uint16_t port, bool useSsl = false)
virtual void close()
int writeString(const char *data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)
int writeString(const String &data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)
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: -1 on error

int write(IDataSourceStream *stream)
uint16_t getAvailableWriteSize()
void flush()
void setTimeOut(uint16_t waitTimeOut)
IpAddress getRemoteIp() const
uint16_t getRemotePort() const
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:

bool setSslConnection(Ssl::Connection *connection)
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.

Public Members

HttpResourceTree paths

Maps paths to resources which deal with incoming requests.

uint16_t activeClients = 0
MQTT: MQ Telemetry Transport

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

Client API
enum mqttclient::MqttClientState

Values:

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

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

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

MqttClient(bool withDefaultPayloadParser = true, bool autoDestruct = false)
~MqttClient()
void setKeepAlive(uint16_t seconds)

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

Parameters
  • seconds:

void setPingRepeatTime(unsigned 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:

bool publish(const String &topic, const String &message, uint8_t flags = 0)
bool publish(const String &topic, IDataSourceStream *stream, uint8_t flags = 0)
bool subscribe(const String &topic)
bool unsubscribe(const String &topic)
void setEventHandler(mqtt_type_t type, MqttDelegate handler)
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.

Protected Functions

bool connect(const String &server, int port, bool useSsl = false)
bool connect(IpAddress addr, uint16_t port, bool useSsl = false)
void close()
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

bool send(const char *data, uint16_t len, bool forceCloseAfterSent = false)
bool sendString(const String &data, bool forceCloseAfterSent = false)
bool isProcessing()
TcpClientState getConnectionState()
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.

int writeString(const char *data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)
int writeString(const String &data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)
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: -1 on error

int write(IDataSourceStream *stream)
uint16_t getAvailableWriteSize()
void flush()
void setTimeOut(uint16_t waitTimeOut)
IpAddress getRemoteIp() const
uint16_t getRemotePort() const
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:

bool setSslConnection(Ssl::Connection *connection)
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.

NTP: Network Time Protocol

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

Client API
typedef Delegate<void(NtpClient &client, time_t ntpTime)> NtpTimeResultDelegate
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.

Protected Functions

virtual bool listen(int port)
virtual bool connect(IpAddress ip, uint16_t port)
virtual void close()
virtual bool send(const char *data, int length)
bool sendString(const char *data)
bool sendString(const String &data)
virtual bool sendTo(IpAddress remoteIP, uint16_t remotePort, const char *data, int length)
bool sendStringTo(IpAddress remoteIP, uint16_t remotePort, const char *data)
bool sendStringTo(IpAddress remoteIP, uint16_t remotePort, const String &data)
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
typedef Delegate<int(SmtpClient &client, int code, char *status)> SmtpClientCallback
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(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&:

Public Members

String to
String from
String subject
String cc
class SmtpClient : protected TcpClient
#include <SmtpClient.h>

Public Functions

SmtpClient(bool autoDestroy = false)
~SmtpClient()
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(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

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

size_t countPending()
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:

Protected Functions

bool connect(const String &server, int port, bool useSsl = false)
bool connect(IpAddress addr, uint16_t port, bool useSsl = false)
void close()
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

bool send(const char *data, uint16_t len, bool forceCloseAfterSent = false)
bool sendString(const String &data, bool forceCloseAfterSent = false)
bool isProcessing()
TcpClientState getConnectionState()
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.

int writeString(const char *data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)
int writeString(const String &data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)
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: -1 on error

int write(IDataSourceStream *stream)
uint16_t getAvailableWriteSize()
void flush()
void setTimeOut(uint16_t waitTimeOut)
IpAddress getRemoteIp() const
uint16_t getRemotePort() const
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:

bool setSslConnection(Ssl::Connection *connection)
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.

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
typedef Delegate<void(TcpConnection&)> TcpConnectionDestroyedDelegate
NETWORK_DEBUG
NETWORK_SEND_BUFFER_SIZE
class TcpConnection
#include <TcpConnection.h>

Subclassed by FtpDataStream, FtpServerConnection, TcpClient, TcpServer

Public Functions

TcpConnection(bool autoDestruct)
TcpConnection(tcp_pcb *connection, bool autoDestruct)
virtual ~TcpConnection()
virtual bool connect(const String &server, int port, bool useSsl = false)
virtual bool connect(IpAddress addr, uint16_t port, bool useSsl = false)
virtual void close()
int writeString(const char *data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)
int writeString(const String &data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)
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: -1 on error

int write(IDataSourceStream *stream)
uint16_t getAvailableWriteSize()
void flush()
void setTimeOut(uint16_t waitTimeOut)
IpAddress getRemoteIp() const
uint16_t getRemotePort() const
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:

bool setSslConnection(Ssl::Connection *connection)
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
typedef Delegate<void(TcpClient &client, TcpConnectionEvent sourceEvent)> TcpClientEventDelegate
typedef Delegate<void(TcpClient &client, bool successful)> TcpClientCompleteDelegate
typedef Delegate<bool(TcpClient &client, char *data, int size)> TcpClientDataDelegate
TCP_CLIENT_TIMEOUT
class TcpClient : public TcpConnection
#include <TcpClient.h>

Subclassed by HttpConnection, MqttClient, SmtpClient

Public Functions

TcpClient(bool autoDestruct)
TcpClient(tcp_pcb *clientTcp, TcpClientDataDelegate clientReceive, TcpClientCompleteDelegate onCompleted)
TcpClient(TcpClientCompleteDelegate onCompleted, TcpClientEventDelegate onReadyToSend, TcpClientDataDelegate onReceive = nullptr)
TcpClient(TcpClientCompleteDelegate onCompleted, TcpClientDataDelegate onReceive = nullptr)
TcpClient(TcpClientDataDelegate onReceive)
~TcpClient()
bool connect(const String &server, int port, bool useSsl = false)
bool connect(IpAddress addr, uint16_t port, bool useSsl = false)
void close()
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

bool send(const char *data, uint16_t len, bool forceCloseAfterSent = false)
bool sendString(const String &data, bool forceCloseAfterSent = false)
bool isProcessing()
TcpClientState getConnectionState()
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.

int writeString(const char *data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)
int writeString(const String &data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)
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: -1 on error

int write(IDataSourceStream *stream)
uint16_t getAvailableWriteSize()
void flush()
void setTimeOut(uint16_t waitTimeOut)
IpAddress getRemoteIp() const
uint16_t getRemotePort() const
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:

bool setSslConnection(Ssl::Connection *connection)
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.

Server API
typedef Delegate<void(TcpClient *client)> TcpClientConnectDelegate
TCP_SERVER_TIMEOUT
class TcpServer : public TcpConnection
#include <TcpServer.h>

Subclassed by FtpServer, HttpServer, TelnetServer

Public Functions

TcpServer()
TcpServer(TcpClientConnectDelegate onClientHandler, TcpClientDataDelegate clientReceiveDataHandler, TcpClientCompleteDelegate clientCompleteHandler)
TcpServer(TcpClientDataDelegate clientReceiveDataHandler, TcpClientCompleteDelegate clientCompleteHandler)
TcpServer(TcpClientDataDelegate clientReceiveDataHandler)
~TcpServer()
virtual bool listen(int port, bool useSsl = false)
void setKeepAlive(uint16_t seconds)
void shutdown()
const Vector<TcpConnection *> &getConnections() const
virtual bool connect(const String &server, int port, bool useSsl = false)
virtual bool connect(IpAddress addr, uint16_t port, bool useSsl = false)
virtual void close()
int writeString(const char *data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)
int writeString(const String &data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)
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: -1 on error

int write(IDataSourceStream *stream)
uint16_t getAvailableWriteSize()
void flush()
void setTimeOut(uint16_t waitTimeOut)
IpAddress getRemoteIp() const
uint16_t getRemotePort() const
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:

bool setSslConnection(Ssl::Connection *connection)
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.

Public Members

uint16_t activeClients = 0
Telnet

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

Server API
typedef Delegate<void(TcpClient *client, char *data, int size)> TelnetServerCommandDelegate
TELNETSERVER_MAX_COMMANDSIZE
class TelnetServer : public TcpServer
#include <TelnetServer.h>

Public Functions

TelnetServer()
void enableDebug(bool reqStatus)
void enableCommand(bool reqStatus)
virtual bool listen(int port, bool useSsl = false)
void setKeepAlive(uint16_t seconds)
void shutdown()
const Vector<TcpConnection *> &getConnections() const
virtual bool connect(const String &server, int port, bool useSsl = false)
virtual bool connect(IpAddress addr, uint16_t port, bool useSsl = false)
virtual void close()
int writeString(const char *data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)
int writeString(const String &data, uint8_t apiflags = TCP_WRITE_FLAG_COPY)
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: -1 on error

int write(IDataSourceStream *stream)
uint16_t getAvailableWriteSize()
void flush()
void setTimeOut(uint16_t waitTimeOut)
IpAddress getRemoteIp() const
uint16_t getRemotePort() const
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:

bool setSslConnection(Ssl::Connection *connection)
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.

Public Members

uint16_t activeClients = 0
UDP: User Datagram Protocol

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

Connection API
typedef Delegate<void(UdpConnection &connection, char *data, int size, IpAddress remoteIP, uint16_t remotePort)> UdpConnectionDataDelegate
class UdpConnection
#include <UdpConnection.h>

Subclassed by DnsServer, NtpClient

Public Functions

UdpConnection()
UdpConnection(UdpConnectionDataDelegate dataHandler)
virtual ~UdpConnection()
virtual bool listen(int port)
virtual bool connect(IpAddress ip, uint16_t port)
virtual void close()
virtual bool send(const char *data, int length)
bool sendString(const char *data)
bool sendString(const String &data)
virtual bool sendTo(IpAddress remoteIP, uint16_t remotePort, const char *data, int length)
bool sendStringTo(IpAddress remoteIP, uint16_t remotePort, const char *data)
bool sendStringTo(IpAddress remoteIP, uint16_t remotePort, const String &data)
URL: Uniform Resource Locator

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

typedef 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()
Url(const Url &url)
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(const String &scheme, const String &user, const String &password, const String &host, int port = 0, const String &path = nullptr, const String &query = nullptr, const String &fragment = nullptr)
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

operator String() const
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 User
String Password
String Host

hostname or IP address

int Port = 0

Undefined by default.

String Path

with leading “/”

HttpParams Query
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
typedef Vector<WebsocketConnection *> WebsocketList
typedef Delegate<void(WebsocketConnection&)> WebsocketDelegate
typedef Delegate<void(WebsocketConnection&, const String&)> WebsocketMessageDelegate
typedef Delegate<void(WebsocketConnection&, uint8_t *data, size_t size)> WebsocketBinaryDelegate
WEBSOCKET_VERSION
struct WsFrameInfo
#include <WebsocketConnection.h>

Public Functions

WsFrameInfo()
WsFrameInfo(ws_frame_type_t type, char *payload, size_t payloadLength)

Public Members

ws_frame_type_t type = WS_FRAME_TEXT
char *payload = nullptr
size_t payloadLength = 0
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

virtual ~WebsocketConnection()
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
  • void*: The user data previously set by setUserData()

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

WebsocketClient()
WebsocketClient(HttpConnection *connection)
HttpConnection *getHttpConnection()
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.

Protected Functions

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
  • void*: The user data previously set by setUserData()

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

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

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

Typedefs

typedef Delegate<void(bool success, BssList &list)> ScanCompletedDelegate

Scan complete handler function.

typedef Delegate<bool(SmartConfigEvent event, const SmartConfigEventInfo &info)> SmartConfigDelegate

Smart configuration handler function.

Parameters
  • event:
  • info:
Return Value
  • bool: return true to perform default configuration

typedef Delegate<bool(WpsStatus status)> WPSConfigDelegate

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:

eWPS_Success = 0
eWPS_Failed
eWPS_Timeout
eWPS_WEP

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 ~StationClass()
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 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 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 ~AccessPointClass()
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 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

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

typedef Delegate<void(const String &ssid, MacAddress bssid, uint8_t channel)> StationConnectDelegate

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:

typedef Delegate<void(const String &ssid, MacAddress bssid, WifiDisconnectReason reason)> StationDisconnectDelegate

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

typedef Delegate<void(WifiAuthMode oldMode, WifiAuthMode newMode)> StationAuthModeChangeDelegate

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:

typedef Delegate<void(IpAddress ip, IpAddress netmask, IpAddress gateway)> StationGotIPDelegate

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:

typedef Delegate<void(MacAddress mac, uint16_t aid)> AccessPointConnectDelegate

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

typedef Delegate<void(MacAddress mac, uint16_t aid)> AccessPointDisconnectDelegate

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

typedef Delegate<void(int rssi, MacAddress mac)> AccessPointProbeReqRecvedDelegate

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

typedef Delegate<void(uint8_t *data, uint16_t length)> WifiSnifferCallback
typedef Delegate<void(const BeaconInfo &beacon)> WifiBeaconCallback
typedef Delegate<void(const ClientInfo &client)> WifiClientCallback
struct BeaconInfo
#include <WifiSniffer.h>

Decoded Wifi beacon (Access Point) information.

Public Members

uint8_t bssid[ETH_MAC_LEN]
uint8_t ssid[33]
uint8_t ssid_len
uint8_t channel
int8_t err
int8_t rssi
uint8_t capa[2]
struct ClientInfo
#include <WifiSniffer.h>

Decoded Wifi client information.

Public Members

uint8_t bssid[ETH_MAC_LEN]
uint8_t station[ETH_MAC_LEN]
uint8_t ap[ETH_MAC_LEN]
uint8_t channel
int8_t err
int8_t rssi
uint16_t seq_n
class BeaconInfoList : public Vector<BeaconInfo>
#include <WifiSniffer.h>

For applications to use to manage list of unique beacons.

Public Types

template<>
typedef int (*Comparer)(const BeaconInfo &lhs, const BeaconInfo &rhs)

Public Functions

int indexOf(const uint8_t bssid[])
unsigned int capacity() const
bool contains(const BeaconInfo &elem) const
const BeaconInfo &firstElement() const
int indexOf(const BeaconInfo &elem) const
bool isEmpty() const
const BeaconInfo &lastElement() const
int lastIndexOf(const BeaconInfo &elem) const
unsigned int count() const
unsigned int size() const
void copyInto(BeaconInfo *array) const
bool add(const BeaconInfo &obj)
bool addElement(const BeaconInfo &obj)
bool addElement(BeaconInfo *objp)
void clear()
bool ensureCapacity(unsigned int minCapacity)
void removeAllElements()
bool removeElement(const BeaconInfo &obj)
bool setSize(unsigned int newSize)
void trimToSize()
const BeaconInfo &elementAt(unsigned int index) const
bool insertElementAt(const BeaconInfo &obj, unsigned int index)
const void remove(unsigned int index)
void removeElementAt(unsigned int index)
bool setElementAt(const BeaconInfo &obj, unsigned int index)
const BeaconInfo &get(unsigned int index) const
const BeaconInfo &operator[](unsigned int index) const
BeaconInfo &operator[](unsigned int index)
void sort(Comparer compareFunction)
const BeaconInfo &at(unsigned int i) const
class ClientInfoList : public Vector<ClientInfo>
#include <WifiSniffer.h>

For applications to use to manage list of unique clients.

Public Types

template<>
typedef int (*Comparer)(const ClientInfo &lhs, const ClientInfo &rhs)

Public Functions

int indexOf(const uint8_t station[])
unsigned int capacity() const
bool contains(const ClientInfo &elem) const
const ClientInfo &firstElement() const
int indexOf(const ClientInfo &elem) const
bool isEmpty() const
const ClientInfo &lastElement() const
int lastIndexOf(const ClientInfo &elem) const
unsigned int count() const
unsigned int size() const
void copyInto(ClientInfo *array) const
bool add(const ClientInfo &obj)
bool addElement(const ClientInfo &obj)
bool addElement(ClientInfo *objp)
void clear()
bool ensureCapacity(unsigned int minCapacity)
void removeAllElements()
bool removeElement(const ClientInfo &obj)
bool setSize(unsigned int newSize)
void trimToSize()
const ClientInfo &elementAt(unsigned int index) const
bool insertElementAt(const ClientInfo &obj, unsigned int index)
const void remove(unsigned int index)
void removeElementAt(unsigned int index)
bool setElementAt(const ClientInfo &obj, unsigned int index)
const ClientInfo &get(unsigned int index) const
const ClientInfo &operator[](unsigned int index) const
ClientInfo &operator[](unsigned int index)
void sort(Comparer compareFunction)
const ClientInfo &at(unsigned int i) const
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

typedef void (*TaskCallback32)(uint32_t param)

Task callback function type, uint32_t parameter.

Note
Callback code does not need to be in IRAM

typedef void (*TaskCallback)(void *param)

Task callback function type, void* parameter.

Note
Callback code does not need to be in IRAM

typedef Delegate<void()> TaskDelegate

Task Delegate callback type.

typedef TaskDelegate SystemReadyDelegate

Handler function for system ready.

Enums

enum CpuFrequency

CPU Frequency.

Values:

eCF_80MHz = 80

CPU 80MHz.

eCF_160MHz = 160

CPU 160MHz.

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 ~ISystemReadyHandler()
virtual void onSystemReady() = 0

Handle system ready events.

class SystemClass
#include <System.h>

System class.

Public Functions

SystemClass()
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.

typedef Delegate<void(String commandLine, CommandOutput *commandOutput)> CommandFunctionDelegate

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

~CommandDelegate()

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.

~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 use, instantiate a single instance of Profiling::CpuUsage and call Profiling::CpuUsage::begin() from the application’s init() function, passing a callback function to be invoked after calibration has completed. This function will continue with your application’s normal execution.

See RingTone Player for an 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 >

Public Functions

MinMax(const String &title)
const String &getTitle() const
void clear()
void update(T value)
T getMin() const
T getMax() const
T getTotal() const
T getAverage() const
unsigned getCount() const
size_t printTo(Print &p) const
Min-Max Times
template <class Timer>
class MinMaxTimes : public Profiling::MinMax<uint32_t>, public Timer

Public Types

template<>
using Millis = NanoTime::TimeSource<Clock, NanoTime::Milliseconds, uint32_t>
template<>
using Micros = NanoTime::TimeSource<Clock, NanoTime::Microseconds, TimeType>

Public Functions

MinMaxTimes(const String &title)
void update()
NanoTime::Time<uint32_t> getMinTime() const
NanoTime::Time<uint32_t> getMaxTime() const
NanoTime::Time<uint32_t> getAverageTime() const
NanoTime::Time<uint32_t> getTotalTime() const
size_t printTo(Print &p) const
const String &getTitle() const
void clear()
void update(T value)
T getMin() const
T getMax() const
T getTotal() const
T getAverage() const
unsigned getCount() const
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

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

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

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

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)

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

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

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

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)

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

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

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

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

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

void setInterval()

Set timer interval in timer ticks (static check)

Note
On error, compilation fails with error message
Template Parameters
  • ticks: Interval in ticks

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

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.

void setIntervalUs()

Set timer interval in microseconds (static check)

bool setIntervalMs(uint32_t milliseconds)

Set timer interval in milliseconds.

void setIntervalMs()

Set timer interval in milliseconds (static check)

Public Static Functions

static constexpr Millis millis()

Get a millisecond time source.

static constexpr Micros micros()

Get a microsecond time source.

static constexpr uint64_t usToTicks()

Convert microsecond count into timer ticks.

static TickType usToTicks(TimeType time)

Convert microsecond count into timer ticks.

static constexpr uint64_t ticksToUs()

Convert timer ticks into microseconds.

static TimeType ticksToUs(TickType ticks)

Convert timer ticks into microseconds.

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

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

static constexpr void checkIntervalMs()

Check timer interval in milliseconds is valid (static check)

static constexpr void checkIntervalUs()

Check timer interval in microseconds is valid (static check)

Protected Types

template<>
using Clock = OsTimerApi::Clock
template<>
using TickType = uint64_t
template<>
using TimeType = uint64_t

Protected Functions

bool isArmed() const
TickType ticks() const
void arm(bool repeating)
void disarm()
String name() const
String toString() const
operator String() const

Protected Attributes

TimerCallback func
void *arg

Protected Static Functions

static constexpr const char *typeName()
static constexpr TickType minTicks()
static constexpr TickType maxTicks()

Wiring

The Wiring Framework is common to all Arduino platforms.

Some adaptations and improvements have been made for Sming which are documented here.

Vector
template <typename Element>
class Vector : public Countable<Element>

Vector class template.

Public Types

template<>
typedef int (*Comparer)(const Element &lhs, const Element &rhs)

Public Functions

Vector(unsigned int initialCapacity = 10, unsigned int capacityIncrement = 10)
Vector(const Vector &rhv)
~Vector()
unsigned int capacity() const
bool contains(const Element &elem) const
const Element &firstElement() const
int indexOf(const Element &elem) const
bool isEmpty() const
const Element &lastElement() const
int lastIndexOf(const Element &elem) const
unsigned int count() const
unsigned int size() const
void copyInto(Element *array) const
bool add(const Element &obj)
bool addElement(const Element &obj)
bool addElement(Element *objp)
void clear()
bool ensureCapacity(unsigned int minCapacity)
void removeAllElements()
bool removeElement(const Element &obj)
bool setSize(unsigned int newSize)
void trimToSize()
const Element &elementAt(unsigned int index) const
bool insertElementAt(const Element &obj, unsigned int index)
const void remove(unsigned int index)
void removeElementAt(unsigned int index)
bool setElementAt(const Element &obj, unsigned int index)
const Element &get(unsigned int index) const
const Element &operator[](unsigned int index) const
Element &operator[](unsigned int index)
const Vector<Element> &operator=(const Vector<Element> &rhv)
const Vector<Element> &operator=(const Vector<Element> &&other)
void sort(Comparer compareFunction)
const Element &at(unsigned int i) const
HashMap
template <typename K, typename V>
class HashMap

HashMap class template.

Public Types

template<>
typedef bool (*comparator)(const K&, const K&)

Public Functions

HashMap()
HashMap(comparator compare)
~HashMap()
unsigned int count() const
const K &keyAt(unsigned int idx) const
K &keyAt(unsigned int idx)
const V &valueAt(unsigned int idx) const
V &valueAt(unsigned int idx)
const V &operator[](const K &key) const
V &operator[](const K &key)
void allocate(unsigned int newSize)
int indexOf(const K &key) const
bool contains(const K &key) const
void removeAt(unsigned index)
void remove(const K &key)
void clear()
void setMultiple(const HashMap<K, V> &map)
void setNullValue(const V &nullv)
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 <https://github.com/esp8266/arduino/pull/5690>. 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).

String(const char *cstr)
String(const char *cstr, size_t length)
String(const String &str)
String(flash_string_t pstr, int length = -1)
String(String &&rval)
String(StringSumHelper &&rval)
String(char c)
String(unsigned char, unsigned char base = 10)
String(int, unsigned char base = 10)
String(unsigned int, unsigned char base = 10)
String(long, unsigned char base = 10)
String(long long, unsigned char base = 10)
String(unsigned long, unsigned char base = 10)
String(unsigned long long, unsigned char base = 10)
String(float, unsigned char decimalPlaces = 2)
String(double, unsigned char decimalPlaces = 2)

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

String &operator=(const String &rhs)
String &operator=(const char *cstr)

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.

String &operator=(String &&rval)
String &operator=(StringSumHelper &&rval)

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

bool concat(const String &str)
bool concat(const FlashString &fstr)
bool concat(const char *cstr)
bool concat(const char *cstr, size_t length)
bool concat(char c)
bool concat(unsigned char num)
bool concat(int num)
bool concat(unsigned int num)
bool concat(long num)
bool concat(long long num)
bool concat(unsigned long num)
bool concat(unsigned long long num)
bool concat(float num)
bool concat(double num)

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)

String &operator+=(const String &rhs)
String &operator+=(const FlashString &rhs)
String &operator+=(const char *cstr)
String &operator+=(char c)
String &operator+=(unsigned char num)
String &operator+=(int num)
String &operator+=(unsigned int num)
String &operator+=(long num)
String &operator+=(long long num)
String &operator+=(unsigned long num)
String &operator+=(unsigned long long num)
String &operator+=(float num)
String &operator+=(double num)

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

int compareTo(const char *cstr, size_t length) const
int compareTo(const String &s) const

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

bool equals(const String &s) const
bool equals(const char *cstr) const
bool equals(const char *cstr, size_t length) const
bool equals(const FlashString &fstr) const

Equality operator ==

Return Value
  • bool: true if Strings are identical

bool operator==(const String &rhs) const
bool operator==(const char *cstr) const
bool operator==(const FlashString &fstr) const

In-equality operator !=

Return Value
  • bool: Returns true if strings are not identical

bool operator!=(const String &rhs) const
bool operator!=(const char *cstr) const

Comparison operators

bool operator<(const String &rhs) const
bool operator>(const String &rhs) const
bool operator<=(const String &rhs) const
bool operator>=(const String &rhs) const

Test for equality, without case-sensitivity

null strings are treated as empty.

Return Value
  • bool: true if strings are considered the same

bool equalsIgnoreCase(const char *cstr) const
bool equalsIgnoreCase(const char *cstr, size_t length) const
bool equalsIgnoreCase(const String &s2) const
bool equalsIgnoreCase(const FlashString &fstr) const

Array operators

If index is invalid, returns NUL \0

char operator[](size_t index) const
char &operator[](size_t index)

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 indexOf(char ch, size_t fromIndex = 0) const
int indexOf(const char *s2_buf, size_t fromIndex, size_t s2_len) const
int indexOf(const char *s2_buf, size_t fromIndex = 0) const
int indexOf(const String &s2, size_t fromIndex = 0) const

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

int lastIndexOf(char ch) const
int lastIndexOf(char ch, size_t fromIndex) const
int lastIndexOf(const String &s2) const
int lastIndexOf(const String &s2, size_t fromIndex) const
int lastIndexOf(const char *s2_buf, size_t fromIndex, size_t s2_len) const

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

String substring(size_t from, size_t to) const
String substring(size_t from) const

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.

void replace(char find, char replace)
bool replace(const String &find, const String &replace)
bool replace(const char *find_buf, size_t find_len, const char *replace_buf, size_t replace_len)

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

void remove(size_t index)
void remove(size_t index, size_t count)

Public Functions

String()

Default constructor.

Note
Creates a null String which evaluates to false.

~String(void)
void setString(const char *cstr, int length = -1)
void setString(flash_string_t pstr, int length = -1)
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.

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

const char *begin() const
const char *end() const
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.

long toInt(void) const
float toFloat(void) const

Public Members

PtrBuf ptr
SsoBuf sso

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.

Friends

StringSumHelper &operator+(const StringSumHelper &lhs, const String &rhs)
StringSumHelper &operator+(const StringSumHelper &lhs, const char *cstr)
StringSumHelper &operator+(const StringSumHelper &lhs, char c)
StringSumHelper &operator+(const StringSumHelper &lhs, unsigned char num)
StringSumHelper &operator+(const StringSumHelper &lhs, int num)
StringSumHelper &operator+(const StringSumHelper &lhs, unsigned int num)
StringSumHelper &operator+(const StringSumHelper &lhs, long num)
StringSumHelper &operator+(const StringSumHelper &lhs, unsigned long num)
StringSumHelper &operator+(const StringSumHelper &lhs, unsigned long long num)
StringSumHelper &operator+(const StringSumHelper &lhs, float num)
StringSumHelper &operator+(const StringSumHelper &lhs, double num)

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 Audio

Demonstrates how to set up I2S driver to produce audio output.

References

Basic Capsense

Simple demonstration showing Displays raw output from a capacitive sensor.

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

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 ported from the C code that Espressif provides in the 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 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 DISABLE_SPIFFS=1

See webserver.cpp for the details.

References

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.

That’s it.

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

Basic rBoot

Introduction

This sample integrates rBoot and Sming, for the many people who have been asking for it. It demonstrates dual rom booting, big flash support, OTA updates and dual spiffs filesystems. You must enable big flash support in rBoot and use on an ESP12 (or similar device with 4MB flash). 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 ESP_HOME & SMING_HOME, as environment variables or edit component.mk as you would for general Sming app compiling.
  2. Set WIFI_SSID & WIFI_PWD environment variables with your wifi details.
  3. Edit the OTA server details at the top of app/application.cpp.
  4. Check overridable variables in component.mk, or set as env vars.
  5. make && make flash
  6. Put rom0.bin and spiff_rom.bin in the root of your webserver for OTA.
  7. Interact with the sample using a terminal (make terminal). Sorry - no web-gui (yet).
Flashing

If flashing manually use esptool.py to flash rBoot, rom & spiffs e.g.:

esptool.py –port write_flash -fs 32m 0x00000 rboot.bin 0x02000 rom0.bin 0x100000 spiffs.rom

Using the correct -fs parameter is important. This will be -fs 32m on an ESP12.

You can also flash rom0.bin to 0x202000, but booting and using OTA is quicker!

Technical Notes

spiffs_mount_manual(address, length) must be called from init.

Important compiler flags used:

  • BOOT_BIG_FLASH - when using big flash mode, ensures flash mapping code is built in to the rom.
  • RBOOT_INTEGRATION - ensures Sming specific options are pulled in to the rBoot source at compile time.
  • SPIFF_SIZE=value - passed through to code for mounting the filesystem. Also used in the Makefile to create the SPIFFS.
Flash layout considerations

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 the rBoot readme for further details.

  • If using a very small flash (e.g. 512k) there may be no room for a spiffs fileystem, disable it with DISABLE_SPIFFS = 1
  • If you are using spiffs set RBOOT_SPIFFS_0 & RBOOT_SPIFFS_1 to indicate where the filesystems are located on the flash.
  • 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!

Credits

This sample was made possible with the assistance of piperpilot, gschmott and robotiko on the esp8266.com forum.

References

CANBUS

Demonstrates communication with MCP2515-compatible devices via SPI interface.

CommandProcessing Debug

Demonstrates Sming’s command handling capability via HTTP, FTP and Telnet interfaces.

References

HMC5883L Compass

ESP8266 HMC5883L sensor reader.

_images/hmc5883l.jpg
References

DFPlayer Mini

Introduction

The DFPlayer Mini MP3 Player is a small and low price MP3 module with an 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 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 file to an SD card. The example playes one file for some time and moves to next one. * Insert that SD card in the slot of the DF Player module. * Flash and run the sample code.

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.

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 a simple FTP server with a couple of files stored in SPIFFS.

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.

After flashing:

make flashapp

You should be able to connect using an FTP client:

ftp ipaddress
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.

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 HttpClient and direct ESP8266 ThingSpeak data pushing.

_images/thing-speak.png
References

AJAX Http Server

Demonstration of a simple web server with page updates using AJAX.

References

Bootstrap Http Server

ESP8266 embedded web server.

At start ESP will download all requried files from remote server. All content, including JQuery and Bootstrap will be saved on device (and can work offline). Static files stored in GZIPed mode.

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

    • Set SPI_SIZE to the flash memory size of your device.
    • If necessary, modify RBOOT_ROM0_ADDR, RBOOT_ROM1_ADDR, RBOOT_SPIFFS_0 and SPIFF_SIZE to fit both ROM slots and the file system into the available flash memory. Make sure that the flash areas do not overlap with each other or any the reserved regions. Refer to the rBoot documentation for further details.
  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.

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.

DHT22 Humidity Sensor

Example application demonstrating the use of the DHT ESP Temperature/Humidity Sensors library with a DHT22 humidity sensor.

_images/dht22.jpg

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.

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

MeteoControl MQTT

Similar to MeteoControl sample without LCD interface and instead controlled via MQTT.

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.

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.

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.

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.

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

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 SPI_MODE=dio SPI_SIZE=4M
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

SD Card

SDCard/FAT file usage and write benchmark.

References
Used by

5110 LCD Screen

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

How to interface with displays such as the Adafruit 2.8” Touch Shield V2 (SPI).

References

ST7735 TFT Screen

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 was 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 TCP Client / Server

Demonstrates a simple Telnet server application.

References

DS1820 Temperature Sensor

ESP8266 DS1820/DS18B20 sensor reader.

_images/ds1820.jpg

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

This example just does Advertise.

Listen implementation is still on going work and will be basically porting http://gkaindl.com/software/arduino-ethernet/bonjour to “Sming”.

In short this code will advertise other machines about its ipaddress.

But you can not convert other mDNS advertiser’s host name to ipaddress. (this is work of Listening)

How to use mDNS:

  1. ADD your WIFI_SSID / Password

  2. Flash the Complied code to ESP8266

  3. According to OS of your PC / phone

    Mac OS (iphone/ipad/ mac)

    in Safari browser type “http://test.local/” to open a sample webpage.

    Windows

    You need Bonjour Service running. If you do not have it Install it from http://download.cnet.com/Bonjour-for-Windows/3000-18507_4-93550.html.

    After installing in IE or Chrome or other browser type “http://test.local/” to open a sample webpage.

    Linux

    You need to install Avahi mDNS/DNS-SD daemon.

    In your browser type “http://test.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://test.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 for 3.3v and in my case work without problems

By nik.sharky http://esp8266.ru/forum/members/sharky.396/

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

Libraries

Sming comes with a number of ported libraries that cover many areas of embedded programming but for sure not all.

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

Adafruit ILI9341 Display

This is a 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

To download. click the DOWNLOADS button in the top right corner, rename the uncompressed folder Adafruit_ILI9341. Check that the Adafruit_ILI9341 folder contains Adafruit_ILI9341.cpp and Adafruit_ILI9341.

Place the Adafruit_ILI9341 library folder your arduinosketchfolder/libraries/ folder. You may need to create the libraries subfolder if its your first library. Restart the IDE

Also requires the Adafruit_GFX library for Arduino.

References

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

Adafruit VL53L0X Library .. image:: https://travis-ci.com/adafruit/Adafruit_VL53L0X.svg?branch=master


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

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

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

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

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

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

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

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

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

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

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

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

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

Public Members

HttpServerConnectionBodyDelegate onBody = nullptr

resource wants to process the raw body data

HttpResourceDelegate onHeadersComplete = nullptr

headers are ready

HttpResourceDelegate onRequestComplete = nullptr

request is complete OR upgraded

HttpServerConnectionUpgradeDelegate onUpgrade = nullptr

request is upgraded and raw data is passed to it

References
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;
};

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

Print an integral number to output stream

Parameters
  • num: Number to print
  • base: The base for output (Default: Decimal (base 10))
Return Value
  • size_t: Quantity of characters written to stream

size_t print(unsigned long num, int base = DEC)
size_t print(const unsigned long long &num, int base = DEC)
size_t print(long, int base = DEC)
size_t print(const long long&, int base = DEC)
size_t print(unsigned int num, int base = DEC)
size_t print(unsigned char num, int base = DEC)
size_t print(int num, int base = DEC)

Print an integral number to output stream, appending newline

Parameters
  • num: Number to print
  • base: The base for output (Default: Decimal (base 10))
Return Value
  • size_t: Quantity of characters written to stream

size_t println(unsigned char num, int base = DEC)
size_t println(unsigned int num, int base = DEC)
size_t println(unsigned long num, int base = DEC)
size_t println(const unsigned long long &num, int base = DEC)
size_t println(int num, int base = DEC)
size_t println(long num, int base = DEC)
size_t println(const long long &num, int base = DEC)

Public Types

enum Error

Error code values.

Values:

None

No error occured thus far (default value of errorCode if hasError() returns false)

InvalidFormat

Invalid/unsupported upgrade file format.

UnsupportedData

Some content of the upgrade file is not supported by this version of OtaUpgradeStream.

DecryptionFailed

Decryption failed. Probably wrong decryption key.

NoRomFound

The file did not contain a ROM image suitable for the start address of the slot to upgrade.

RomTooLarge

The contained ROM image does not fit into the application firmware slot.

DowngradeNotAllowed

Attempt to downgrade to older firmware version.

VerificationFailed

Signature/checksum verification failed - updated ROM not activated.

FlashWriteFailed

Error while writing to Flash memory.

RomActivationFailed

Error while activating updated ROM slot.

OutOfMemory

Dynamic memory allocation failed.

Internal

An unexpected error occured.

Public Functions

BasicStream()
size_t write(const uint8_t *data, size_t size)

Process chunk of upgrade file.

Return
If less than size, an error occured. Check errorCode for more details.
Note
Even if write() never returns less than size it is not guaranteed that the upgrade was successful. Always use hasError() to determine success.
Parameters
  • data: Pointer to chunk of data.
  • size: Size of chunk pointed to by data in bytes.

bool hasError() const

Returns true if an error happened during the upgrade process.

See
errorCode

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

virtual int available()

Return the total length of the stream.

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

bool isFinished()

Check if all data has been read.

Return Value
  • bool: True on success.

size_t write(uint8_t charToWrite)

Write a single character to the stream.

Parameters
  • charToWrite:
Return Value
  • size_t: Number of chars written (1 on success, 0 on failure)

size_t write(const char *str)

Writes a c-string to output stream.

Parameters
  • str: Pointer to c-string
Return Value
  • size_t: Quantity of characters written to stream

size_t write(const char *buffer, size_t size)

Writes characters from a buffer to output stream.

Parameters
  • buffer: Pointer to character buffer
  • size: Quantity of characters to write
Return Value
  • size_t: Quantity of characters written to stream

virtual size_t copyFrom(IDataSourceStream *source, size_t size)

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

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

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, unsigned 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: SEEK_SET, SEEK_CUR, SEEK_END
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.

int length()

Return the total length of the stream.

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

void flush()
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

String readString(size_t maxLen = UINT16_MAX)

Overrides Stream method for more efficient reading.

Note
Content is read using readMemoryBlock() so read position (for seekable streams) is not changed

String readString()
void setTimeout(unsigned long timeout)
bool find(char *target)
bool find(char *target, size_t length)
bool findUntil(char *target, char *terminator)
bool findUntil(char *target, size_t targetLen, char *terminate, size_t termLen)
long parseInt()
float parseFloat()
size_t readBytes(char *buffer, size_t length)
size_t readBytesUntil(char terminator, char *buffer, size_t length)
String readStringUntil(char terminator)
virtual int indexOf(char c)
int getWriteError()

Gets last error.

Return Value
  • int: Error number of last write error

void clearWriteError()

Clears the last write error.

size_t print(char c)

Prints a single character to output stream.

Parameters
  • c: Character to print
Return Value
  • size_t: Quantity of characters written to stream

size_t print(const char str[])

Prints a c-string to output stream.

Parameters
  • str: c-string to print
Return Value
  • size_t: Quantity of characters written to output stream

size_t print(double num, int digits = 2)

Print a floating-point number to output stream.

Parameters
  • num: Number to print
  • digits: The decimal places to print (Default: 2, e.g. 21.35)
Return Value
  • size_t: Quantity of characters written to stream

size_t print(const Printable &p)

Prints a Printable object to output stream.

Parameters
  • p: Object to print
Return Value
  • size_t: Quantity of characters written to stream

size_t print(const String &s)

Prints a String to output stream.

Parameters
Return Value
  • size_t: Quantity of characters written to stream

size_t println()

Prints a newline to output stream.

Return Value
  • size_t: Quantity of characters written to stream

size_t println(const char str[])

Prints a c-string to output stream, appending newline.

Parameters
  • str: c-string to print
Return Value
  • size_t: Quantity of characters written to stream

size_t println(char c)

Prints a single character to output stream, appending newline.

Parameters
  • c: Character to print
Return Value
  • size_t: Quantity of characters written to stream

size_t println(double num, int digits = 2)

Print a floating-point number to output stream, appending newline.

Parameters
  • num: Number to print
  • digits: The decimal places to print (Default: 2, e.g. 21.35)
Return Value
  • size_t: Quantity of characters written to stream

size_t println(const Printable &p)

Prints a Printable object to output stream, appending newline.

Parameters
  • p: Object to print
Return Value
  • size_t: Quantity of characters written to stream

size_t println(const String &s)

Prints a String to output stream, appending newline.

Parameters
Return Value
  • size_t: Quantity of characters written to stream

size_t printf(const char *fmt, ...)

Prints a formatted c-string to output stream.

Note
Use standard printf placeholders, e.g. d for integer, s for c-string, etc.
Parameters
  • fmt: Pointer to formated c-string to print
  • ...: Parameters for placeholders within formated string
Return Value
  • size_t: Quantity of characters written to stream

Public Members

Error errorCode = Error::None

Error code. Only relevant if hasError() returns true.

Public Static Functions

static String errorToString(Error code)

Convert error code to string.

See
errorCode

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.

Print an integral number to output stream

Parameters
  • num: Number to print
  • base: The base for output (Default: Decimal (base 10))
Return Value
  • size_t: Quantity of characters written to stream

size_t print(unsigned long num, int base = DEC)
size_t print(const unsigned long long &num, int base = DEC)
size_t print(long, int base = DEC)
size_t print(const long long&, int base = DEC)
size_t print(unsigned int num, int base = DEC)
size_t print(unsigned char num, int base = DEC)
size_t print(int num, int base = DEC)

Print an integral number to output stream, appending newline

Parameters
  • num: Number to print
  • base: The base for output (Default: Decimal (base 10))
Return Value
  • size_t: Quantity of characters written to stream

size_t println(unsigned char num, int base = DEC)
size_t println(unsigned int num, int base = DEC)
size_t println(unsigned long num, int base = DEC)
size_t println(const unsigned long long &num, int base = DEC)
size_t println(int num, int base = DEC)
size_t println(long num, int base = DEC)
size_t println(const long long &num, int base = DEC)

Public Types

enum Error

Error code values.

Values:

None

No error occured thus far (default value of errorCode if hasError() returns false)

InvalidFormat

Invalid/unsupported upgrade file format.

UnsupportedData

Some content of the upgrade file is not supported by this version of OtaUpgradeStream.

DecryptionFailed

Decryption failed. Probably wrong decryption key.

NoRomFound

The file did not contain a ROM image suitable for the start address of the slot to upgrade.

RomTooLarge

The contained ROM image does not fit into the application firmware slot.

DowngradeNotAllowed

Attempt to downgrade to older firmware version.

VerificationFailed

Signature/checksum verification failed - updated ROM not activated.

FlashWriteFailed

Error while writing to Flash memory.

RomActivationFailed

Error while activating updated ROM slot.

OutOfMemory

Dynamic memory allocation failed.

Internal

An unexpected error occured.

Public Functions

EncryptedStream()
~EncryptedStream()
size_t write(const uint8_t *data, size_t size)

Process an arbitrarily sized chunk of an encrypted OTA upgrade file.

Return
If less than size, an error occured. Check for more details.
Note
size does not have to match the chunk size used by otatool.py
Parameters
  • data: Pointer to chunk of data.
  • size: Size of chunk pointed to by data in bytes.

size_t write(uint8_t charToWrite)

Write a single character to the stream.

Parameters
  • charToWrite:
Return Value
  • size_t: Number of chars written (1 on success, 0 on failure)

size_t write(const char *str)

Writes a c-string to output stream.

Parameters
  • str: Pointer to c-string
Return Value
  • size_t: Quantity of characters written to stream

size_t write(const char *buffer, size_t size)

Writes characters from a buffer to output stream.

Parameters
  • buffer: Pointer to character buffer
  • size: Quantity of characters to write
Return Value
  • size_t: Quantity of characters written to stream

bool hasError() const

Returns true if an error happened during the upgrade process.

See
errorCode

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

virtual int available()

Return the total length of the stream.

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

bool isFinished()

Check if all data has been read.

Return Value
  • bool: True on success.

virtual size_t copyFrom(IDataSourceStream *source, size_t size)

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

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

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, unsigned 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: SEEK_SET, SEEK_CUR, SEEK_END
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.

int length()

Return the total length of the stream.

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

void flush()
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

String readString(size_t maxLen = UINT16_MAX)

Overrides Stream method for more efficient reading.

Note
Content is read using readMemoryBlock() so read position (for seekable streams) is not changed

String readString()
void setTimeout(unsigned long timeout)
bool find(char *target)
bool find(char *target, size_t length)
bool findUntil(char *target, char *terminator)
bool findUntil(char *target, size_t targetLen, char *terminate, size_t termLen)
long parseInt()
float parseFloat()
size_t readBytes(char *buffer, size_t length)
size_t readBytesUntil(char terminator, char *buffer, size_t length)
String readStringUntil(char terminator)
virtual int indexOf(char c)
int getWriteError()

Gets last error.

Return Value
  • int: Error number of last write error

void clearWriteError()

Clears the last write error.

size_t print(char c)

Prints a single character to output stream.

Parameters
  • c: Character to print
Return Value
  • size_t: Quantity of characters written to stream

size_t print(const char str[])

Prints a c-string to output stream.

Parameters
  • str: c-string to print
Return Value
  • size_t: Quantity of characters written to output stream

size_t print(double num, int digits = 2)

Print a floating-point number to output stream.

Parameters
  • num: Number to print
  • digits: The decimal places to print (Default: 2, e.g. 21.35)
Return Value
  • size_t: Quantity of characters written to stream

size_t print(const Printable &p)

Prints a Printable object to output stream.

Parameters
  • p: Object to print
Return Value
  • size_t: Quantity of characters written to stream

size_t print(const String &s)

Prints a String to output stream.

Parameters
Return Value
  • size_t: Quantity of characters written to stream

size_t println()

Prints a newline to output stream.

Return Value
  • size_t: Quantity of characters written to stream

size_t println(const char str[])

Prints a c-string to output stream, appending newline.

Parameters
  • str: c-string to print
Return Value
  • size_t: Quantity of characters written to stream

size_t println(char c)

Prints a single character to output stream, appending newline.

Parameters
  • c: Character to print
Return Value
  • size_t: Quantity of characters written to stream

size_t println(double num, int digits = 2)

Print a floating-point number to output stream, appending newline.

Parameters
  • num: Number to print
  • digits: The decimal places to print (Default: 2, e.g. 21.35)
Return Value
  • size_t: Quantity of characters written to stream

size_t println(const Printable &p)

Prints a Printable object to output stream, appending newline.

Parameters
  • p: Object to print
Return Value
  • size_t: Quantity of characters written to stream

size_t println(const String &s)

Prints a String to output stream, appending newline.

Parameters
Return Value
  • size_t: Quantity of characters written to stream

size_t printf(const char *fmt, ...)

Prints a formatted c-string to output stream.

Note
Use standard printf placeholders, e.g. d for integer, s for c-string, etc.
Parameters
  • fmt: Pointer to formated c-string to print
  • ...: Parameters for placeholders within formated string
Return Value
  • size_t: Quantity of characters written to stream

Public Members

uint8_t header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]
uint16_t chunkSizeMinusOne
Error errorCode = Error::None

Error code. Only relevant if hasError() returns true.

Public Static Functions

static String errorToString(Error code)

Convert error code to string.

See
errorCode

References

rc-switch

Build Status

Use your Arduino or Raspberry Pi to operate remote radio controlled devices

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

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

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-d4b3423f-bdae-4108-93df-f6243778b10a.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

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

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

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

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

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

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

API Documentation

This is a separate section built using DOXYGEN, which will replace this file when built.

Information

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.

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 SPI_MODE=dio SPI_SIZE=4M 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_RELASE=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).

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 for Sming for an example.

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

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 and spiffy tool.
  • 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.

Components as submodules All component.mk files must be available for parsing. For submodules, it can be provided in a .patch/ sub-directory.

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.

You can find general details for the memory layout in the ESP8266 Wiki.

This is the layout for Sming with 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
Partition Tables

{ todo }

Whilst SDK version 3 requires a partition table, previous versions do not but this can be added so that we can use it as a common reference for all the above locations.

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.

Task Schedulers

At present Sming doesn’t provide any structured (class-based) support for task scheduling, however there are various scheduling 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 JTAG Signal
1 VCC VCC
2 MTDO / GPIO15 TDO
3 MTDI / GPIO12 TDI
4 MTCK / GPIO13 TCK
5 MTMS / GPIO14 TMS
6 GND 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 board/esp-wroom-32.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 with the command below

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. Set the following options in your project’s component.mk file:

RBOOT_ROM1_ADDR = 0x80000
SPI_SIZE        = 1M
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.

If you have to 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();
uint32_t address = (slot == 0) ? RBOOT_SPIFFS_0 : RBOOT_SPIFFS_1;
//debugf("trying to mount SPIFFS at %x, length %d", address, SPIFF_SIZE);
spiffs_mount_manual(address, SPIFF_SIZE);
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. Add the following code:

RbootHttpUpdater* otaUpdater = nullptr;

void OtaUpdate_CallBack(RbootHttpUpdater& client, bool result)
{
    if (result) {
        // success - switch slot
        uint8_t slot = rboot_get_current_rom();
        if (slot == 0) {
            slot = 1;
        } else {
            slot = 0;
        }
        // set to boot new ROM and then reboot
        Serial.printf("Firmware updated, rebooting to ROM %d...\r\n", slot);
        rboot_set_current_rom(slot);
        System.restart();
    } else {
        // fail
        Serial.println("Firmware update failed!");
    }
}

void OtaUpdate()
{
    // need a clean object, otherwise if run before and failed will not run again
    delete otaUpdater;
    otaUpdater = new RbootHttpUpdater();

    // select ROM slot to flash
    rboot_config bootconf = rboot_get_config();
    uint8_t slot = bootconf.current_rom;
    if (slot == 0) {
        slot = 1;
    } else {
        slot = 0;
    }

#ifndef RBOOT_TWO_ROMS
    // flash ROM to position indicated in the rBoot config ROM table
    otaUpdater->addItem(bootconf.roms[slot], ROM_0_URL);
#else
    // flash appropriate ROM
    otaUpdater->addItem(bootconf.roms[slot], (slot == 0) ? ROM_0_URL : ROM_1_URL);
#endif

    // use user supplied values (defaults for 4MB flash in makefile)
    otaUpdater->addItem((slot == 0) ? RBOOT_SPIFFS_0 : RBOOT_SPIFFS_1, SPIFFS_URL);

    // set a callback
    otaUpdater->setCallback(OtaUpdate_CallBack);

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

You will need to define ROM_0_URL, ROM_1_URL and SPIFFS_URL with http urls for the files to download.

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

Hot Tips!

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.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 };

typedef struct {
    u8  prefix[PREFIX_SIZE];
    u8  signature[SIGNATURE_SIZE];
} MyHdr;

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

Development guides

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
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 described in our Style Guide. The coding style rules are mandatory for the Sming/SmingCore and samples directories and all their sub-directories. And they should be applied to all C, C++ and header files in those directories.

A Pull Request that does not adhere to the coding style rules will not be merged until those rules are applied.

Tools

Tools will help you easily adhere to the coding standards described in our Style Guide without the need to know them by heart.

Installation
Clang-Format and Clang-Tidy

In order to automatically check and apply our coding standards you need to install clang-format and optionally clang-tidy.

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.

We are using version 6.0 of clang-format and clang-tidy on our Continuous Integration (CI) System. If possible try to install the same version or newer on your development computer.

Configuration
Rules

The coding rules are described in the .clang-format which can be found in the root directory of the project. You don’t have to change anything on this file unless it is discussed and agreed coding style change.

IDE integration

There are multiple existing integrations for IDEs that can be found at the bottom of that page ClangFormat.html.

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 either configure Eclipse to auto-format your files on “Save” or 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).

Style Guide

You don’t have to know by heart the coding style but it is worth having an idea about our rules. Below are described some of them. Those rules will be can be automatically applied as mentioned in the previous chapter.

Indentation

We use tabs for indentation. Configure your editor to display a tab as long as 4 spaces. The corresponding settings in clang-format are:

TabWidth:        4
UseTab:          Always
IndentWidth:     4
Naming
Identifier type Rules for naming Examples
Classes Class names 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).
class HttpClient {}
class HttpClientConnection {}
Methods 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.
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.

int i;
char c;
WebsocketClient* client;
Constants Constants must be written in uppercase characters separated by underscores. Constant names may contain digits if appropriate, but not as the first character.
#define MAX_PARTICIPANTS 10
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
Other Elements
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`

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
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-root-folder>/
    git submodule add \
       https://github.com/StefanBruens/ESP8266_new_pwm.git \
       Sming/Arch/Esp8266/Components/driver/new-pwm \
       --name ESP8266.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.

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

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

Visual Studio Code is free (as in “free beer”, and they claim the code is Open Source) code editor for Windows, Linux and Mac. While not as sophisticated in C/C++ support as full featured Visual Studio, it already has some official support with C/C++ extension from Microsoft. GNU Global replaces venerable ctags tool and help collecting symbols for Intellisense engine.

For easier integration make sure you have both ESP_HOME and SMING_HOME exported in your working environment. ${workspaceRoot} below is the directory with your project, this notation is used also in VS Code config files. All environment variables are available in configuration using this notation, eg. ${HOME} etc.

Fire and forget way
Easy way
  • install VS Code, extensions and tools
  • clone project skeleton using https://github.com/zgoda/sming-skel.git as source
  • remove unwanted bits (at least .git directory)
  • update paths configuration in ${workspaceRoot}/.vscode/c_cpp_properties.json - should already work out of the box with Linux providing you have ESP_HOME and SMING_HOME properly exported
Step by step
  • install VS Code, extensions and tools
  • update paths configuration in ${workspaceRoot}/.vscode/c_cpp_properties.json so the list includes toolchain include path (${ESP_HOME}/xtensa-lx106-elf/xtensa-lx106-elf/include), Sming framework include paths (${SMING_HOME} and ${SMING_HOME}/system/include) and possibly your project additional paths (eg. ${workspaceRoot}/lib), if you screw your configuration just close VS Code, delete this file and start from scratch
  • make sure path list in browse section contains the same entries as includePath list in root section
  • define RunOnSave task in your ${workspaceRoot}/.vscode/settings.json (create file if does not exist) to regenerate GNU Global database on every save, eg:
{
  "emeraldwalk.runonsave": {
    "commands": [
      {
        "match": "\\.(c|cpp|h|hpp)$",
        "isAsync": true,
        "cmd": "gtags ${workspaceRoot}"
      }
    ]
  }
}
  • create file ${workspaceRoot}/.vscode/tasks.json and define tasks you want to run from command palette, eg minimal set:
{
  "version": "0.1.0",
  "command": "make",
  "isShellCommand": true,
  "showOutput": "always",
  "echoCommand": true,
  "suppressTaskName": true,
  "tasks": [
    {
      "taskName": "Build",
      "isBuildCommand": true
    },
    {
      "taskName": "Clean",
      "args": [
        "clean"
      ]
    },
    {
      "taskName": "Flash",
      "args": [
        "flash"
      ]
    }
  ]
}
  • add tools and binary artifacts to .gitignore, eg:
out

# development tools
.vscode
GTAGS
GRTAGS
GPATH

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. Set the SPI_SIZE of your flash memory. Example: If you have device with 4 megabytes make sure that the following is set:
export SPI_SIZE=4M
  1. Run flashinit.

Run the following commands.

cd $SMING_HOME/../samples/Basic_Blink
make flashinit

flashinit is erasing the current flash memory and populating some areas on it with the bytes that your SDK / BootROM is expecting to be present.

This command needs to be executed only when you change SDKs or memory layouts.

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

ESP8266

This is the documentation for the API of Sming, a C++ framework for development on the ESP8266 microcontroller platform.

The ESP8266 is a microcontroller with Wi-Fi, manufactured by Espressif Systems.

The microcontroller is used in modules from various manufacturers, each of which present a subset of the interface pins. For example, the ESP-01 only provides 2 digital GPIO whereas the ESP-12 provides 9 digital GPIO plus ADC analogue input.

Sming provides a C++ framework that facilitates the creation of application code for the ESP8266. The core framework provides access to the ESP8266 functions such as GPIO, timers, WiFi configuration, etc. A set of libraries extend this functionality such as switch debounce, SD Card interface, etc.

Licenses

Sming Core
LGPL v3
Espressif SDK
ESPRSSIF MIT License (with some closed-source blobs)
Libraries
See each library for details of its own open source license

Indices and tables