Mit dem Pokeys direkt per UDP sprechen, um die 1-Wire Sensoren auszulesen

Wir hatten bereits früher gezeigt, wie man per PoBlocks verschiedene Sensoren auslesen kann. Leider musste dazu der Sensor unbedingt an Pin 55 angeschlossen werden. Jetzt haben wir nach einer Lösung gesucht, Sensoren an beliebigen Pins auslesen zu können.

Die Pokeys Protocol Specification beinhaltet im Kapitel 1-Wire Parameter, mit denen man den Pin angeben kann. Deswegen haben wir uns damit eingehender beschäftigt. Dabei kommt heraus, dass wir den Pokeys direkt per UDP ansprechen können. Dies ist sogar recht einfach.

In diesem Kapitel zeigen wir, wie man das ganz einfach umsetzen kann. Die Beispiele sind dabei in Python umgesetzt, lassen sich aber auch sehr leicht in andere Sprachen portieren.

Die UDP Schnittstelle

Wir nutzen den Pokeys 57 EN, also einen Pokey aus der 57er Reihe mit Netzwerk Anschluss. Der Pokeys muss aus der 57er Reihe sein, weil erst diese beliebige Pins für 1-Wire erlaubt. Die Netzwerkschnittstelle nutzen wir zur Kommunikation. die im folgenden beschriebenen Befehle funktionieren auch bei der USB-Schnittstelle, aber die Schnittstelle lässt sich nicht ganz so einfach ansprechen.

Vorbereitung

Zur Vorbereitung starten wir PoBlocks und merken uns die IP Adresse des Pokeys. Wir könnten auch mit dem Broadcast starten, um die IP Adresse heraus zu finden, aber das brauchen wir nicht wirklich.

Den Port gibt der Pokeys vor. Grundsätzlich ist der Port 20055 zu nutzen, unabhängig davon, ob wie mit dem Pokeys per UDP oder per TCP sprechen.

Für die ersten Versuche nutzen wir ein Tool, mit dem wir direkt UDP Kommando verschicken können. Hierzu nutzen wir den Packet Sender. Dieses Tool lässt sich sehr einfach bedienen, hat aber ein paar kleinere Bugs.

Struktur des Befehls

Die Struktur der Befehle ist ziemlich einfach. Ein Befehl ist grundsätzlich 64 Byte lang. Auch die Antwort, die zurück gesendet wird, ist immer 64 Byte lang. Dabei werden nicht immer alle Bytes verwendet. Bytes, die nicht verwendet werden, sind dabei immer auf 00 zu setzen.

Die ersten 8 Byte haben eine ziemlich feste Struktur. Diese sieht wie folgt aus:

  • Das erste Byte des Kommandos ist grundsätzlich BB. Bei einer Antwort ist das erste Byte übrigens immer AA.
  • Das zweite Byte ist immer der spezifische Befehlsblock. Für 1-Wire ist das immer der Block DC. Für unser erstes Kommando ist das 00.
  • Das dritte bis sechste Byte sind für weitere Informationen des Befehls vorgesehen. Dies ist je nach Befehlsblock unterschiedlich.
  • Das siebte Byte ist die Request ID. Diese dient dazu, einen Antwort einem Kommando zuzuordnen. Typischerweise zählt man hier einfach hoch. Der Pokeys überprüft die Zahl aber nicht, es kann auch immer die gleiche Request ID verwendet werden. Dann kann man die Antworten aber nicht gut unterscheiden.
  • Das Achte Byte ist die Checksumme. Hier werden die Bytes 1-7 addiert und das Ergebnis wird in Byte 8 eingetragen. Ist die Summe größer als FF, so muss natürlich mit Modulo 0x100 gerechnet werden, damit die Größe auch wirklich ein Byte ist.

Die Bytes nach der Checksumme werden nur in einigen Fällen benötigt. Für unserer 1-Wire Befehle werden sie eigentlich immer gebraucht. Sie fließen nicht in die Checksumme mit ein.

Das erste Kommando – Statusinformationen

Als erstes Kommando werden wir uns die Informationen vom Pokeys anzeigen lassen. Dieses Kommando hat einen sehr einfach Struktur. Wir müssen nur das erste Byte, die Request ID und die Checksumme berechnen. Alle anderen Bytes sind 00. Der Datenblock sieht also wie folgt aus:

bb 00 00 00 00 00 01 bc 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Wichtig ist es, wirklich 64 Bytes zu senden. Sind es zu viele oder zu wenige wird der Pokeys nicht antworten. Eingetragen sieht das dann wie folgt aus:

Befehl per Packet Sender verschicken
Befehl per Packet Sender verschicken

Wir bekommen von Pokeys eine Antwort, die wie folgt aussieht:

AA 00 5D 21 33 16 01 72 50 4B 45 78 21 5D 00 00 
33 16 1F 00 41 70 72 20 32 34 20 32 30 31 38 50 
6F 4B 65 79 73 35 37 45 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 33 16 00 00 BA

Aus diesem Datenblock lassen sich jetzt einige Informationen auslesen. Wir benötigen keine, um per 1-Wire weiter arbeiten zu können, um zu zeigen, wie man an die Informationen kommt, schauen wir uns aber beispielhaft an, wo der Typ des Pokeys steht.

In Byte 19 steht die HW-ID. Wir haben hier ein 1F erhalten. 31 ist die HW-ID des PoKeys57Ev1.1.

Die Temperatur vom DS18B20 auslesen

Als ersten 1-Wire Sensor nehmen wir uns zwei DS18B20 vor. Die beiden Sensoren werden an den Pins 54 und 55 angeschlossen.

Wie funktioniert 1-Wire per UDP?

Für die Nutzung von 1-Wire gibt es mehrere Befehle, die wir verwenden. Es gibt Befehle, um 1-Wire auf einem Pin zu aktivieren und zu deaktivieren. Daneben gibt es einen Befehl, um das eigentlichen 1-Wire Kommando zu senden, und einen Befehl, um auf die Antwort zu warten und diese dann auch auszulesen.

Der Aufbau des 1-Wire Befehls

Der 1-Wire Befehl für das Ansteuern per UDP hat folgenden Aufbau:

  • Erstes Byte ist wie immer BB.
  • Das zweite Byte ist DC.
  • Das dritte Byte ist der Befehl für 1-Wire. Hierfür verwenden wir die folgenden 4 Varianten:
    • 00 für das Deaktivieren des 1-Wire Pin
    • 01 für das Aktivieren des 1-Wire Pin
    • 10 für das Senden eines 1-Wire Befehls
    • 11 für das Warten und das Empfangen der Daten.
  • Das vierte bis 6 Byte für weitere Informationen, abhängig vom Befehl.
  • Das siebte Byte für die Request ID
  • Das achte Byte für die Checksumme
  • Die darauffolgenden Bytes für die eigentlichen 1-Wire Befehle. Dies benötigen wir nur beim Senden des Befehls.

Wichtig ist dabei, dass die Pin in diesem Fall 0-basiert angegeben werden muss. Wollen wir also den Pin 55 ansprechen, haben wir die 54 zu verwenden, für Pin 1 haben wir die 0 zu verwenden.

Für den Ablauf ist es wichtig, dass wir nach dem Senden des Befehls immer wieder den Warte-Befehl senden, damit wir das Ergebnis auf dem Bus nicht verpassen. Dies schaffen wir manuell nicht mehr, deswegen setzen wir das ganze gleich Python um.

Umsetzung in Python

Als erstes müssen wir in Python Befehle per UDP versenden und die Antworten auswerten können. Hierzu nutzen wir die socket Bibliothek. Diese lässt sich sehr einfach einbinden:

import socket

IP = '169.254.123.201'
PORT = 20055
BUFFER_SIZE = 64

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect((IP, PORT))

command = "" # hier kommt dann das eigentliche Kommando
s.send(command)
data = s.recv(BUFFER_SIZE)

s.close()

Mit diesem Block können wir Kommandos an den Pokeys schicken. Dabei können zwischen dem connect() und dem close() auch mehrere Datenblöcke gesendet werden. Jetzt benötigen wir nur noch die entsprechenden 1-Wire Befehle und den Block, der diese dann ausführt. Dazu verwenden wir eine kleine Funktion:

def send_command(pin, onewire, datalength):

    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect((IP, PORT))

    # Pin aktivieren
    s.send(bytearray.fromhex("bb dc 01 " + '{:x}'.format(pin) + " 00 00 01 " + '{:x}'.format(153 + pin) + 56*"00"))
    data = s.recv(BUFFER_SIZE)
    time.sleep(0.01)
 
    ol = len(onewire)//3
    c = (0xbb + 0xdc + 0x10 + ol + datalength + + pin + 0x02)% 0x100
    command = bytearray.fromhex("bb dc 10 " + '{:02x}'.format(ol) + '{:02x}'.format(datalength) + '{:x}'.format(pin) + " 02 " + '{:x}'.format(c)  + onewire + (56 - ol) *"00")
    s.send(command)
    data = s.recv(BUFFER_SIZE)
    time.sleep(0.01)   
    
    gotData = False
    i = 0
    while not gotData:
        i += 1
        command = bytearray.fromhex("bb dc 11 " + '{:x}'.format(pin) + " 00 00 " + '{:02x}'.format(2 + i) + '{:x}'.format(170 + pin + i) + 56*"00")
        s.send(command)
        data = s.recv(BUFFER_SIZE)
        
        gotData = data[8] == 0x01
        time.sleep(0.01)   

    retData = data[10: 10 + datalength]

   # Pin deaktivieren
    s.send(bytearray.fromhex("bb dc 00 " + '{:x}'.format(pin) + " 00 00 " + '{:02x}'.format(3 + i) + '{:x}'.format(154 + pin + i) + 56*"00"))
    data = s.recv(BUFFER_SIZE)
    
    s.close()
   
    return retData

Jetzt werden für den Aufruf die eigentlichen 1-Wire Befehle benötigt.

Benötigte 1-Wire Befehle

Wir nutzen zwei Befehle aus dem Befehlssatz. Das ist zum einen der Befehl 44, der den Sensor auffordert, zu messen. Zum anderen ist es der Befehl BE, der die gemessenen Daten ausliest.

Vor diesem Befehl muss der 1-Wire Sensor aber noch direkt addressiert werden. Dazu wird der Befehl 55 mit der Adresse des Sensors vor dem Befehl geschrieben. Dies macht die Funktion send.

def send(self, pin, sensoraddress, command, datalength):
    
    onewire = "55 " + sensoraddress + " " + command + " "
    return send_command(pin, onewire, datalength)

Der Funktionsaufruf sieht dann wie folgt aus:

send(54, "28 ff 32 85 87 16 03 AB", "44 ", 0)
t55 = send(54, "28 ff 32 85 87 16 03 AB", "BE ", 2)

Jetzt erhalten wir zwei Bytes, die die Temperatur beinhalten. Diese müssen jetzt noch konvertiert werden. Dazu nutzen wir eine weitere kleine Funktion:

def convertTemperature(data):
    
    first = data[0]
    second = data[1]
    
    temp = 0

    for i in range (0,8):
        temp += ((first >> i) & 1) * math.pow(2, i - 4)

    for i in range (0,3):
        temp += ((second >> i) & 1) * math.pow(2, i + 4)
        
    return temp

Die Funktion kommt nur mit Temperaturen Größer 0 Grad Celsius klar. Für Temperaturen unter 0 Grad Celsius muss die Funktion noch erweitert werden.

Wie geht es weiter?

Die Skripte werden jetzt für die weiteren Sensoren portiert. Wir möchten das auch für unsere DS2413 und DS2450 verwenden.

Danach werden die Python Skripte auf die Beckhoff Steuerung portiert, so dass die Beckhoff Steuerung die Daten direkt anfragen kann.

Links:

5 Antworten auf „Mit dem Pokeys direkt per UDP sprechen, um die 1-Wire Sensoren auszulesen“

  1. Hallo,

    ich möchte zuallererst sagen, dass der Artikel sehr hilfreich ist. Allerdings fehlt genau der Teil, den ich brauche. Ich möchte auch etwas ähnliches versuchen, allerdings genau in die andere Richtung und bräuchte dafür Hilfe von jemandem, der ein PoKeys Board besitzt.

    Ich möchte ein eigenes Board entwerfen, das sich mit der PoKeys Software steuern lässt, genauer gesagt von Mach3 mit dem PoKeys plugin. Ich schreibe jetzt also zum Testen ein Programm am PC, das über UDP und TCP mit der PoKeys config software spricht. TCP funktioniert bereits und ich kann mit der Software kommunizieren, allerdings kann man in Mach3 keine fixe IP Adresse eingeben, sondern muss die Discovery Funktion über UDP verwenden.

    Hier ist also mein Problem: Ich bekomme wie spezifiziert ein leeres UDP packet und muss darauf reagieren, allerdings passiert nichts, egal welches Packet ich der Software zurückschicke.

    Könnte vielleicht jemand, der ein PoKeys Board besitzt, mit dem Programm „Wireshark“ überprüfen, welches Packet das Board sendet und mir die Ergebnisse zukommen lassen? Andernfalls könnte man auch einfach mit dem obigen Python-Programm den leeren UDP-Broadcast senden und sehen, was vom Board zurückkommt. Ich müsste den Inhalt des Packets wissen, die Länge und auf welchen IP Adressen und Ports es gesendet wurde.

    Es würde mir sehr viel weiterhelfen, ich hoffe auf eine Antwort.

    Mit freundlichen Grüßen
    Florian

  2. Ich habe mein Problem schon gelöst, ich benötige keine Hilfe mehr.
    Ich habe einfach den Support von PoKeys kontaktiert und er war sehr hilfreich.

    1. Hallo Florian,

      das freut mich, dass Du so eine schnelle Lösung gefunden hast. Ja der Support von PoKeys ist echt gut, mir hat er auch schon mehrfach geholfen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.