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.
Wir setzen weiterhin voraus, dass ein passender Rauchmelder mit einem Industrial Digital In 4 Bricklet verbunden wurde wie hier beschrieben.
Wir setzen uns folgendes Ziel für dieses Projekt:
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
Im Folgenden werden wir Schritt für Schritt zeigen wie diese Ziele erreicht werden können.
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 SmokeDetector
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
Während des Enumerierungsprozesses soll das Industrial Digital In 4 Bricklet konfiguriert werden. Dadurch ist sichergestellt, dass es neu konfiguriert wird 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
Das Industrial Digital In 4 Bricklet wird so eingestellt, dass es die
InterruptCB
Callback-Funktion aufruft wenn sich die Spannung an einem
der Eingänge verändert. Die Entprellperiode wird auf 10s (10000ms) gestellt,
um zu vermeiden zu viele Callback zu erhalten. Interrupt-Erkennung wird für
alle Eingänge aktiviert (15 = 0b1111).
If deviceIdentifier = BrickletIndustrialDigitalIn4.DEVICE_IDENTIFIER Then
brickletIndustrialDigitalIn4 = New BrickletIndustrialDigitalIn4(UID, ipcon)
brickletIndustrialDigitalIn4.SetDebouncePeriod(10000)
brickletIndustrialDigitalIn4.SetInterrupt(15)
AddHandler brickletIndustrialDigitalIn4.Interrupt, AddressOf InterruptCB
End If
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 = BrickletIndustrialDigitalIn4.DEVICE_IDENTIFIER Then
brickletIndustrialDigitalIn4 = New BrickletIndustrialDigitalIn4(UID, ipcon)
brickletIndustrialDigitalIn4.SetDebouncePeriod(10000)
brickletIndustrialDigitalIn4.SetInterrupt(15)
AddHandler brickletIndustrialDigitalIn4.Interrupt, AddressOf InterruptCB
End If
End If
End Sub
Jetzt müssen wir noch auf das Alarmsignal des Rauchmelders reagieren. Es soll
aber nur auf das Einschalten der LED reagiert werden, nicht auf das
Ausschalten. Dazu wird valueMask
auf > 0
geprüft, in diesem Fall liegt
an mindesten einem Eingang eine Spannung an, sprich die LED leuchtet.
Sub InterruptCB(ByVal sender As BrickletIndustrialDigitalIn4, _
ByVal interruptMask As Integer, ByVal valueMask As Integer)
If valueMask > 0 Then
System.Console.WriteLine("Fire! Fire!")
End If
End Sub
Das ist es. Wenn wir diese drei Schritte zusammen in eine Datei kopieren und ausführen, dann hätten wir jetzt eine funktionierendes Programm, das den Alarmstatus eines Rauchmelders ausließt und auf dessen Alarmsignal reagiert.
In der jetzigen Form gibt das Programm nur eine Meldung aus. Dies kann auf verschiedene Weise verbessert werden. Zum Beispiel könnte das Programm jemanden per E-Mail oder SMS über den Alarm informieren.
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!
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 dies 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 der Master Brick angeschlossen ist.
Es müssen auch noch mögliche Fehler während des Enumerierungsprozesses behandelt werden:
If deviceIdentifier = BrickletIndustrialDigitalIn4.DEVICE_IDENTIFIER Then
Try
brickletIndustrialDigitalIn4 = New BrickletIndustrialDigitalIn4(UID, ipcon)
brickletIndustrialDigitalIn4.SetDebouncePeriod(10000)
brickletIndustrialDigitalIn4.SetInterrupt(15)
AddHandler brickletIndustrialDigitalIn4.Interrupt, AddressOf InterruptCB
System.Console.WriteLine("Industrial Digital In 4 initialized")
Catch e As TinkerforgeException
System.Console.WriteLine("Industrial Digital In 4 init failed: " + e.Message)
brickletIndustrialDigitalIn4 = Nothing
End Try
End If
Zusätzlich wollen wir noch ein paar Logausgaben einfügen. Diese ermöglichen es später herauszufinden was ein mögliches Problem ausgelöst hat.
Zum Beispiel, wenn der Master Brick über WLAN angebunden ist und häufig Auto-Reconnects auftreten, dann ist wahrscheinlich die WLAN Verbindung nicht sehr stabil.
Jetzt sind alle für gesteckten Ziele für unseren gehackten Rauchmelder erreicht.
Das gesamte Programm für den gehackten Rauchmelder (download):
Imports Tinkerforge
Module SmokeDetector
Const HOST As String = "localhost"
Const PORT As Integer = 4223
Private ipcon As IPConnection = Nothing
Private brickletIndustrialDigitalIn4 As BrickletIndustrialDigitalIn4 = Nothing
Sub InterruptCB(ByVal sender As BrickletIndustrialDigitalIn4, _
ByVal interruptMask As Integer, ByVal valueMask As Integer)
If valueMask > 0 Then
System.Console.WriteLine("Fire! Fire!")
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 = BrickletIndustrialDigitalIn4.DEVICE_IDENTIFIER Then
Try
brickletIndustrialDigitalIn4 = New BrickletIndustrialDigitalIn4(UID, ipcon)
brickletIndustrialDigitalIn4.SetDebouncePeriod(10000)
brickletIndustrialDigitalIn4.SetInterrupt(15)
AddHandler brickletIndustrialDigitalIn4.Interrupt, AddressOf InterruptCB
System.Console.WriteLine("Industrial Digital In 4 initialized")
Catch e As TinkerforgeException
System.Console.WriteLine("Industrial Digital In 4 init failed: " + e.Message)
brickletIndustrialDigitalIn4 = 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