Tutorial - Robuster Ansatz

Mit dem neuen Protokoll 2.0 ist es möglich Programme zu schreiben, die robust gegenüber Unterbrechungen, kurzen Stromausfällen und ähnlichem sind.

Der generelle Ansatz für ein solches Programm sieht aus wie folgt (Pseudocode):

func enumerate_callback(...) {
    configure_brick();
    configure_bricklet();
}

func connected_callback(...) {
    ipcon.enumerate();
}

func main() {
    ipcon.enumerate();

    while (true) {
        if (brick_is_configured) {
            do_something_with_brick();
        }

        if (bricklet_is_configured) {
            do_something_with_bricklet();
        }
    }
}

Es muss sichergestellt werden, dass die Konfiguration von Bricks und Bricklets während der Enumerierung stattfindet. Dies führt dazu, dass die Konfiguration (z.B. Callback-Periode) immer gesetzt wird, auch wenn ein Brick oder Bricklet neu gestartet wurde und dadurch seine Konfiguration verloren hat.

Eine Möglichkeit um dies zu realisieren ist, den Konfigurationscode in das Enumeration-Callback zu schreiben. Es sollte zusätzlich sichergestellt sein, dass eine neue Enumerierung ausgelöst wird, wenn eine TCP/IP-Verbindung neu aufgebaut wird, nachdem sie getrennt wurde. Wenn eine Verbindung getrennt und wiederhergestellt wird, ist nicht ausgeschlossen, dass ein Brick oder Bricklet in der Zwischenzeit neu gestartet wurde. Deshalb muss es auch dann neu konfiguriert werden.

Im Folgenden finden sich C#- und Python-Quelltexte für ein Programm, dass eine Temperatur auf einem LCD 20x4 Bricklet anzeigt. Dieses Programm sollte auch bei einem Neustart des Master Bricks oder einer verlorenen WLAN-Verbindung weiter funktionieren. Es ist sogar möglich, das Temperature oder LCD 20x4 Bricklet auszutauschen, da das Programm die UID aus der Enumerierung benutzt.

C#

Download (ExampleRugged.cs)

  1using Tinkerforge;
  2
  3// This class will use any LCD Bricklet and Temperature Bricklet that
  4// are connected to the PC and display the temperature on the LCD.
  5//
  6// The program should stay stable if Bricks are connected/disconnected,
  7// if the Brick Daemon is restarted or if a Wi-Fi/RS485 connection is lost.
  8// It will also keep working if you exchange the Master or one of the
  9// Bricklets by a new one of the same type.
 10//
 11// If a Brick or Bricklet loses its state (e.g. callback configuration)
 12// while the connection was lost, it will automatically be reconfigured
 13// accordingly.
 14class ExampleRugged
 15{
 16    private static string HOST = "localhost";
 17    private static int PORT = 4223;
 18
 19    private static IPConnection ipcon = null;
 20    private static BrickletLCD20x4 lcd = null;
 21    private static BrickletTemperature temp = null;
 22
 23    static void Main() 
 24    {
 25        // Create IP Connection
 26        ipcon = new IPConnection();
 27
 28        // Register IP Connection callbacks
 29        ipcon.EnumerateCallback += EnumerateCB;
 30        ipcon.Connected += ConnectedCB;
 31
 32        // Connect to brickd, will trigger cb_connected
 33        ipcon.Connect(HOST, PORT); 
 34        ipcon.Enumerate();
 35
 36        System.Console.WriteLine("Press enter to exit");
 37        System.Console.ReadLine();
 38        ipcon.Disconnect();
 39    }
 40
 41    // Callback updates temperature displayed on lcd
 42    static void TemperatureCB(BrickletTemperature sender, short temperature)
 43    {
 44        if(lcd != null)
 45        {
 46            lcd.ClearDisplay();
 47            string s = "Temperature: " + temperature/100.0 + (char)0xdf + "C";
 48            lcd.WriteLine(0, 0, s);
 49        }
 50    }
 51
 52    // Callback switches lcd backlight on/off based on lcd button 0
 53    static void ButtonPressedCB(BrickletLCD20x4 sender, byte button)
 54    {
 55        if(lcd != null)
 56        {
 57            if(button == 0)
 58            {
 59                if(lcd.IsBacklightOn())
 60                {
 61                    lcd.BacklightOff();
 62                }
 63                else
 64                {
 65                    lcd.BacklightOn();
 66                }
 67            }
 68        }
 69    }
 70
 71    // Callback handles device connections and configures possibly lost
 72    // configuration of lcd and temperature callbacks, backlight etc.
 73    static void EnumerateCB(IPConnection sender, string UID, string connectedUID, 
 74                            char position, short[] hardwareVersion, 
 75                            short[] firmwareVersion, int deviceIdentifier, 
 76                            short enumerationType)
 77    {
 78        if(enumerationType == IPConnection.ENUMERATION_TYPE_CONNECTED ||
 79           enumerationType == IPConnection.ENUMERATION_TYPE_AVAILABLE)
 80        {
 81            // Enumeration is for LCD Bricklet
 82            if(deviceIdentifier == BrickletLCD20x4.DEVICE_IDENTIFIER)
 83            {
 84                // Create lcd device object
 85                lcd = new BrickletLCD20x4(UID, ipcon);
 86                lcd.ButtonPressed += ButtonPressedCB;
 87
 88                lcd.ClearDisplay();
 89                lcd.BacklightOn();
 90            }
 91            // Enumeration is for Temperature Bricklet
 92            if(deviceIdentifier == BrickletTemperature.DEVICE_IDENTIFIER)
 93            {
 94                // Create temperature device object
 95                temp = new BrickletTemperature(UID, ipcon);
 96                temp.Temperature += TemperatureCB;
 97
 98                temp.SetTemperatureCallbackPeriod(50);
 99            }
100        }
101    }
102
103    // Callback handles reconnection of IP Connection
104    static void ConnectedCB(IPConnection sender, short connectReason)
105    {
106        // Enumerate devices again. If we reconnected, the Bricks/Bricklets
107        // may have been offline and the configuration may be lost.
108        // In this case we don't care for the reason of the connection
109        ipcon.Enumerate();
110    }
111}

Python

Download (example_rugged.py)

 1#!/usr/bin/env python
 2# -*- coding: utf-8 -*-  
 3
 4from tinkerforge.ip_connection import IPConnection
 5from tinkerforge.bricklet_lcd_20x4 import LCD20x4
 6from tinkerforge.bricklet_temperature import Temperature
 7
 8# This class will use any LCD Bricklet and Temperature Bricklet that
 9# are connected to the PC and display the temperature on the LCD.
10#
11# The program should stay stable if Bricks are connected/disconnected,
12# if the Brick Daemon is restarted or if a Wi-Fi/RS485 connection is lost.
13# It will also keep working if you exchange the Master or one of the
14# Bricklets by a new one of the same type.
15#
16# If a Brick or Bricklet loses its state (e.g. callback configuration)
17# while the connection was lost, it will automatically be reconfigured
18# accordingly.
19class ExampleRugged:
20    HOST = "localhost"
21    PORT = 4223
22
23    def __init__(self):
24        self.lcd = None
25        self.temp = None
26
27        # Create IP Connection
28        self.ipcon = IPConnection() 
29
30        # Register IP Connection callbacks
31        self.ipcon.register_callback(IPConnection.CALLBACK_ENUMERATE, 
32                                     self.cb_enumerate)
33        self.ipcon.register_callback(IPConnection.CALLBACK_CONNECTED, 
34                                     self.cb_connected)
35
36        # Connect to brickd, will trigger cb_connected
37        self.ipcon.connect(ExampleRugged.HOST, ExampleRugged.PORT) 
38
39        self.ipcon.enumerate()
40
41    # Callback switches lcd backlight on/off based on lcd button 0
42    def cb_button_pressed(self, button):
43        if self.lcd:
44            if button == 0:
45                if self.lcd.is_backlight_on():
46                    self.lcd.backlight_off()
47                else:
48                    self.lcd.backlight_on()
49
50    # Callback updates temperature displayed on lcd
51    def cb_temperature(self, temperature):
52        if self.lcd:
53            self.lcd.clear_display()
54            s = 'Temperature: {0:.2f}{1:c}C'.format(temperature/100.0, 0xdf)
55            self.lcd.write_line(0, 0, s)
56
57    # Callback handles device connections and configures possibly lost 
58    # configuration of lcd and temperature callbacks, backlight etc.
59    def cb_enumerate(self, uid, connected_uid, position, hardware_version, 
60                     firmware_version, device_identifier, enumeration_type):
61        if enumeration_type == IPConnection.ENUMERATION_TYPE_CONNECTED or \
62           enumeration_type == IPConnection.ENUMERATION_TYPE_AVAILABLE:
63            
64            # Enumeration is for LCD Bricklet
65            if device_identifier == LCD20x4.DEVICE_IDENTIFIER:
66                # Create lcd device object
67                self.lcd = LCD20x4(uid, self.ipcon) 
68                self.lcd.register_callback(self.lcd.CALLBACK_BUTTON_PRESSED, 
69                                           self.cb_button_pressed)
70                self.lcd.clear_display()
71                self.lcd.backlight_on()
72            # Enumeration is for Temperature Bricklet
73            if device_identifier == Temperature.DEVICE_IDENTIFIER:
74                # Create temperature device object
75                self.temp = Temperature(uid, self.ipcon) 
76                self.temp.register_callback(self.temp.CALLBACK_TEMPERATURE, 
77                                            self.cb_temperature)
78
79                self.temp.set_temperature_callback_period(50)
80
81    # Callback handles reconnection of IP Connection
82    def cb_connected(self, connected_reason):
83        # Enumerate devices again. If we reconnected, the Bricks/Bricklets
84        # may have been offline and the configuration may be lost.
85        # In this case we don't care for the reason of the connection
86        self.ipcon.enumerate()
87        
88
89if __name__ == "__main__":
90    ExampleRugged()
91    raw_input('Press key to exit\n') # Use input() in Python 3