Tuesday, February 11, 2020

Data Serialization - Getting started with Wire Format Compiler

Getting started with Wire Format Compiler

Data serialization is the process of getting an in memory data structure into a stream of bytes that can be sent via a network connection or written to disk and back. For PCs, servers, and smart phones there are many options to do this. There is no wrong or right way to do it, but different concepts offer different advantages and disadvantages.

For embedded systems Wire Format Compiler is a tool that generates C++ source code that performs this task. The generated code is light-weight enough to run on small embedded systems. Its language has been inspired by Google's protocol buffers, but it offers numerous enhancements for use on embedded systems, and provides about 3-4 times better performance when it comes to processing time and ROM footprint. Additionally, with WFC you are able to even target very small embedded systems such as 16 bit devices or even 8 bit devices. For this many options exists that allow to tune the generated code by taking advantage of specific aspects of small devices, such as lower address space, register width or implementation concepts.

First simple Example

As a first example let's take a look at the configuration data of a potential embedded device. The data description in WFC source format looks like this:

message Config
{
        string     hostname   = 1;
        unsigned   baudrate   = 2;
        string     wifi_ssid  = 3;
        string     wifi_pass  = 4;
        bool       station    = 5;
        fixed32    ipv4       = 6;
        fixed8     netmask    = 7;
        fixed32    gateway    = 8;
}
This description specifies a data structure with several members. First the data type is specified, second is the name of that structure member, and last the unique identifier. The unique identifier must never be changed after bringing data into real world usage. The data type can be modified for later software updates with some restrictions, that I may talk about in a later post. Backward and even forward compatibility is very strong for WFC generated code, if you follow some rules when extending messages for a later release. Forward compatibility is achieved by being able to skip over unknown data structures, but still extracting what is well known.

The data types used in this example are:
  • string: this will be implemented as a std::string per default, but this can be changed to a target specific more appropriate type. How to do this, I will write about in a different post.
  • unsigned: This data type will be generated as a platform specific unsigned integer data type. Take care to make sure that the default unsigned type has the appropriate range of values. This data type is serialized as a variable width integer that will require 10 byte at most to be stored plus usually 1 or 2 bytes for the message member tag.
  • bool: This is just the regular boolean type. Data serialization relies also on the concept of variable length integer, but boolean data will of course only take 1 byte for its data. Due to relying on the variable integer concept a boolean type can later be changed to an enumerated data type or some integer type that is not serialzed in signed format.
  • fixed8 and fixed32: These are fixed width integers that will be represented as uint8_t and uint32_t accordingly. Fixed8 will require 1 byte, fixed32 needs 4 bytes in their serial representation plus the tag. 
Of course there are many more types. I will take a look at what other types are available in a later post.

Without any additional options WFC will generate code for serializing an deserializing the data structure to and from memory. Additionally, helper functions are generated to calculated the serialized size for a specific data instance, and to serialize the structure directly to a C++ std::string. Furthermore, options exist to also create an ASCII or JSON output for human interaction or interfacing with other tools.

Integrating the generated code into an application

To integrate the generated code into your application, you need to include the generated header file into your code and add the generated C++ source file into your build process. To make use of the generated code, simply create an instance of the classes defined in the generated head file. In this case the class is called Config, just as the message is called.

So let's take a look how initializing, serializing, and deserializing work in practice. This examples is also included in the WFC source distribution and contains multiple variants how to use the generated code. Here we just take a look at the variant with C++ streams:

To initialize the structure, just set every member of it:
void initDefaults(Config &cfg)
{
        cout << "initializing defaults\n";
        cfg.set_hostname("host001");
        cfg.set_baudrate(115200);
        cfg.set_wifi_ssid("mywifi");
        cfg.set_wifi_pass("1234567890");
}
Next let's serialize the data to a file:
void saveConfig(const Config &cfg)
{
        string str;
        cfg.toString(str);      // serialize the data
        ofstream out;
        out.open(CfgName);      // open file
        out << str;             // write to file
        if (out.good())
                cout << "writing config successful\n";
        else
                cerr << "error writing config\n";
}
Now that was also quite easy. We just serialize the data to a string and write the string to a file. That's it. So now the last important step comes. Deserializing the data:
int readConfig(Config &cfg, const char *CfgName)
{
        ifstream in;
        in.open(CfgName,ios::in);   /
/ open file
        in.seekg(0,ios::end);       // seek to end...
        ssize_t n = in.tellg();     // to get the size.
        in.seekg(0,ios::beg);       // back to begin
        if (n <= 0)
                return 1;
        char *buf = new char[n];    // allocate buffer
        in.read(buf,n);             // read data
       
        cfg.fromMemory(buf,n);      // parse the data
        delete[] buf;               // clean-up
        // That's it! Ready to go.
       
        cout << "read config from " << CfgName << endl;
        return 0;
}
Reading back is more complex, because we need to know how much memory to allocate and the C++ streams interface has no easy way to determine the size of a file. WFC even has a concept that enables it to find the end of the stream by itself. But this is an extension that I do not want to dive into here, because it needs some additional considerations. Restoring the serialized data is also just one line that asks the relevant class object to parse the data from memory. What is done behind the scene can be found in the code generated by WFC. But you don't have to understand anything about it, to use the generated code.

That's it for this post. The next posts will take a look at more data types, how to do some target specific optimizations, and how to use custom string types to reduce the footprint even further.

Get Wire Format Compiler. Happy hacking!

Automatic Versioning with Make/CMake and Mercurial

Keeping Version Strings Up-To-Date

A software package and its binary executables should have a version number or a release name. This is more or less common practice, due to the benefits one has, when it is possible to identify the version of a specific software.

But what is the best way to bring the version tag into the software and keeping it up-to-date? Traditionally, version information has been hard coded into the source code. This is the simplest and most straight forward approach, but this comes with a big question: When and how do you update the version to e.g. version 2.0?

It makes sense to do it in an exclusive commit to the revision control system of the project. Additionally, it should be done after performing all those release and regression tests. But if the version string is hard coded, you will be unable to figure out from which commit of your repository a specific binary was built, unless you update the version string on every commit. But if you cannot ensure that only the latest and greatest binary will be used, it can be quite important to identify to which commit a specific binary relates during development.

Furthermore, if you change the hard-coded version string after release testing, how do you know you didn't break the code while changing the version string? So another release test of the version with the correct string might become necessary. And, as we all know, manual tasks are prone to error. So sooner or later, manually updating the version string will be forgotten, the update will be incorrect or introduce some bug. Maybe the code will not compile or even worse the version string will be incorrect or whatever...

So the simple hard-coded version string concept may work well in many cases, but might not make everybody happy. 

Automatic Version Generation

Therefore, it makes sense to think about a better solution. A better approach is to generate the version string during build time. This concept also can provide more information about the build itself. E.g. hostname of build server, version of tools used for building, time of build and so on come to mind.

For this, support from the build system and the version control system is necessary. The build system must trigger an update of the relevant information and link it to the binary.

But how can this information be gathered, when it should be available at build time, but not hard-coded into the sources? One way to do it, is to use the infrastructure of Mercurial as a revision control system and its tagging mechanism. The advantage of this concept is that it also works for archives that have been generated by mercurial, but have no reference to the repository. With other revision control systems you might have to come up with another approach, but probably will find a similar solution.

Using GNU Make or CMake as build systems, Mercurial's infrastructure can easily be employed to provide the necessary information. Mercurial's tags provide the ability to associate a given commit with a version string. Like this a specific revision can be given a name or version after its commit has been submitted and tested. So you can do the commit, test it, and once you are sure all release prerequisites are fulfilled and it is ready for publication, you tag the revision with a version name without changing any code manually. This reduces the risk of breaking anything dramatically.

Furthermore, Mecurial also lets you query the distance to the latest tag. So if the latest tag is always the latest version number, the delta can be used as a patch level to the named version. For a mercurial repository the latest tag and distance as patch level can be queried with 'hg log -r . "{latesttag}.{latesttagdistance}"'.

Now, if the repository gets exported to a zip or tgz archive, the repository cannot be queried anymore. The good thing is that Mercurial creates a file called .hg_archival.txt that contains just this information. To extract the version information from this file some shell scripting with awk or grep and sed is necessary. All this is demonstrated here in a sample repository with a small shell script, which should work on all UNIX based systems like Linux, BSD, Solaris or MacOSX.

The version string itself must be written either to a header file or a source file for make and cmake being able to recognize a change in the version string and trigger the appropriate build steps, unless you want to do a full build every time. So putting the version string in a command line argument for a #define will not yield the intended result, as a source code delta might be overlooked by that approach by GNU Make and CMake. Therefore, just make sure to generate a source file that is picked up during the build process with all relevant version information that you would like to have integrated into the binary.

Example

Let's take a look at a small example, how to use the template repository. The demo repository contains a file called hello.c that prints the version that has compiled into its executable. Both CMake and GNU Make are supported. Support for BSD Make is missing, so on BSD and Solaris you will have to call gmake instead of make.

First, we start by cloning the template repository:
> hg clone mkversion.hg myproject 

Then we build the project with autoconf and make and take a look at what we get:

> ./configure
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking target system type... x86_64-pc-linux-gnu
checking for cc... cc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether cc accepts -g... yes
checking for cc option to accept ISO C89... none needed
configure: creating ./config.status
config.status: creating Makefile
> make
cc -MM -MG hello.c -o .depend
sh mkversion.sh
creating version.h
cc -g -O2   -c -o hello.o hello.c
cc -g -O2  hello.o -o hello
> cat version.h
#ifndef VERSION_H
#define VERSION_H
#define VERSION         "V0.1.1 (hg:1/7906498bc6e3)"
#define HG_REV          "1"
#define HG_BRANCH       "default"
#define HG_NODE          "7906498bc6e36f95daf03ffce97a18c3000990fb"
#define HG_ID           "7906498bc6e3"
#define HG_TAGS         "tip"
#define HG_LATESTTAG    "V0.1"

#endif
> ./hello
version V0.1.1 (hg:1/7906498bc6e3)


What we see here, is that we got Version V0.1.1 after cloning the repository. But the latest tag is V0.1. So, let's take a look at the log:
> hg log
changeset:   1:7906498bc6e3
tag:         tip
user:        Thomas Maier-Komor <thomas@maier-komor.de>
date:        Thu Jul 25 07:37:32 2019 +0200
summary:     Added tag V0.1 for changeset c6295b293642
 
changeset:   0:c6295b293642
tag:         V0.1
user:        Thomas Maier-Komor <thomas@maier-komor.de>
date:        Thu Jul 25 07:37:25 2019 +0200
summary:     initial checkin of build template with version generation
> hg id
7906498bc6e3 tip
As you can see, the clone updated the sandbox to the latest revision, which is the addition of the tag for changeset 0. Therefore, if we want to get the expected version information, we must update to the revision that we tested and tagged afterwards. I.e.:
> hg up -r V0.1
After that and rebuilding the binary, we get the expected result.
> ./hello
version V0.1.0 (hg:0/c6295b293642)
As written above, Mercurial also provides the infrastructure to determine if the sandbox for building has any uncommitted changes. Like this it is easily possible to integrate this important information into the version string. This template adds a plus character at the end of the version string, if there are uncommitted changes detected at build time. Of course you can change the plus character to something different or even cancel the build if you want to make sure that only reproducible binaries are created. This kind of restriction could also be applied to a specific server.

Let's see how it works. Just make a simple modification to one of the files that are tracked in the repository. E.g. add a newline at the end of hello.c:
> echo >> hello.c
After that trigger a new build with make. The version string then looks like this:
% ./hello 
version V0.1.1+ (hg:1/7906498bc6e3)
This template has no big magic, just a shell script and its integration in the build infrastructure with GNU make and CMake. You can also easily expand it to include the username and/or hostname of the person who triggered the build or whatever else you would like to see. 

Get the template as a Mercurial repository here. I hope you like it. I am rolling this concept out to all of my software development projects.

Thursday, June 13, 2019

Embedded Systems' Data Serialization

Data Serialization

Data serialization is the process of transferring data structures from the in-memory representation that is used for working with the data (e.g. structs) to a binary data stream (usually a sequence of binary octets) that can be transmitted via network or stored on non-volatile memory (e.g. disk, flash). The process in the opposite direction from disk to in memory is called either deserialization or parsing. 

There are many ways to serialize data, each having individual advantages and drawbacks. In this post I want to focus on handling generic structured data. Most popular for handling structured data are probably XML and JSON, but there exist also more advanced techniques for creating highly efficient binary data streams.

XML and JSON

XML and JSON are both generic file formats that can be used for almost any kind of application and structured data except for BLOBs (binary large objects - e.g. pictures, video streams, audio data). Both can be used over a variety of platforms, and can be extended easily later if necessary (e.g. later software version with new features), without loosing backward compatibility. Additionally, their representation is human readable, and therefore, it is easy to deal with those kinds of files.

Comparing XML to JSON, XML provides concepts for meta-data, and the ability to restrict data values to certain ranges to provide means for verification and validation of data. But these features are rather heavy weighted when it comes to their implementation and run-time impact. Nevertheless, powerful embedded systems like smartphones often rely on this technique.

For less powerful embedded systems XML is mostly out of scope, as RAM is limited and processing power may be too limited to provide good enough performance. JSON has much lower requirements when it comes to processing power, but it lacks some of those features, which make XML interesting. Nevertheless, JSON libraries for embedded systems are widely available, and JSON is also useful when it comes to interacting with web servers and other generic data processing partners.

Still, even JSON requires a good amount of code and processing time in comparison with binary file formats. 


Google protocol buffers

There are many binary file formats available for all kinds of applications: images, videos, audio, archives, and many others to name just a few. They use different concepts to encode and compress the data. Providing data serialization for generic data structures is a quite complex task. Therefore, Google provides Protocol Buffers for specifying structured data and generating code for parsing and serializing data.

Google protocol buffer's language has concepts for extending the file format later. Therefore, every individual data field gets a unique identifier that must not be changed over the life-time of the file format. But the associated data type may be changed and extended when following certain guide-lines. This concepts makes it perfect for use with applications that may need later extensions and may want to obsolete certain functions without breaking compatibility.

Unfortunately, even their lite library implementation is pretty heavy weighted and not targeted for embedded systems. Therefore, I have implemented Wire Format Compiler, which extends the language of protocol buffers with options and concepts for embedded systems. It is also hosted on github, where everybody can participate in the work and file bug reports.

Wire Format Compiler

Wire Format Compiler's language has been directly derived from protocol buffers. Of course if WFC extensions and features are used, the protocol description will not work with protocol buffers. But it is perfectly possible to come up with data structure specifications that work with both compilers.

In contrast to protocol buffers, wire format compiler is designed with a strong focus on embedded systems. Therefore, many optimizations for reducing code size and increasing processing performance are implemented to support embedded systems well. Additionally new data types have been added (e.g. fixed8, sfixed16) that support very small controller families, so that targeting even 8- and 16-bit controller families becomes feasible.

Furthermore, there exists a new concept for specifying options to tailor and optimize one data structure definition for multiple applications on different targets. The serialized data will be usable among all applications, but individual tuning allows to remove unsupported and unneeded data structures by telling the compiler with "used=false" that a specific member will not be used. Furthermore, data types the will be used for strings and byte arrays can be adjusted, so that specialized classes can be used.

Additionally, many options are provided to generate even more optimized code for specific targets. E.g. there exists support to make optimize for little endian systems that support unaligned access. On the other hand, it is still possible to target also big endian systems that work totally different, when it comes to byte placements in memory.

A small example

This is a small example that shows a wfc description and its generated header file. The first part of the wfc description has 3 different option sets that I describe below in more detail. After the option descriptions, a message description follows. This is a data structure that will be generated as a C++ class with methods for handling the data fields, and methods for (de-)serialization. 

The description of a field requires three elements: a field type, the field name, and a unique identifier. The unique identifier is set once for every field name, and it must not change later. The associated data is serialized with this unique identifier, and therefore, it is needed for deserialization. Changing the type is only possible, if it is a compatible change. E.g. a variable length integer might be changed from 8 to 16 bits, but the change of type must not impact the serialization concept of the member. Changing the name does only conflict with associated source code. So this is usually without a problem, unless special concepts like JSON support or ASCII generation are used.


The first option set, called "embedded", is optimized for a 32-bit embedded systems without string handling. I.e. the strings that are referenced in the data structure are regular C-strings (char pointer), and their memory management must be done independently. In contrast the option set "embedded_dyn" specifies "astring" as string data type. You could also specify your own data type, as long as it has some basic interfaces of std::string, like the member functions size() and c_str(). 

The generated header file includes a class definition that reflects the message description. It includes a function for calculating the concrete size of a serialized object (calcSize). This can be used to allocate enough memory before using the toMemory function to serialize the object to memory. After that the memory block can be written to a file, to flash memory, or send via network, as needed. For deserializing the data on the receiver side the fromMemory function can be used.

In addition, wfc supports the generation of functions for using a string instead of a memory block for serialization. This is more convenient in use, but might not be possible on all embedded systems. 


For every message object member, functions are generated to get and set their values and to determine the number of elements (size) of repeated members. For optional members functions are added to determine if a certain member has been set (e.g. has_hostname). All generated functions employ statically linked core functions. But options can be used to share the core functionality among multiple message objects.

The options and concepts shown in this post are only the most basic ones. Please refer to the available options and documentation, to learn in what other ways the code generation can be influenced. 

If you have questions or want to file a bug report, please refer to the project page on github or get in touch with me directly.

Sunday, May 05, 2019

Variants of D1 Mini with ESP8266

Overview

In this post I take a look at prototype boards for MCUs from Espressif. Espressif offers two popular micro-controller families: ESP8266 and ESP32. Those two SOCs have a lot in common, while the ESP32 is the stronger one with 2 main cores, higher performance, and more peripheral interfaces.

On the other hand the ESP8266 has lower power consumption and provides sufficient performance for many applications. Additionally, the ESP8266 family has a member called ESP8285 that provides internal flash memory, so that it can be used for very small boards, as no additional external flash is necessary.

Here is a short comparison table of these controllers: 

FeatureESP8266ESP32
architectureTensilica L106Tensilica LX6
# main cores12
WiFi MACyesyes
Bluetooth MACnoyes
Ethernet MACnoyes
touchnoyes
hall sensornoyes
SPI24
PWMsoftwarehardware
waveform generatornoyes

Both families provide much more features than shown in this short table. This table should give you just an impression of those architectures, in case you are not familiar with them.

One popular prototyping board with ESP8266 is the D1 mini offered by Wemos, which is also provide as a clone by other companies. The variants and versions of the D1 mini have subtle differences, we want to take a look at here.

Boards with ESP8266

D1 Mini:


  • ESP8266
  • 4 MB external flash integrated in metal package
  • USB socket connect to UART0 TX/RX
  • Reset button
  • From Version 2.2.0 to version 2.3.0 the USB socket moved from back to front 
  • one LED





D1 Mini Lite:

  • ESP8285
  • 1MB internal flash
  • USB socket connect to UART0 TX/RX 
  • Reset button
  • one LED




Mini32:

  • Controller: ESP32
  • Flash: 4MB
  • USB socket is connected to UART TX only
    (Update: this observation is probably related to a broken device. A sample of a similar looking clone without branding did not show this limitation. The clone had an additional LED, so it has not the exact schematic. But the additional LED is just a supply monitoring LED.)
  • Reset button
  • Pads have same spacing, but are assigned differntly. The critical pads are assigned the same: VCC, GND, TX, RX, 3,3V, RST
  • 2 LEDs

    Tuesday, March 12, 2019

    Entscheidung für eine Hausautomatisierung

    Einsatzbereich und Erweiterbarkeit

    Hausautomatisierungen bieten heute ein großes Portfolio an Anwendungsmöglichkeiten. Nicht alle Anbieter von Smart-Home Lösungen bieten Komponenten für alle diese Anwendungen. Deswegen macht es Sinn, sich mit der Frage auseinanderzusetzen, welche Anwendungen in Zukunft in Betracht kommen könnten. 

    Hat man sich einmal für einen Systemanbieter entschieden, muss man mit den Komponenten auskommen, die dieser im Programm hat oder man muss ggf. Systeme verschiedener Anbieter parallel betreiben. Da die Systeme normalerweise zueinander inkompatibel sind, bedeutet das, dass man mehrere Apps verwenden muss, und keine Interaktion über die Systemgrenzen möglich sind.

    Folgende Anwendungen können über Systeme zur Hausautomatisierung abgedeckt werden:
    • Beschattung mit Rollos und Jalousien
    • Heizung
    • Beleuchtung, Schalten, Dimmen
    • Wetter- und Umweltdatenerfassung
    • Bewässerung
    • Schließsysteme
    • Alarm- und Überwachungsanlagen
    • Multimedia
    Die Auswahl am Markt ist sehr groß, aber fast alle Systeme sind zueinander inkompatibel. Conrad hat eine schöne Übersicht über die verfügbaren Systeme und ihre Funktionen.

    Unterstützung für Sprachsteuerung über Alexa und Co. wird von einigen dieser Systeme mit angeboten. Was aber üblicherweise nicht geht, ist beispielsweise eine schaltbare Steckdose eines Anbieters in die Steuerung eines anderen zu integrieren. Auch können Umweltdaten wie Temperatur und Luftfeuchtigkeit eines Sensors von Anbieter A nicht zur Steuerung der Heizung mit einem Thermostat von Anbieter B verwendet werden.

    Funk oder Kabel?

    Grundsätzlich gibt es Kabel gebundene Systeme und Systeme die über Funk kommunizieren. Kabel gebundene Kommunikation ist im Punkt Stör- und Ausfallsicherheit etwas robuster als Funk basierte Systeme. Allerdings kann eine beschädigte Verkabelung zu einem sehr hohen Aufwand bei der Fehlersuche führen.

    Funk basierte Systeme wurden erst später eingeführt und können auch in bestehenden Gebäuden einfach nachgerüstet werden, ohne dass nachträglich aufwendig neue Kabel eingezogen werden müssen. Leider behindern Fußböden und Wände aus Beton die Kommunikation via Funk deutlich. Doch es gibt Zusatzgeräte um in Gebäuden mit viel Beton eine Hausautomatisierung mit Funk in jeder Ecke zu ermöglichen.

    Die meisten kabellose Systeme verwenden das 868 MHz Band. Allerdings ist diese Kommunikation nicht standardisiert. Das bedeutet, dass Systeme verschiedener Anbieter die eine 868MHz basierte Lösung haben, normalerweise nicht miteinander kompatibel sind und somit nicht miteinander verwendet werden können. Deswegen sollte man immer davon ausgehen, dass Systeme verschiedener Anbieter nicht miteinander funktionieren. 

    Bedienung und Integration des Designs

    Über die Art und Weise der Bedienung sollte man sich im Vorfeld auch Gedanken machen. Ist eine Bedienung per Wand- oder Handschalter gewünscht? Ist ein Tischaufsteller vorzuziehen? Soll der Status der Systeme irgendwie visualisiert werden (z.B. Display an der Wand)? Wie sieht es aus mit einer App für Smartphone oder Tablet - würde diese von den Bewohnern verwendet werden?

    Nicht jeder Anbieter bietet alle Bedienkonzepte, und häufig haben die Anbieter ein eigenes Design. Das kann insbesondere auf Ablehnung stoßen, wenn die Wandschalter zwischen Hausautomatisierung  und normalen Schaltern unterschiedlich aussehen. Einige Hersteller bieten eine Integration ins bestehende Schalterprogramm an. Dabei werden die Blenden des Anbieters für das Schalterprogramm über Adapter auf die Komponenten der Hausautomatisierung gesteckt.

    Alternativ kann man das ganze Schalterprogramm tauschen (also alle Lichtschalter und Steckdosen). Allerdings treibt das die Kosten unter Umständen deutlich in die Höhe.

    Wenn der bestehende Look des Schalterprogramms erhalten werden soll, muss sichergestellt werden, dass es für die konkrete Designvariante passende Adapter gibt. Diese sind oft für die bekannten Hersteller von Schaltern und Steckdosen, wie Busch-Jaeger, Gira, Jung und andere verfügbar. 

    Manche Hersteller von Schalterprogrammen bieten auch eigene Lösungen zum Thema Smart Home an. Dann stellt sich die Frage des passenden Designs gar nicht erst. Busch-Jaeger und Gira haben beispielsweise eine gemeinsame technische Lösung names eNet. Allerdings ist hier zu beachten, dass die klassischen Anbieter von Schalterprogrammen sich auf ihre klassischen Anwendungen konzentrieren und für die Installation von geschultem Fachpersonal ausgehen. 

    Zentralsteuerung vs. Internetbasierter Lösung

    Smart Home Lösungen arbeiten entweder mit einem Server der zu Hause aufgestellt wird oder mit einer Cloud basierten Lösung im Internet. 

    Vorteile eine Servers zu Hause:

    • funktioniert auch, wenn die Internetverbindung zusammenbricht
    • alle Daten bleiben zu Hause
    • Funktionsfähigkeit hängt nur an den beschafften Geräten und nicht an externen Firmenentscheidungen (z.B. Pleite, Abkündigung von Cloud-Diensten)

    Vorteile einer Cloud-Lösung (Server im Internet):

    • geringere Anschaffungskosten
    • geringerer Installationsaufwand
    • der Zugriff über das Internet in der Regel ohne Mehraufwand möglich

    Entscheidung für ein System

    Meine Anforderungen:

    • Integration ins bestehende Schaltersystem Gira E2
    • Funkbasierte Lösung, da an vielen Stellen nachträglich keine Verkabelung möglich ist
    • Unterstützung von Jalousien und dem Schrägstellen der Jalousielamellen für eine Teilbeschattung
    • Abdeckung möglichst vieler Anwendungsgebiete
    • Optional ein offenes System, so dass auch Eigenlösungen integriert werden können
    • App für die einfache Bedienung wäre von Vorteil

    Lösungsfindung:

    Unser Schalterprogramm ist von Gira, deswegen habe ich als erstes das Angebot von Gira evaluiert. Gira bietet alles was ich zu Beginn haben wollte. Einzig störte mich, dass eNet ein geschlossenes System ist, in das keine Eigenlösungen integriert werden kann. Der Preis für den eNet Server bewegt sich im Mittelfeld und im Vergleich zur kabelgebundenen KNX Lösungen ist er deutlich günstiger. Außerdem sind die angebotenen Komponenten auf die klassischen Anwendungen eines Schalterprogramms beschränkt.

    Lösungen mit dem Gira E2 Design gibt es auch von anderen Herstellern. Am Ende habe ich mich für Homematic IP entschieden, da dies als Server Lösung mit CCU3 kompatibel zu Homematic ist und außerdem als Cloud-Variante einen einfachen und sehr kostengünstigen Einstieg mit vielen Erweiterungsmöglichkeiten bietet. Außerdem ist das Homematic-System teiloffen und es gibt Möglichkeiten Eigenlösungen zu entwickeln. Darüber hinaus werden Alexa, Google Assistant und Conrad Connect auch noch unterstützt.

    Die Konkurrenzprodukte von Bosch, Innogy und Co. habe ich wegen fehlenden Anwendungsmöglichkeiten ausgeschlossen. Die Auswahl der richtigen Komponenten bei Gira gestaltete sich schwierig, da es mehrere verschiedene Systeme gibt, die nicht alle beliebig miteinander kombiniert werden können.

    Meine Lösung:

    Zunächst habe ich die Cloud basierte Variante von Homematic-IP mit dem für 50 Euro sehr günstigen AccessPoint in Betrieb genommen. Meine Erwartungshaltung war hier, dass ich mit der App recht schnell an die Grenzen dessen komme was möglich ist und Wünsche offen bleiben. In diesem Fall wäre der Schritt zu einer CCU3 als Home-Server einfach möglich, da alle Geräte von Homematic-IP auch damit kompatibel sind. Anders herum sind leider die Geräte von Homematic nicht mit der Cloud-Lösung von Homematic-IP kompatibel. Die Cloud basierte Lösung bleibt somit auf das Angebot von Homematic-IP beschränkt.

    Erfahrungen bei Installation und Betrieb

    Die Installation der Hardware benötigt für alle Geräte einen Nullleiter (schwarzer Draht). Das ist auch für alle Konkurenzprodukte so zu erwarten. D.h. man sollte vor dem Kauf der Geräte prüfen, ob die Dosen alle Anforderungen bzgl. des Bauraums und der erforderlichen Leitungen erfüllen. Ggf. muss noch ein Draht eingezogen werden, wenn die entsprechende Dose noch keinen Nullleiter hat. Die Installation sollte auf jedem Fall von jemanden durchgeführt werden, der über das entsprechende elektrische Fachwissen verfügt. 

    Bitte immer daran denken, dass zum prüfen des Bauraums und der verfügbaren Leitungen die entsprechenden Phasen über die Sicherungen zum Eigenschutz abgeschaltet werden müssen.

    Persönlich hat mich bei der Inbetriebnahme überrascht, dass ich mit der App zur Konfiguration des Cloud Dienstes tatsächlich alle meine Wünsche bzgl. der Beschattungskonfiguration mit Gruppensteuerungen, Zeitprofilen und Abhängigkeiten zu Sonnen Auf- und Untergang realisieren konnte. Der Einstieg erfolgte mit 4 Rollladen Steuerungen und 4 Jalousiesteuerungen. Inzwischen sind bereits mehr Geräte integriert und die nächste Erweiterung für Aussperrschutz (Türsensor der Verhindert, dass die zugehörige Jalousie heruntergefahren wird, wenn die Tür offen steht) und die Heizung um Keller bestellt. 

    Sogar ein Schutz der Jalousien in Abhängigkeit von der Windgeschwindigkeit basierend auf den Wetterdaten im Internet zu der Postleitzahl war möglich. Ein richtiger Windmesser einer Wetterstation liefert allerdings genauere Daten und darüber hinaus Informationen zur Sonnensituation.

    Die Funk-Reichweite in unserem Haus mit viel Beton ist grenzwertig. Trotzdem war durch eine geeignete Positionierung des Accesspoint im ersten Obergeschoß es bisher möglich alle Geräte zu erreichen. Eine Reichweitenerhöhung über Steckdosenrepeater mit Schaltfunktion ist ggf. für ca. 40 Euro möglich. 

    In Summe hat diese Lösung weniger als die Hälfte dessen gekostet, was die gleiche Lösung von Gira gekostet hätte. Außerdem ist bereits von Anfang an ein höherer Funktionsumfang durch die Cloud-Technik gegeben (z.B. Windschutz der Jalousien). 

    Die App

    Die App von Homematic IP ist tatsächlich auch für einen Endanwender vernünftig nutzbar und zeigt den Status auch unterwegs ohne Zusatzaufwand vernünftig an.

    Hier einige beispielhafte Screenshots, wie die App zur Bedienung über das Smartphone aussieht. Im Falle von Homematic-IP erfolgte auch die gesamte Konfiguration und das Update der Komponenten über diese App. Aus meiner Sicht ist die App mit ihrer Aufteilung in Räume, Gruppen und Zeitprofilen sauber und verständlich strukturiert.

      

    Power-User kommen mit der App möglicherweise an die Grenze dessen, was die App kann. In diesem Fall kann man bei Homematic-IP aber immer noch auf einen Server (CCU3) umstellen, der dann eine deutlich detailliertere und aufwendigere Konfiguration ermöglicht. Bei der Verwendung der CCU3 können dann auch noch Komponenten aus dem Angebot von Homematic (also nicht Homematic-IP) verwendet werden.

    Fazit

    Homematic-IP als Cloud basierte Smart-Home-Lösung kann ich durchaus empfehlen. Mangels Erfahrung mit Konkurrenzprodukten ist allerdings eine differenzierte Bewertung an dieser Stelle nicht möglich. So bleibt nur mein subjektiver aber positiver Gesamteindruck. Außerdem sehe ich auch im Nachhinein am Markt keine bessere Lösung und würde es so auch wieder umsetzen.

    Positiv ist mir aufgefallen:

    • Inbetriebnahme des Accesspoints
    • Verständlichkeit und Aufwand der Konfiguration
    • Einfache Bedienung über App und Taster
    • Verwendung von Windinformationen auf Postleitzahlbasis zum Schutz der Jalousien
    • Steuerung in Abhängigkeit von Sonnenauf- und -untergang
    • Automatische Erkennung der Abschaltpunkte und Laufzeiten von Rollos und Jalousien
    • Automatisches Update der Geräte
    • Preis/Leistungs-Verhältnis

    Verbesserungswürdig habe ich empfunden:

    • minimale Steuerzeit der Jalousien erlaubt keine Feinpositionierung der Lamellenwinkel (vermutlich Systembedingt wegen der verbauten Relais)
    • schwacher Druckpunkt der Steuertaster
    • manche Änderungen waren nur über ein Abmelden, Rücksetzen und Wiederanmelden der Geräte möglich
    • das Verschraubungs- und Befestigungskonzept erlaubt nur begrenzte Feinpositionierung

    Tuesday, March 05, 2019

    Speedport Smart 3 vs. FritzBox - Erfahrungen...

    Nach ein paar Jahren mit einem anderen Internet-Anbieter bin ich nun zur Telekom gewechselt. Deswegen stand die Frage im Raum: Speedport Smart 3 mieten oder ein eigenes Gerät kaufen. Da die Mietkonditionen für Neukunden sehr günstig sind, und das WLAN To Go nur mit dem Speedport angeboten wird, habe ich mich zunächst einmal für die Miete entschieden.

    Eigentlich bin ich davon ausgegangen, dass es technisch keinen Unterschied machen sollte, ob eine FritzBox (hatte ich zuvor) als Router verwendet wird oder ein Speedport Smart 3 von der Telekom. Leider stimmt das nicht bei meinen Anforderungen. Denn es gibt mit dem Speedport einiges worauf man verzichten muss:
    1. Die Liste der verpassten Anrufe und das Telefonbuch werden vom Speedport nicht auf dem AVM Telefon unterstützt, und das Wählverfahren läuft nur im Ton-Wahlverfahren. 
    2. VPN ist nicht im Speedport direkt konfigurierbar und nur über eine Portfreischaltung realisierbar. D.h. ein zusätzliches Gerät muss als VPN Server konfiguriert werden (z.B. ein RasPi mit Pi-VPN). Durch das zusätzliches Gerät sind die Stromkosten höher, und das eigenhändige Pflegen eines VPN Servers bedeutet Mehraufwand bietet bringt zusätzliche Sicherheitsrisiken mit sich.
    3. Der DHCP Server des Speedport gibt die Namenseinträge der Gerät nicht an den Nameserver weiter. D.h. die Geräte sind dadurch nicht wie von der FritzBox gewohnt über ihren Hostnamen sondern nur über ihre IP-Adresse ansprechbar. Das hat bei mir alle Owncloud Dienste auf den Clients lahmgelegt und ist auch sonst ein nerviges Hindernis für Netzwerkdienste wie z.B. SSH, NFS. 
    4. Es gibt keinen Gast-LAN Port. Diesen hatte ich bisher dafür benutzt unsere auf Homematic IP basierende Hausautomatisierung vom restlichen Netz zu isolieren. Das ist nicht notwendig, fand ich aber ein nützliches Detail, da so eine Sicherheitslücke im Homematic-IP Gerät sich nicht nutzen lässt, um auf das Heimnetz zuzugreifen.
    5. WLAN-Mesh: die AVM Repeater lassen sich nicht mit dem Speedport n ein gemeinsam konfigurierbares und Update-bares Mesh zusammenführen. Also auch hier Mehraufwand.
    Entsprechend habe ich kurzer Hand eine FritzBox 7530 gekauft und werde die Miete kündigen. Einziges Manko mit der 7530: Das Gast-LAN (nicht Gast-WLAN) ist bei Verwendung der 7530 an einem Glasfaseranschluss nicht Verfügbar, da anscheinend das Glasfasermodem auf dem Ethernet-Port des Gast-LAN angeschlossen wird...

    Update: Das Gast-LAN der FritzBox kann nach der Einstellung der Glasfaser-Verbindung wieder reaktiviert werden.