From IoT with AME
Revision as of 20:09, 15 June 2018 by Ctreber (Talk | contribs)

Jump to: navigation, search


Software for Building IoT Devices

Generation 1: PubSubClient, ESP8266Client, ITEADLIB Arduino WeeESP8266

This is the stack I used first, and for quite some time:

  • MQTT client: PubSubClient. Regarding the data connection it is based on the standard Arduino Client interface.
  • Client: ESP8266Client: This is a library I wrote that provides a Client facade to the ITEADLIB Arduino WeeESP8266 lib. It deals with ESP8266 misbehavior that can't be handled on the ESP8266 level itself (like, lost TCP or WLAN connectivity).
  • ESP8266: ITEADLIB Arduino WeeESP8266. This library is quite voluminous (in example, because of the use of String) and has a few small bugs

Generation 2: AC_MQTT & AC_ESP8266

After dealing with various problems and repairing and optimizing things to a degree I finally did what all programmers must do at some point: rewrite the whole damn thing :-)

  • MQTT client: AC_MQTT. Compact and easy to understand code implementing MQTT operation for QoS levels 0 and 1. Does not reserve buffers and performs only streaming read and write operations.
  • "Client": AC_ESP8266Client. This is NOT the Arduino standard Client interface, but a tighter integration between AC_MQTT and AC_ESP8266. While it would have been nice to stick to the Arduino Client interface I found this requires too much of a compromise in terms of excessive memory use and type of access ("Client" is rather serial while the ESP8266 expects reading and writing buffers). It deals with all kinds of problems like WLAN and TCP connection troubles and general ESP8266 misbehavior. Depending on the hardware provided it can even hardware-reset or power-cycle the ESP8266 if necessary. It can even power-cycle a router if so required (yes, sometimes a mobile Internet router deserves and profits from a kick).
  • ESP8266: AC_ESP8266. The library only provides access to ESP functions that are needed for MQTT communications. The code is pretty compact as it does not reserve buffers on its own (though it relies on buffers provided by clients). It provides a callback interface in case some data arrived over an IP connection (no need to poll).

My stack does quite a bit more than the stack above regarding error handling, and it does so with less flash and SRAM memory usage while IMHO being more readable at the same time.

Generation 3: AC_IoT, AC_MQTT, AC_TCP_ESP8266, AC_ESP8266

And while the generation 2 solution was not bad per se I wanted more. I found that I wanted to deploy multiple devices of the same kind, but without having to change the configuration in multiple files. And I would like to simply plug in capabilities like measuring temperature or switching a relay. From an architectural standpoint, I prefer MQTT to interface with TCP, not with the ESP8266. As buffers are limited and awkward to handle (make them too big and memory is gone; make them too small and you'll get overruns), streaming is the preferred mode of data exchange. I want my code to be readable by humans. And, yes, this can be done. Even in the limited Arduino environment.

And thus generation 3 of my MQTT stack was born (and it is still growing up).


AC_ESP8266 provides access to the ESP8266 on C++ level, no more, no less. It uses two (blowing my own horn) pretty clever functions to compose commands and to parse responses based on pattern expansion/ matching. The buffer used for commands and responses is the only one in the whole class, as writing and reading IP data is performed streaming. Callbacks announce the arrival of data and loss of connection.


AC_TCP_ESP8266 provides streaming TCP functionality based on - you guessed it - the ESP8266. It deals with loss of TCP and WLAN connectivity and does what is required to get the ESP working again, which,frankly sometimes is not easy and a pain in the back. This includes resetting the chip via software and - optionally - hardware (it uses the reset pin of the ESP). If you want to, it even can turn the ESP off and on again. I think that does exhaust all options available.


AC_MTTQ provides standard MQTT functions with QoS 0 or 1 (or more honestly "0.5" as ACKs message from the broker properly, but arriving ACKs are discarded and missing ACKs dont' prompt retries). Unlike other libaries AC_MQTT does not get unhappy if a ping response gets lost while performing publish and subscribe operations.


AC_IoT provides convenience on device level with concepts such as centrally setting a device name and an MQTT-ID that get used wherever appropriate. In example, a device with MQTT-ID "garden01" uses "garden" as a prefix for topics in publications and subscriptions.


Devices report the version of the IoT framework, their own version, IP and MAC address, and number of connect attempts when the MQTT connection is established. In example, the messages

  • "garden/versionIoT 1.038"
  • "garden/version 1.010"
  • "garden/connectAttempts 12"
  • "garden/ip"
  • "garden/mac 5d:de:ad:be:ef"

would be sent.


Capabilities can easily be added to a device. For a gardening system you maybe add capabilities "temperature", "spoil moisture", "rain", and "relay" (ie, to turn an irrigation system on and off). A capability encapsulates hardware intricacies, posses a built-in name, and provides configuration options such as a capability ID (a number), update intervals, I/O pins and so on.

In example, a capability encapsulating a BMP280 air pressure and temperature sensor with the built-in name "pressure" has been assigned capability ID "1". The capability will produces publications at a configurable interval using the name of the device, the name of the capability, the capability ID, and the attribute name. Building on the example above, publications would use the topic "garden/pressure01/pressure" and "garden/pressure01/temperature".

Additionally, Capabilities report their version and input and output parameters when the MQTT connection is established. In example, the messages

  • "garden/pressure01/version 1.001"
  • "garden/pressure01/capa01 <pressure:<d>" and
  • "garden/pressure01/capa02 <temperature:<d>"

would be sent, indicating the production of two output attributes with the names "pressure" and "temperature" with numeric output format.