Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
286 lines (208 sloc) 16.4 KB

How to Port the Azure IoT C SDK to Other Platforms

The purpose of this document is to provide guidance on how to port the C Shared utility library to platforms not supported out of the box. The C shared utility library is used by C SDKs like IoTHub client SDK and EventHub client SDK.

The document does not cover the specifics of any particular platform.


Header files

Table of contents


The C shared utility library is written in C99 for the purpose of portability to most platforms. However, several components rely on platform-specific resources in order to achieve the functionality required. Thus, porting the C shared utility library is done by porting the PAL components (adapters) that are part of the library. Below is a rough overview of the components:

There are several mandatory components for which an adapter must be provided:

  • A tickcounter implementation: this provides the SDK an adapter for getting a tick counter expressed in ms. The precision does not have to be milliseconds, but rather the value provided to the SDK has to be expressed in milliseconds.

  • An agenttime implementation: this provides the SDK adapters for the C time management functions like time, difftime, etc. This is needed due to the very wide spread differences in the way time is handled by different platforms.

  • A sleep implementation to provide a device-independent sleep function.

  • A platform implementation to perform global init and de-init.

  • A tlsio implementation to allow the SDK to communicate over TLS. The IoT Hub does not support insecure communication.

Additionally, there are two optional components, threadapi and lock, that allow the SDK to communicate with an IoT Hub on a dedicated thread. Yet another optional component is the socketio adapter, which is used with some kinds of tlsio adapter implementations.

There are several existing adapters that can be found under the SDK's adapters directory and source directory. If any of these adapters work for your device, simply include that file in your project directly.

tickcounter adapter

Tickcounter behavior is specified in the tickcounter specification.

To get started, copy this version of tickcounter.c file and modify it to suit your device.

If memory size is an issue, the tickcounter specification contains an optimization suggestion that may work for you.

agenttime adapter

The agenttime adapter is specified in agenttime adapter specification and provides platform-independent time functions.

For most platforms/OSs you can include the standard agenttime.c file in your build. This adapter simply calls the C functions time, difftime, ctime, etc.

If this file does not work for your implementation, make a copy of it and modify it appropriately.

The Azure IoT SDK only requires the get_time and get_difftime functions. The other functions in this adapter -- get_gmtime, get_mktime, and get_ctime -- are deprecated and may be omitted or left non-functional.

sleep adapter

The sleep adapter is a single function that provides a device-independent thread sleep. Unlike other adapters, it does not have its own header file. Instead, its declaration is contained in the threadapi.h file along with the declarations for the optional threadapi adapter, and its implementation is typically contained in an associated threadapi.c file.

Unlike the rest of the functions in threadapi.h, however, ThreadAPI_Sleep is required by the SDK and must always be functional.

The specification for the sleep adapter is found in the threadapi and sleep adapter specification.

platform adapter

The platform adapter performs one time initialization and deinitialization for the platform and also supplies the SDK with an appropriate TLSIO.

The platform adapter specification gives full instructions for writing the platform adapter.

To get started creating your platform adapter, copy this Windows platform.c file and modify it to suit your needs.

threadapi and lock adapters

The threadapi and lock adapters must exist for the SDK to compile, but their functionality is optional. Their specification documents (see below) detail what each empty function should do if threading functionality is not needed.

These components that allow the SDK to communicate with an IoT Hub within a dedicated thread. The use of a dedicated thread has some cost in memory consumption because of the need for a separate stack, which may make the dedicated thread difficult to use on devices with little free memory.

The upside of the dedicated thread is that all current tlsio adapters may repeatedly block for some fraction of a minute when attempting to connect to their IoT Hub when the network is unavailable, and having a thread dedicated to the Azure IoT SDK will allow other device functions to remain responsive while the SDK is blocked.

Future versions of the SDK may eliminate this potential blocking behavior, but for now, devices which must be responsive will need to use a dedicated thread for the SDK, which requires implementing the ThreadApi and Lock adapters.

Here are the specs for the threadapi and lock adapters:

These specs explain how to create null lock and threadapi adapters for when threading is not desired.

To get started creating your threadapi and lock adapters, copy these Windows adapter files and modify them appropriately:

tlsio adapter overview

A tlsio adapter provides the SDK with secure TLS communication wtih the Azure IoT Hub.

Tlsio adapters expose their functionality to the SDK via xio, which is a generic C-language bits-in-bits-out interface defined here.

A tlsio adapter instance is created via the adapter's xio_create function, and the configuration parameter const void* io_create_parameters must be of the specialized type TLSIO_CONFIG when creating the tlsio instance.

Implementation of tlsio adapters for new devices is done by selecting the existing tlsio adapter (and perhaps also a socketio adapter) that most closely fits your needs and modifying it accordingly.

The two styles of tlsio

There are two possible design patterns for a tlsio adapter: direct, and chained. In the direct style, the tlsio adapter owns a TCP socket which it uses to directly perform TLS communication with the remote host. In the chained style, the tlsio adapter does not own a TCP socket and does not communicate directly with the remote host. Instead, it still does all the TLS chores like encryption, decryption, and negotiation, but it communicates with the remote host indirectly via yet another xio-style adapter of some sort.

The direct style adapters require less code and avoid extra memory buffer copying, so they are more suited to microcontrollers. The tlsio adapters for Arduino and ESP32 are both direct types. The chained style adapters are more resource-hungry, but they offer more flexibilty. The tlsio adapters for Windows, Linux, and Mac are all chained style adapters.

The choice of TLS implementation may dictate the style of tlsio. For example, the TLS implementations for Arduino and Espressif's OpenSSL implementation for ESP32 can only work directly with their own internal TCP socket, so they can only be used as part of a direct style tlsio. By contrast, the full official version of OpenSSL can be used either way.

socketio adapter overview

In order to connect to the internet, a chained tlsio must at some point talk through an xio adapter that contains a TCP socket. In the Azure IoT SDK, an xio adapter managing a TCP socket is called a socketio adapter.

Multiple xio components can be chained together if desired. Here is a diagram illustrating the differences between a direct tlsio, a chained tlsio, and a chained tlsio with an xio-based http proxy component: The xio_http_proxy component is only shown to illustrate xio capability. Its details are beyond the scope of this document.

tlsio adapter implementation

Adapters other than tlsio are easy to implement, even for inexperienced developers. The tlsio adapters, however, are quite complicated, and writing a tlsio adapter is a task only for experienced developers who are comfortable setting up include directories and external libraries without instruction.

The most up-to-date information on tlsio implementation is contained in the tlsio specification.

All of the existing tlsio adapters use TLS libraries that are not part of the Azure IoT SDK. Refer to the documentation for the specific TSL libraries for instructions on library usage such as setting include directories and linking library files.

Existing direct tlsio implementations

There are two existing direct adapter implementations:

Of these two, the tlsio_openssl_compact for ESP32 is probably the better candidate for copying for re-use because it is more likely to resemble newer devices, and it was written in tandem with the tlsio specification.

The tlsio_openssl_compact for ESP32 abstracts its operating system dependencies using these two files:

It is recommended that all direct tlsio implementatons follow this pattern.

The socket_async.c and dns_async.c files can be re-used without change for most socket implementations by merely changing the content of the included "socket_async_os.h" file, which contains os-specific headers.

Existing chained tlsio implementations

Chained adapter implementations include:

Existing socketio implementations for chained tlsio adapters

There is no spec for socketio adapters, so it is necessary to copy the behavior of an existing one, and socketio_berkeley is the best candidate for copying. However, be aware that all existing socketio adapters including socketio_berkeley purge their pending io lists during socketio_destroy, which is incorrect. The pending io lists should be purged during socketio_close instead.

Adding device support repositories

Newly supported devices are likely to use a wide variety of possible build systems, so no single standard source tree can be prescribed. The support repository for ESP32 is the one we recommend considering as a model for creating a new device support repository.

Last step: profit!