Read out Smoke Detectors using Visual Basic .NET – Tinkerforge

- Getting Started
  - Primer
  - Tutorials
  - FAQ
- Hardware
  - Bricks
  - Bricklets
  - Master Extensions
  - Power Supplies
  - Accessories
  - Timeline
- Software
  - Brick Daemon (brickd)
  - Brick Viewer (brickv)
  - API Bindings
  - Firmwares and Plugins
  - Device Identifier
  - Source Code and Bug Tracking
  - Programming Interface
- Starter Kits
  - Weather Station
  - Hardware Hacking
    - Features
    - Description
    - Technical Specifications
    - Resources
    - Required Tools
    - Firmware updating and first tests
    - How it works
    - Hardware Hacking for Beginners
    - Examples
      - Control Mains Switches Remotely
      - Read out Smoke Detectors
        - Hardware Setup
        - Using C/C++
        - Using C#
        - Using Delphi/Lazarus
        - Using Java
        - Using PHP
        - Using Python
        - Using Ruby
        - Using Visual Basic .NET
          - Goals
          - Step 1: Discover Bricks and Bricklets
          - Step 2: Initialize Bricklet on Enumeration
          - Step 3: Handle the alarm signal
          - Step 4: Error handling and Logging
          - Step 5: Everything put together
      - Control Garage Doors Remotely
      - Is someone at the Door? - Doorbell Notifier
  - Server Room Monitoring
  - Blinkenlights
  - Internet of Things
  - Camera Slider
- Embedded Boards
  - Raspberry Pi
- Specifications
  - Technical Data
  - TCP/IP Protocol
  - Modbus Protocol
  - Wireshark Dissector

# Read out Smoke Detectors using Visual Basic .NET

For this project we are assuming, that you have a Visual Basic .NET development environment set up and that you have a rudimentary understanding of the Visual Basic .NET language.

If you are totally new to Visual Basic .NET itself you should start here. If you are new to the Tinkerforge API, you should start here.

We are also assuming that you have a smoke detector connected to an Industrial Digital In 4 Bricklet as described here.

## Goals

We are setting the following goal for this project:

- Read out the alarm status of a smoke detector
- and react on its alarm signal.

Since this project will likely run 24/7, we will also make sure that the application is as robust towards external influences as possible. The application should still work when

- Bricklets are exchanged (i.e. we don't rely on UIDs),
- Brick Daemon isn't running or is restarted,
- WIFI Extension is out of range or
- Brick is restarted (power loss or accidental USB removal).

In the following we will show step-by-step how this can be achieved.

## Step 1: Discover Bricks and Bricklets

To start off, we need to define where our program should connect to:

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

If the WIFI Extension is used or if the Brick Daemon is running on a different PC, you have to exchange "localhost" with the IP address or hostname of the WIFI Extension or PC.

When the program is started, we need to register the `EnumerateCallback` callback and the `Connected` callback and trigger a first enumerate:

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

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

        ipcon.Enumerate()
    End Sub

The enumerate callback is triggered if a Brick gets connected over USB or if the `Enumerate()` function is called. This allows to discover the Bricks and Bricklets in a stack without knowing their types or UIDs beforehand.

The connected callback is triggered if the connection to the WIFI Extension or to the Brick Daemon got established. In this callback we need to trigger the enumerate again, if the reason is an auto reconnect:

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

An auto reconnect means, that the connection to the WIFI Extension or to the Brick Daemon was lost and could subsequently be established again. In this case the Bricklets may have lost their configurations and we have to reconfigure them. Since the configuration is done during the enumeration process (see below), we have to trigger another enumeration.

Step 1 put together:

    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

## Step 2: Initialize Bricklet on Enumeration

During the enumeration we want to configure the Industrial Digital In 4 Bricklet. Doing this during the enumeration ensures that the Bricklet gets reconfigured if the Brick was disconnected or there was a power loss.

The configurations should be performed on first startup (`ENUMERATION_TYPE_CONNECTED`) as well as whenever the enumeration is triggered externally by us (`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

We configure the Industrial Digital In 4 Bricklet to call the `InterruptCB` callback if a change of the voltage level on any input pin is detected. The debounce period is set to 10s (10000ms) to avoid being spammed with callbacks. Interrupt detection is enabled for all inputs (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

Step 2 put together:

    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

## Step 3: Handle the alarm signal

Now we need to react on the alarm signal of the smoke detector. But we want to react only if the LED is turned on, not if it is turn off. This is done by checking `valueMask` for being `> 0`. In that case there is a voltage applied to at least one input, therefore, the LED is on.

    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

That's it. If we would copy these three steps together in one file and execute it, we would have a working program that reads the alarm status of a hacked smoke detector and reacts on its alarm signal!

Currently the program just outputs a warning. There are several ways to extend this. For example, the program could send an email or a text message to notify someone about the alarm.

However, we do not meet all of our goals yet. The program is not yet robust enough. What happens if it can't connect on startup? What happens if the enumerate after an auto reconnect doesn't work?

What we need is error handling!

## Step 4: Error handling and Logging

On startup, we need to try to connect until the connection works:

    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

and we need to try enumerating until the message goes through:

    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

With these changes it is now possible to first start the program and connect the Master Brick afterwards.

We also have to deal with errors during the initialization:

    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

Additionally we added some logging. With the logging we can later find out what exactly caused a potential problem.

For example, if we connect to the Master Brick via Wi-Fi and we have regular auto reconnects, it likely means that the Wi-Fi connection is not very stable.

## Step 5: Everything put together

That's it! We are already done with our hacked smoke detector and all of the goals should be met.

Now all of the above put together (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

The content of this page is licensed under Creative Commons Attribution 3.0 Unported License.