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
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.
C compiler (C99 compatible) or C++ compiler
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.
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.
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.
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).
HAL Arduino for Arduino compatible boards with an AVR or ARM processor
HAL Arduino ESP32 for Arduino compatible boards with ESP32 processor
HAL Arduino ESP32 Brick for ESP32 Brick with Arduino base
HAL Arduino ESP32 Ethernet Brick for ESP32 Ethernet Brick with Arduino base
HAL Linux for Linux systems with spidev-support
HAL Raspberry Pi for Raspberry Pi 2, 3, 4, and Zero with the BCM2835 chip
If you want to use the bindings on another platform, you have to implement a custom HAL. This guide shows how.
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.
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.
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.
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.
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 |
||
Bricks |
||
Bricklets |
||
Bricklets (Discontinued) |
||