Dart in the IoT world — FFI, a first class sensor interface
Those who read my first article on Dart in the IOT world may remember that to get values from our light and temperature sensors we had to call out of Dart into Python scripts to interface to the sensors through the Python implementation of the system Mraa and UPM libraries. This was sufficient for what was needed but falls far short of full platform sensor control which is what we really need in Dart as we have in Python and other languages. Fortunately as of Dart 2.6.0 we can now do this using the new Dart feature called Foreign Function Interface(FFI). FFI allows us to use the native Mraa C library straight from Dart and manipulate sensors and associated devices on a lower, fine grained level.
Before explaining what FFI does for us its useful to recap how Dart has traditionally handled interfacing to platform native C libraries. This was achieved through a mechanism called ‘native extensions’. If you look in the ‘include’ folder of the Dart SDK you will see some header files allowing you to create your own shared library that interfaced the Dart VM to your target platform library. Basically the Dart VM was linked to your supplied shared library which itself loaded the target system library forming a data bridge from the Dart VM to the target library. An example of this can be seen in a Dart project I did some years ago interfacing Dart to AMQ. This directory contains the cpp files needed and this file shows how it looked in Dart world, basically stubbed functions annotated with the ‘native’ keyword.
As you can see this has some large drawbacks, you have to be proficient in C/C++ as well as Dart to use the native extensions and there is also a distribution problem in that you should really have a shared library for each platform you intend to support, i.e. Fedora, Ubuntu etc. and of course a DLL for Windows, you also have a further problem with publishing as these artifacts are not really part of the package you upload to pub, to say nothing of versioning. This all gets very messy very quickly and is only really of use for custom builds and one offs.
So, how does FFI help here? Easy, it gets rid of the intermediate shared library and instead lets you load and interface to your system target library directly in Dart. For instance, using Mraa as an example you first load the library :-
lib = ffi.DynamicLibrary.open(‘libmraa.so’);
then set up some typedefs to link the C functions in the library to your Dart functions and call them directly. FFI does all the data marshaling/unmarshaling you had to write yourself using native extensions, a real killer feature. Examples of how this is achieved can be found via the Dart docs and in the Dart Mraa package I’m working on here.
Using FFI we now have the full power of native platform libraries available to Dart, leveling the playing field somewhat with Python Et al.
I’ve updated the iot-home project from my original article to use the Dart Mraa package and get its sensor data that way, no more Python!
Note, FFI is not yet stable and is thus subject to change and it still can’t as of the time of writing handle asynchronous callbacks from the target system library(neither could native extensions for that matter so we have lost nothing here), however I’m sure the Google guys are working on this and other features.
OK, so now I’ve just got to finish Mraa, package UPM and see where are then and what we can now do with our new found powers.