Mit Visual Basic .NET auf das LCD 20x4 Bricklet schreiben

Für diese Projekt setzen wir voraus, dass eine Visual Basic .NET Entwicklungsumgebung eingerichtet ist und ein grundsätzliches Verständnis der Visual Basic .NET Programmiersprache vorhanden ist.

Falls dies nicht der Fall ist sollte hier begonnen werden. Informationen über die Tinkerforge API sind dann hier zu finden.

Ziele

Wir setzen uns folgende Ziele für dieses Projekt:

  • Temperatur, Helligkeit, Luftfeuchte und Luftdruck sollen auf dem LCD 20x4 Bricklet angezeigt werden,
  • die gemessenen Werte sollen automatisch aktualisiert werden sobald sie sich verändern und
  • die gemessenen Werte sollen in einem verständlichen Format angezeigt werden.

Da dieses Projekt wahrscheinlich 24/7 laufen wird, wollen wir sicherstellen, dass das Programm möglichst robust gegen externe Einflüsse ist. Das Programm sollte weiterhin funktionieren falls

  • Bricklets ausgetauscht werden (z.B. verwenden wir keine fixen UIDs),
  • Brick Daemon läuft nicht oder wird neu gestartet,
  • WIFI Extension ist außer Reichweite oder
  • Wetterstation wurde neu gestartet (Stromausfall oder USB getrennt).

Im Folgenden werden wir Schritt für Schritt zeigen wie diese Ziele erreicht werden können.

Schritt 1: Bricks und Bricklets dynamisch erkennen

Als Erstes legen wir fest wohin unser Programm sich verbinden soll:

Const HOST As String = "localhost"
Const PORT As Integer = 4223

Falls eine WIFI Extension verwendet wird, oder der Brick Daemon auf einem anderen PC läuft, dann muss "localhost" durch die IP Adresse oder den Hostnamen der WIFI Extension oder des anderen PCs ersetzt werden.

Nach dem Start des Programms müssen der EnumerateCallback Callback und der Connected Callback registriert und ein erstes Enumerate ausgelöst werden:

Sub Main()
    ipcon = New IPConnection()
    ipcon.Connect(HOST, PORT)

    AddHandler ipcon.EnumerateCallback, AddressOf EnumerateCB
    AddHandler ipcon.Connected, AddressOf ConnectedCB

    ipcon.Enumerate()
End Sub

Der Enumerate Callback wird ausgelöst wenn ein Brick per USB angeschlossen wird oder wenn die Enumerate() Funktion aufgerufen wird. Dies ermöglicht es die Bricks und Bricklets im Stapel zu erkennen ohne im Voraus ihre UIDs kennen zu müssen.

Der Connected Callback wird ausgelöst wenn die Verbindung zur WIFI Extension oder zum Brick Daemon hergestellt wurde. In diesem Callback muss wiederum ein Enumerate angestoßen werden, wenn es sich um ein Auto-Reconnect handelt:

Sub ConnectedCB(ByVal sender As IPConnection, ByVal connectedReason as Short)
    If connectedReason = IPConnection.CONNECT_REASON_AUTO_RECONNECT Then
        ipcon.Enumerate()
    End If
End Sub

Ein Auto-Reconnect bedeutet, dass die Verbindung zur WIFI Extension oder zum Brick Daemon verloren gegangen ist und automatisch wiederhergestellt werden konnte. In diesem Fall kann es sein, dass die Bricklets ihre Konfiguration verloren haben und wir sie neu konfigurieren müssen. Da die Konfiguration beim Enumerate (siehe unten) durchgeführt wird, lösen wir einfach noch ein Enumerate aus.

Schritt 1 zusammengefügt:

Module WeatherStation
    Const HOST As String = "localhost"
    Const PORT As Integer = 4223

    Sub ConnectedCB(ByVal sender As IPConnection, ByVal connectedReason as Short)
        If connectedReason = IPConnection.CONNECT_REASON_AUTO_RECONNECT Then
            ipcon.Enumerate()
        End If
    End Sub

    Sub Main()
        ipcon = New IPConnection()
        ipcon.Connect(HOST, PORT)

        AddHandler ipcon.EnumerateCallback, AddressOf EnumerateCB
        AddHandler ipcon.Connected, AddressOf ConnectedCB

        ipcon.Enumerate()
    End Sub
End Module

Schritt 2: Bricklets beim Enumerate initialisieren

Während des Enumerierungsprozesse sollen alle messenden Bricklets konfiguriert werden. Dadurch ist sichergestellt, dass sie neu konfiguriert werden nach einem Verbindungsabbruch oder einer Unterbrechung der Stromversorgung.

Die Konfiguration soll beim ersten Start (ENUMERATION_TYPE_CONNECTED) durchgeführt werden und auch bei jedem extern ausgelösten Enumerate (ENUMERATION_TYPE_AVAILABLE):

Sub EnumerateCB(ByVal sender As IPConnection, ByVal uid As String, _
                ByVal connectedUid As String, ByVal position As Char, _
                ByVal hardwareVersion() As Short, ByVal firmwareVersion() As Short, _
                ByVal deviceIdentifier As Integer, ByVal enumerationType As Short)
    If enumerationType = IPConnection.ENUMERATION_TYPE_CONNECTED Or _
       enumerationType = IPConnection.ENUMERATION_TYPE_AVAILABLE Then

Die Konfiguration des LCD 20x4 ist einfach, wir löschen den aktuellen Inhalt des Displays und schalten das Backlight ein:

If deviceIdentifier = BrickletLCD20x4.DEVICE_IDENTIFIER Then
    brickletLCD = New BrickletLCD20x4(UID, ipcon)
    brickletLCD.ClearDisplay()
    brickletLCD.BacklightOn()

Das Ambient Light, Humidity und Barometer Bricklet werden so eingestellt, dass sie uns ihre jeweiligen Messwerte höchsten mit einer Periode von 1000ms (1s) mitteilen:

Else If deviceIdentifier = BrickletAmbientLight.DEVICE_IDENTIFIER Then
    brickletAmbientLight = New BrickletAmbientLight(UID, ipcon)
    brickletAmbientLight.SetIlluminanceCallbackPeriod(1000)
    AddHandler brickletAmbientLight.Illuminance, AddressOf IlluminanceCB
Else If deviceIdentifier = BrickletHumidity.DEVICE_IDENTIFIER Then
    brickletHumidity = New BrickletHumidity(UID, ipcon)
    brickletHumidity.SetHumidityCallbackPeriod(1000)
    AddHandler brickletHumidity.Humidity, AddressOf HumidityCB
Else If deviceIdentifier = BrickletBarometer.DEVICE_IDENTIFIER Then
    brickletBarometer = New BrickletBarometer(UID, ipcon)
    brickletBarometer.SetAirPressureCallbackPeriod(1000)
    AddHandler brickletBarometer.AirPressure, AddressOf AirPressureCB
End If

Dies bedeutet, dass die Bricklets die IlluminanceCB, HumidityCB und AirPressureCB Callback-Funktionen immer dann aufrufen wenn sich der Messwert verändert hat, aber höchsten alle 1000ms.

Schritt 2 zusammengefügt:

Sub EnumerateCB(ByVal sender As IPConnection, ByVal uid As String, _
                ByVal connectedUid As String, ByVal position As Char, _
                ByVal hardwareVersion() As Short, ByVal firmwareVersion() As Short, _
                ByVal deviceIdentifier As Integer, ByVal enumerationType As Short)
    If enumerationType = IPConnection.ENUMERATION_TYPE_CONNECTED Or _
       enumerationType = IPConnection.ENUMERATION_TYPE_AVAILABLE Then
        If deviceIdentifier = BrickletLCD20x4.DEVICE_IDENTIFIER Then
            brickletLCD = New BrickletLCD20x4(UID, ipcon)
            brickletLCD.ClearDisplay()
            brickletLCD.BacklightOn()
        Else If deviceIdentifier = BrickletAmbientLight.DEVICE_IDENTIFIER Then
            brickletAmbientLight = New BrickletAmbientLight(UID, ipcon)
            brickletAmbientLight.SetIlluminanceCallbackPeriod(1000)
            AddHandler brickletAmbientLight.Illuminance, AddressOf IlluminanceCB
        Else If deviceIdentifier = BrickletHumidity.DEVICE_IDENTIFIER Then
            brickletHumidity = New BrickletHumidity(UID, ipcon)
            brickletHumidity.SetHumidityCallbackPeriod(1000)
            AddHandler brickletHumidity.Humidity, AddressOf HumidityCB
        Else If deviceIdentifier = BrickletBarometer.DEVICE_IDENTIFIER Then
            brickletBarometer = New BrickletBarometer(UID, ipcon)
            brickletBarometer.SetAirPressureCallbackPeriod(1000)
            AddHandler brickletBarometer.AirPressure, AddressOf AirPressureCB
        End If
    End If
End Sub

Schritt 3: Messwerte auf dem Display anzeigen

Wir wollen eine hübsche Darstellung der Messwerte auf dem Display. Zum Beispiel:

Illuminanc 137.39 lx
Humidity    34.10 %
Air Press  987.70 mb
Temperature 22.64 °C

Die Dezimaltrennzeichen und die Einheiten sollen in jeweils einer Spalte übereinander stehen. Daher verwenden wird zwei Zeichen für jede Einheit, zwei Nachkommastellen und kürzen die Namen so, dass sie in den restlichen Platz der jeweiligen Zeile passen. Das ist auch der Grund, warum dem "Illuminanc" das letzte "e" fehlt.

Dim text As String = String.Format("{0,6:###.00}", value)

Der obige Ausdruck wandelt eine Fließkommazahl in eine Zeichenkette um, gemäß der gegebenen Formatspezifikation. Das Ergebnis ist dann mindestens 6 Zeichen lang mit 2 Nachkommastellen. Fall es weniger als 6 Zeichen sind wird von Links mit Leerzeichen aufgefüllt.

Sub IlluminanceCB(ByVal sender As BrickletAmbientLight, ByVal illuminance As Integer)
    Dim text As String = String.Format("Illuminanc {0,6:###.00} lx", illuminance/10.0)
    brickletLCD.WriteLine(0, 0, text)
End Sub

Sub HumidityCB(ByVal sender As BrickletHumidity, ByVal humidity As Integer)
    Dim text As String = String.Format("Humidity   {0,6:###.00} %", humidity/10.0)
    brickletLCD.WriteLine(1, 0, text)
End Sub

Sub AirPressureCB(ByVal sender As BrickletBarometer, ByVal airPressure As Integer)
    Dim text As String = String.Format("Air Press {0,7:####.00} mb", airPressure/1000.0)
    brickletLCD.WriteLine(2, 0, text)
End Sub

Es fehlt noch die Temperatur. Das Barometer Bricklet kann auch die Temperatur messen, aber es hat dafür keinen Callback. Als einfacher Workaround können wir die Temperatur in der AirPressureCB Callback-Funktion abfragen:

Sub AirPressureCB(ByVal sender As BrickletBarometer, ByVal airPressure As Integer)
    Dim text As String = String.Format("Air Press {0,7:####.00} mb", airPressure/1000.0)
    brickletLCD.WriteLine(2, 0, text)

    Dim temperature As Integer = sender.GetChipTemperature()
    text = String.Format("Temperature {0,5:##.00} {1}C", temperature/100.0, Chr(&HDF))
    brickletLCD.WriteLine(3, 0, text)
End Sub

Schritt 3 zusammengefügt:

Sub IlluminanceCB(ByVal sender As BrickletAmbientLight, ByVal illuminance As Integer)
    Dim text As String = String.Format("Illuminanc {0,6:###.00} lx", illuminance/10.0)
    brickletLCD.WriteLine(0, 0, text)
End Sub

Sub HumidityCB(ByVal sender As BrickletHumidity, ByVal humidity As Integer)
    Dim text As String = String.Format("Humidity   {0,6:###.00} %", humidity/10.0)
    brickletLCD.WriteLine(1, 0, text)
End Sub

Sub AirPressureCB(ByVal sender As BrickletBarometer, ByVal airPressure As Integer)
    Dim text As String = String.Format("Air Press {0,7:####.00} mb", airPressure/1000.0)
    brickletLCD.WriteLine(2, 0, text)

    Dim temperature As Integer = sender.GetChipTemperature()
    ' &HDF == ° on LCD 20x4 charset
    text = String.Format("Temperature {0,5:##.00} {1}C", temperature/100.0, Chr(&HDF))
    brickletLCD.WriteLine(3, 0, text)
End Sub

Das ist es. Wenn wir diese drei Schritte zusammen in eine Datei kopieren und ausführen, dann hätten wir jetzt eine funktionierenden Wetterstation.

Es gibt einige offensichtliche Möglichkeiten die Ausgabe der Messdaten noch zu verbessern. Die Namen könnten dynamisch exakt gekürzt werden, abhängig vom aktuell freien Raum der jeweiligen Zeile. Auch könnten die Namen können noch ins Deutsche übersetzt werden. Ein anderes Problem ist die Abfrage der Temperatur in der AirPressureCB Callback-Funktion. Wenn sich der Luftdruck nicht ändert dann wird auch die Anzeige der Temperatur nicht aktualisiert, auch wenn sich diese eigentlich geändert hat. Es wäre besser die Temperatur jede Sekunde in einem eigenen Thread anzufragen. Aber wir wollen das Programm für den Anfang einfach halten.

Wie dem auch sei, wir haben noch nicht alle Ziele erreicht. Das Programm ist noch nicht robust genug. Was passiert wenn die Verbindung beim Start des Programms nicht hergestellt werden kann, oder wenn das Enumerate nach einem Auto-Reconnect nicht funktioniert?

Wir brauchen noch Fehlerbehandlung!

Schritt 4: Fehlerbehandlung und Logging

Beim Start des Programms versuchen wir solange die Verbindung herzustellen, bis es klappt:

while True
    Try
        ipcon.Connect(HOST, PORT)
        Exit While
    Catch e As System.Net.Sockets.SocketException
        System.Console.WriteLine("Connection Error: " + e.Message)
        System.Threading.Thread.Sleep(1000)
    End Try
End While

und es wird solange versucht ein Enumerate zu starten bis auch dis geklappt hat:

while True
    try
        ipcon.Enumerate()
        Exit While
    Catch e As NotConnectedException
        System.Console.WriteLine("Enumeration Error: " + e.Message)
        System.Threading.Thread.Sleep(1000)
    End Try
End While

Mit diesen Änderungen kann das Programm schon gestartet werden bevor die Wetterstation angeschlossen ist.

Es muss auch sichergestellt werden, dass wir nur auf das LCD schreiben nachdem es initialisiert wurde:

Sub IlluminanceCB(ByVal sender As BrickletAmbientLight, ByVal illuminance As Integer)
    If brickletLCD IsNot Nothing Then
        Dim text As String = String.Format("Illuminanc {0,6:###.00} lx", illuminance/10.0)
        brickletLCD.WriteLine(0, 0, text)
        System.Console.WriteLine("Write to line 0: " + text)
    End If
End Sub

und es müssen mögliche Fehler während des Enumerierungsprozesses behandelt werden:

If deviceIdentifier = BrickletAmbientLight.DEVICE_IDENTIFIER Then
    Try
        brickletAmbientLight = New BrickletAmbientLight(UID, ipcon)
        brickletAmbientLight.SetIlluminanceCallbackPeriod(1000)
        AddHandler brickletAmbientLight.Illuminance, AddressOf IlluminanceCB
        System.Console.WriteLine("Ambient Light initialized")
    Catch e As TinkerforgeException
        System.Console.WriteLine("Ambient Light init failed: " + e.Message)
        brickletAmbientLight = Nothing
    End Try
End If

Zusätzlich wollen wir noch ein paar Logausgaben einfügen. Diese ermöglichen es später herauszufinden was ein Problem ausgelöst hat, wenn die Wetterstation nach einer Weile möglicherweise nicht mehr funktioniert wie erwartet.

Zum Beispiel, wenn die Wetterstation über WLAN angebunden ist und häufig Auto-Reconnects auftreten, dann ist wahrscheinlich die WLAN Verbindung nicht sehr stabil.

Schritt 5: Alles zusammen

Jetzt sind alle für diese Projekt gesteckten Ziele erreicht.

Das gesamte Programm für die Wetterstation (download):

Imports Tinkerforge

Module WeatherStation
    Const HOST As String = "localhost"
    Const PORT As Integer = 4223

    Private ipcon As IPConnection = Nothing
    Private brickletLCD As BrickletLCD20x4 = Nothing
    Private brickletAmbientLight As BrickletAmbientLight = Nothing
    Private brickletAmbientLightV2 As BrickletAmbientLightV2 = Nothing
    Private brickletAmbientLightV3 As BrickletAmbientLightV3 = Nothing
    Private brickletHumidity As BrickletHumidity = Nothing
    Private brickletHumidityV2 As BrickletHumidityV2 = Nothing
    Private brickletBarometer As BrickletBarometer = Nothing
    Private brickletBarometerV2 As BrickletBarometerV2 = Nothing

    Sub IlluminanceCB(ByVal sender As BrickletAmbientLight, ByVal illuminance As Integer)
        If brickletLCD IsNot Nothing Then
            Dim text As String = String.Format("Illuminanc {0,6:###.00} lx", illuminance/10.0)
            brickletLCD.WriteLine(0, 0, text)
            System.Console.WriteLine("Write to line 0: " + text)
        End If
    End Sub

    Sub IlluminanceV2CB(ByVal sender As BrickletAmbientLightV2, ByVal illuminance As Long)
        If brickletLCD IsNot Nothing Then
            Dim text As String = String.Format("Illumina {0,8:###.00} lx", illuminance/100.0)
            brickletLCD.WriteLine(0, 0, text)
            System.Console.WriteLine("Write to line 0: " + text)
        End If
    End Sub

    Sub IlluminanceV3CB(ByVal sender As BrickletAmbientLightV3, ByVal illuminance As Long)
        If brickletLCD IsNot Nothing Then
            Dim text As String = String.Format("Illumina {0,8:###.00} lx", illuminance/100.0)
            brickletLCD.WriteLine(0, 0, text)
            System.Console.WriteLine("Write to line 0: " + text)
        End If
    End Sub

    Sub HumidityCB(ByVal sender As BrickletHumidity, ByVal humidity As Integer)
        If brickletLCD IsNot Nothing Then
            Dim text As String = String.Format("Humidity   {0,6:###.00} %", humidity/10.0)
            brickletLCD.WriteLine(1, 0, text)
            System.Console.WriteLine("Write to line 1: " + text)
        End If
    End Sub

    Sub HumidityV2CB(ByVal sender As BrickletHumidityV2, ByVal humidity As Integer)
        If brickletLCD IsNot Nothing Then
            Dim text As String = String.Format("Humidity   {0,6:###.00} %", humidity/100.0)
            brickletLCD.WriteLine(1, 0, text)
            System.Console.WriteLine("Write to line 1: " + text)
        End If
    End Sub

    Sub AirPressureCB(ByVal sender As BrickletBarometer, ByVal airPressure As Integer)
        If brickletLCD IsNot Nothing Then
            Dim text As String = String.Format("Air Press {0,7:####.00} mb", airPressure/1000.0)
            brickletLCD.WriteLine(2, 0, text)
            System.Console.WriteLine("Write to line 2: " + text)

            Dim temperature As Integer
            Try
                temperature = sender.GetChipTemperature()
            Catch e As TinkerforgeException
                System.Console.WriteLine("Could not get temperature" + e.Message)
                Return
            End Try

            ' &HDF == ° on LCD 20x4 charset
            text = String.Format("Temperature {0,5:##.00} {1}C", temperature/100.0, Chr(&HDF))
            brickletLCD.WriteLine(3, 0, text)
            System.Console.WriteLine("Write to line 3: " + text.Replace(Chr(&HDF), "°"C))
        End If
    End Sub

    Sub AirPressureV2CB(ByVal sender As BrickletBarometerV2, ByVal airPressure As Integer)
        If brickletLCD IsNot Nothing Then
            Dim text As String = String.Format("Air Press {0,7:####.00} mb", airPressure/1000.0)
            brickletLCD.WriteLine(2, 0, text)
            System.Console.WriteLine("Write to line 2: " + text)

            Dim temperature As Integer
            Try
                temperature = sender.GetTemperature()
            Catch e As TinkerforgeException
                System.Console.WriteLine("Could not get temperature" + e.Message)
                Return
            End Try

            ' &HDF == ° on LCD 20x4 charset
            text = String.Format("Temperature {0,5:##.00} {1}C", temperature/100.0, Chr(&HDF))
            brickletLCD.WriteLine(3, 0, text)
            System.Console.WriteLine("Write to line 3: " + text.Replace(Chr(&HDF), "°"C))
        End If
    End Sub

    Sub EnumerateCB(ByVal sender As IPConnection, ByVal uid As String, _
                    ByVal connectedUid As String, ByVal position As Char, _
                    ByVal hardwareVersion() As Short, ByVal firmwareVersion() As Short, _
                    ByVal deviceIdentifier As Integer, ByVal enumerationType As Short)
        If enumerationType = IPConnection.ENUMERATION_TYPE_CONNECTED Or _
           enumerationType = IPConnection.ENUMERATION_TYPE_AVAILABLE Then
            If deviceIdentifier = BrickletLCD20x4.DEVICE_IDENTIFIER Then
                Try
                    brickletLCD = New BrickletLCD20x4(UID, ipcon)
                    brickletLCD.ClearDisplay()
                    brickletLCD.BacklightOn()
                    System.Console.WriteLine("LCD 20x4 initialized")
                Catch e As TinkerforgeException
                    System.Console.WriteLine("LCD 20x4 init failed: " + e.Message)
                    brickletLCD = Nothing
                End Try
            Else If deviceIdentifier = BrickletAmbientLight.DEVICE_IDENTIFIER Then
                Try
                    brickletAmbientLight = New BrickletAmbientLight(UID, ipcon)
                    brickletAmbientLight.SetIlluminanceCallbackPeriod(1000)
                    AddHandler brickletAmbientLight.Illuminance, AddressOf IlluminanceCB
                    System.Console.WriteLine("Ambient Light initialized")
                Catch e As TinkerforgeException
                    System.Console.WriteLine("Ambient Light init failed: " + e.Message)
                    brickletAmbientLight = Nothing
                End Try
            Else If deviceIdentifier = BrickletAmbientLightV2.DEVICE_IDENTIFIER Then
                Try
                    brickletAmbientLightV2 = New BrickletAmbientLightV2(UID, ipcon)
                    brickletAmbientLightV2.SetConfiguration(BrickletAmbientLightV2.ILLUMINANCE_RANGE_64000LUX, _
                                                            BrickletAmbientLightV2.INTEGRATION_TIME_200MS)
                    brickletAmbientLightV2.SetIlluminanceCallbackPeriod(1000)
                    AddHandler brickletAmbientLightV2.Illuminance, AddressOf IlluminanceV2CB
                    System.Console.WriteLine("Ambient Light 2.0 initialized")
                Catch e As TinkerforgeException
                    System.Console.WriteLine("Ambient Light 2.0 init failed: " + e.Message)
                    brickletAmbientLightV2 = Nothing
                End Try
            Else If deviceIdentifier = BrickletAmbientLightV3.DEVICE_IDENTIFIER Then
                Try
                    brickletAmbientLightV3 = New BrickletAmbientLightV3(UID, ipcon)
                    brickletAmbientLightV3.SetConfiguration(BrickletAmbientLightV3.ILLUMINANCE_RANGE_64000LUX, _
                                                            BrickletAmbientLightV3.INTEGRATION_TIME_200MS)
                    brickletAmbientLightV3.SetIlluminanceCallbackConfiguration(1000, True, "x"C, 0, 0)
                    AddHandler brickletAmbientLightV3.IlluminanceCallback, AddressOf IlluminanceV3CB
                    System.Console.WriteLine("Ambient Light 3.0 initialized")
                Catch e As TinkerforgeException
                    System.Console.WriteLine("Ambient Light 3.0 init failed: " + e.Message)
                    brickletAmbientLightV3 = Nothing
                End Try
            Else If deviceIdentifier = BrickletHumidity.DEVICE_IDENTIFIER Then
                Try
                    brickletHumidity = New BrickletHumidity(UID, ipcon)
                    brickletHumidity.SetHumidityCallbackPeriod(1000)
                    AddHandler brickletHumidity.Humidity, AddressOf HumidityCB
                    System.Console.WriteLine("Humidity initialized")
                Catch e As TinkerforgeException
                    System.Console.WriteLine("Humidity init failed: " + e.Message)
                    brickletHumidity = Nothing
                End Try
            Else If deviceIdentifier = BrickletHumidityV2.DEVICE_IDENTIFIER Then
                Try
                    brickletHumidityV2 = New BrickletHumidityV2(UID, ipcon)
                    brickletHumidityV2.SetHumidityCallbackConfiguration(1000, True, "x"C, 0, 0)
                    AddHandler brickletHumidityV2.HumidityCallback, AddressOf HumidityV2CB
                    System.Console.WriteLine("Humidity 2.0 initialized")
                Catch e As TinkerforgeException
                    System.Console.WriteLine("Humidity 2.0 init failed: " + e.Message)
                    brickletHumidityV2 = Nothing
                End Try
            Else If deviceIdentifier = BrickletBarometer.DEVICE_IDENTIFIER Then
                Try
                    brickletBarometer = New BrickletBarometer(UID, ipcon)
                    brickletBarometer.SetAirPressureCallbackPeriod(1000)
                    AddHandler brickletBarometer.AirPressure, AddressOf AirPressureCB
                    System.Console.WriteLine("Barometer initialized")
                Catch e As TinkerforgeException
                    System.Console.WriteLine("Barometer init failed: " + e.Message)
                    brickletBarometer = Nothing
                End Try
            Else If deviceIdentifier = BrickletBarometerV2.DEVICE_IDENTIFIER Then
                Try
                    brickletBarometerV2 = New BrickletBarometerV2(UID, ipcon)
                    brickletBarometerV2.SetAirPressureCallbackConfiguration(1000, True, "x"C, 0, 0)
                    AddHandler brickletBarometerV2.AirPressureCallback, AddressOf AirPressureV2CB
                    System.Console.WriteLine("Barometer 2.0 initialized")
                Catch e As TinkerforgeException
                    System.Console.WriteLine("Barometer 2.0 init failed: " + e.Message)
                    brickletBarometerV2 = Nothing
                End Try
            End If
        End If
    End Sub

    Sub ConnectedCB(ByVal sender As IPConnection, ByVal connectedReason as Short)
        If connectedReason = IPConnection.CONNECT_REASON_AUTO_RECONNECT Then
            System.Console.WriteLine("Auto Reconnect")
            while True
                Try
                    ipcon.Enumerate()
                    Exit While
                Catch e As NotConnectedException
                    System.Console.WriteLine("Enumeration Error: " + e.Message)
                    System.Threading.Thread.Sleep(1000)
                End Try
            End While
        End If
    End Sub

    Sub Main()
        ipcon = New IPConnection()
        while True
            Try
                ipcon.Connect(HOST, PORT)
                Exit While
            Catch e As System.Net.Sockets.SocketException
                System.Console.WriteLine("Connection Error: " + e.Message)
                System.Threading.Thread.Sleep(1000)
            End Try
        End While

        AddHandler ipcon.EnumerateCallback, AddressOf EnumerateCB
        AddHandler ipcon.Connected, AddressOf ConnectedCB

        while True
            try
                ipcon.Enumerate()
                Exit While
            Catch e As NotConnectedException
                System.Console.WriteLine("Enumeration Error: " + e.Message)
                System.Threading.Thread.Sleep(1000)
            End Try
        End While

        System.Console.WriteLine("Press key to exit")
        System.Console.ReadLine()
        ipcon.Disconnect()
    End Sub
End Module