C/C++ for Microcontrollers - API Bindings

The C/C++ bindings for microcontrollers allow you to control Bricks and Bricklets with a C/C++ programm running on a microcontroller. The ZIP file for the bindings contains:

  • in source/bindings the source code of the bindings

  • in source/hal_* the source code of HALs for common platforms.

  • in examples/ the examples for every supported Brick and Bricklet

Supported Hardware

The C/C++ bindings for microcontrollers support Co-processor Bricklets (i.e. those with a 7-pole connector), as well as the HAT Brick and HAT Zero Brick. The older 10-pole Bricklets and other Bricks are not supported. See here for a complete list of supported devices.

As host (i.e. the device running your C/C++ program that uses the bindings), you can currently use the ESP32 Brick and ESP32 Ethernet Brick. Support for other hosts will be added in the future.

The bindings require a hardware abstraction layer (HAL). See here for details.

Requirements

  • C compiler (C99 compatible) or C++ compiler

Installation

Because there is no precompiled library for the bindings there is nothing to install as such. The recommended way of using the bindings is to include their bindings and your preferred HAL directly into your C/C++ project.

Testing an Example

The examples for each supported Brick and Bricklet are not self-contained, as they depend on the host hardware (and its HAL) you want to use. Many HALs contain an example driver that can run any example. See the specific HAL documentation for examples.

UID or Port Name

All device *_create functions take a uid_or_port_name parameter allowing the program to specify which device to communicate with. This can be done in three ways:

  • Passing NULL selects the first unused device with matching type, regardless of UID or port name.

  • Passing a UID such as "XyZ" selects the first unused device with matching UID and type.

  • Passing a port name such as "A" selects the first unused device with matching port name and type.

In this case unused means the device is not attached to a device object. For example, there are two Temperature Bricklet 2.0, one connected to port A and one to port B. With passing NULL the first tf_temperature_v2_create call picks the one on port A, the second call picks the one on port B, because the one on port A is in use already.

Hardware Abstraction Layer

The C/C++ bindings for microcontrollers work somewhat different to the other available bindings. While other bindings, that communicate to a Brick Daemon over TCP/IP, require a IP connection, the C/C++ bindings for microcontrollers require usage of a hardware abstraction layer (HAL). The HAL is an abstraction over the platform specific way to communicate over SPI.

The bindings already contain HALs for the following platforms. For other platforms, a custom HAL must be implemented (see below).

Implementing a Custom HAL

If you want to use the bindings on another platform, you have to implement a custom HAL. This guide shows how.

Callbacks

Callbacks in the C/C++ bindings for microcontrollers work differently than those in the other bindings. As there is no multi-threading on many of the target platforms, there is no automatic polling for callbacks. Instead, if you want to receive callbacks, you have to poll for them yourself with the tf_hal_callback_tick available in every HAL. This function will poll any device with a registered callback handler for new packets.

Callbacks are not dispatched by another thread, but instead while executing a getter, setter or the tf_hal_callback_tick function. Because of this, callback handlers will run while the internal state machines of the bindings are in states, that do not support sending other packets. Because of this, calling getters, setters or tick functions inside a callback handler is not allowed.

Thread safety

As the primary target for these bindings are microcontrollers, the bindings are not thread safe. Some HALs support cooperative multi-tasking, however no calls to the bindings API are allowed when they have yielded. All functions will return TF_E_LOCKED if called inside a callback handler or while the bindings are yielded.

Optimizing Performance

To get the best performance out of the bindings, you can follow this list of optimizations:

  • Increase the SPI clock speed from 1.4 MHz to as close to 2 MHz as you can. Note that a too high clock speed will result in worse performance, as the attached Devices will fail to receive the payload. In our tests, the best performance can be reached at about 1.95 to 1.96 MHz, however this depends on your setup, the used HAL, as well as the stability of the host system's SPI clock.

  • Prefer to use callbacks. The bindings can poll a device for an available callback by clocking out a single byte. If you use getters to poll for rapidly changing state, a complete TFP request, response and acknowledgment must be sent over SPI, resulting in at least 23 bytes of data to send.

  • If you know exactly what callbacks to expect in what intervals, don't use tf_hal_callback_tick, instead use your own scheduling to poll with the specific tf_[device]_callback_tick functions. This allows spending more time on other tasks instead of polling the devices all the time. tf_hal_callback_tick uses a round-robin scheduler to poll all devices with a registered callback handler.

  • If multiple SPI devices are available, use multiple HAL instances. This will only work with HALs that support using a specific SPI device. However you can then communicate with multiple Devices in parallel. Each HAL instance may still only be used by a single thread.

Optimizing Flash and RAM Usage

To fit a firmware using the bindings onto smaller platforms, such as the Arduino Uno, you can change the following configuration options in bindings/config.h:

  • TF_INVENTORY_SIZE: This is the size of the inventory i.e. the mapping from UIDs to the ports under which they are reachable. If you know exactly how many devices you want to communicate with, set the inventory size to this number. Note that the HAL initialization will return TF_E_TOO_MANY_DEVICES if the inventory size is too small.

  • TF_IMPLEMENT_CALLBACKS: If you don't want to use callbacks, remove this define to remove the callback implementation.

  • TF_LOG_LEVEL: Reducing the log level will remove string constants at compile time. Valid values are

    • TF_LOG_LEVEL_NONE: Disables the logging completely

    • TF_LOG_LEVEL_ERROR: Logs only some HAL specific errors if they occur.

    • TF_LOG_LEVEL_INFO: Additionally logs the initial list of detected devices (default)

    • TF_LOG_LEVEL_DEBUG: Additionally logs all internal state changes in the SPITFP state machine

  • TF_IMPLEMENT_STRERROR: If you remove this define, the tf_hal_strerror function will not be implemented. This saves about 500 bytes of flash or RAM, depending on the HAL.

API Reference and Examples

Links to the API reference for the HALs, Bricks and Bricklets as well as the examples from the ZIP file of the bindings are listed in the following table. Further project descriptions can be found in the Kits section.

Name, API, Examples

Miscellaneous

HAL Arduino

API

Examples

HAL Arduino ESP32

API

Examples

HAL Arduino ESP32 Brick

API

Examples

HAL Arduino ESP32 Ethernet Brick

API

Examples

HAL Linux

API

Examples

HAL Raspberry Pi

API

Examples

Bricks

HAT

API

Examples

HAT Zero

API

Examples

Bricklets

Accelerometer 2.0

API

Examples

Air Quality

API

Examples

Ambient Light 3.0

API

Examples

Analog In 3.0

API

Examples

Analog Out 3.0

API

Examples

Barometer 2.0

API

Examples

CAN 2.0

API

Examples

CO2 2.0

API

Examples

Color 2.0

API

Examples

Compass

API

Examples

DC 2.0

API

Examples

Distance IR 2.0

API

Examples

Distance US 2.0

API

Examples

DMX

API

Examples

Dual Button 2.0

API

Examples

E-Paper 296x128

API

Examples

Energy Monitor

API

Examples

GPS 2.0

API

Examples

GPS 3.0

API

Examples

Hall Effect 2.0

API

Examples

Humidity 2.0

API

Examples

IMU 3.0

API

Examples

Industrial Analog Out 2.0

API

Examples

Industrial Counter

API

Examples

Industrial Digital In 4 2.0

API

Examples

Industrial Digital Out 4 2.0

API

Examples

Industrial Dual 0-20mA 2.0

API

Examples

Industrial Dual AC In

API

Industrial Dual AC Relay

API

Examples

Industrial Dual Analog In 2.0

API

Examples

Industrial Dual Relay

API

Examples

Industrial PTC

API

Examples

Industrial Quad Relay 2.0

API

Examples

IO-16 2.0

API

Examples

IO-4 2.0

API

Examples

Isolator

API

Examples

Joystick 2.0

API

Examples

Laser Range Finder 2.0

API

Examples

LCD 128x64

API

Examples

LED Strip 2.0

API

Examples

Linear Poti 2.0

API

Examples

Load Cell 2.0

API

Examples

Motion Detector 2.0

API

Examples

Motorized Linear Poti

API

Examples

Multi Touch 2.0

API

Examples

NFC

API

Examples

OLED 128x64 2.0

API

Examples

One Wire

API

Examples

Outdoor Weather

API

Examples

Particulate Matter

API

Examples

Performance DC

API

Examples

Piezo Speaker 2.0

API

Examples

Real-Time Clock 2.0

API

Examples

Remote Switch 2.0

API

Examples

RGB LED 2.0

API

Examples

RGB LED Button

API

Examples

Rotary Encoder 2.0

API

Examples

Rotary Poti 2.0

API

Examples

RS232 2.0

API

Examples

RS485

API

Examples

Segment Display 4x7 2.0

API

Examples

Servo 2.0

API

Examples

Silent Stepper 2.0

API

Examples

Solid State Relay 2.0

API

Examples

Sound Pressure Level

API

Examples

Temperature 2.0

API

Examples

Temperature IR 2.0

API

Examples

Thermal Imaging

API

Examples

Thermocouple 2.0

API

Examples

UV Light 2.0

API

Examples

Voltage/Current 2.0

API

Examples

XMC1400 Breakout

API

Examples

Bricklets (Discontinued)

PTC 2.0

API

Examples

RGB LED Matrix

API