SYS TEC - bLog

Python Programmierbeispiel: Steuerung über CAN-Bus

| Themen

Das folgende Beispiel einer Python-Applikation soll die bisher beschriebenen, theoretischen Vorteile in einer praktischen Anwendung darlegen und untermauern. Die Applikation wird auf dem sysWORXX CTR-700 ausgeführt und verwendet dabei neben einigen Standardbibliotheken, parallel die IO-Treiberbibliothek für das sysWORXX CTR-700 und somit die vorhandene Hardware des Gerätes. Das Programm baut eine CAN-Verbindung auf und wartet darüber auf eintreffende Kommandos eines speziellen „Master“-Gerätes.

Die gesamte Applikation steht natürlich auch zum Download bereit, so dass sie komfortabel und unkompliziert verwendet und nachvollzogen werden kann. In dem zur Applikationsbeschreibung gehörenden Download-Archiv sind alle Schritte noch einmal als Shell-Skript zusammengefasst, so dass nur dieses Skript ausgeführt werden muss. Die Installation des Python-Interpreters ist hier nicht notwendig, dieser ist standardmäßig vorinstalliert.

Voraussetzung
Für die hier beschriebene Applikation wird eine Gegenstelle am CAN-Bus benötigt, über die Nachrichten an das sysWORXX CTR-700 gesendet werden können. In diesem Beispiel wird dazu das sysWORXX USB-CANmodul1, zusammen mit dem CANinterpreter von emotas verwendet.

Es ist möglich, beide Komponenten durch andere, vergleichbare Produkte auszutauschen, die im Folgenden beschriebenen Schritte beziehen sich jedoch direkt auf die oben genannten Komponenten.

Neben der Hardware werden auch entsprechende Softwarekomponenten vorausgesetzt. Als Interpreter-Sprache benötigt Python, im Gegensatz zu C, oder C++, keinen Compiler und dadurch auch keine komplizierte Toolchain oder IDE. Für die Entwicklung auf einer beliebigen Plattform muss lediglich der Python-Interpreter installiert werden, welcher auf dem sysWORXX CTR-700 bereits vorinstalliert ist. Für andere Plattformen ist die Anleitung dafür unter folgendem Link zu finden: www.python.org/downloads/

Prinzipiell ist für die Softwareentwicklung in Python jeder Texteditor geeignet. Für dieses Beispiel wird Visual Studio Code verwendet, da hier der Konfigurationsaufwand minimal ist und sich durch Erweiterungen viele Komfortfunktionen (Autocomplete, Linting, Format-Checker, usw.) integrieren lassen.

Zusätzlich werden auf dem sysWORXX CTR-700 weitere Software-Komponenten benötigt. Diese sind nicht standardmäßig auf dem Gerät vorinstalliert und müssen bei Bedarf nachinstalliert werden. Dazu gehören „python3-pip“, „python3-dev“, „libffi-dev“ und die Pythonbibliothek „cffi“. Um diese zu installieren, muss die folgende Befehlsreihe ausgeführt werden:

apt install python3-pip, python3-dev, libffi-dev

pip3 install cffi

Anmerkung: Es ist ratsam, dass diese Komponenten (insbesondere „libffi-dev“ und „cffi“) auf dem Entwicklungssystem installiert sind. Andernfalls kann es zu unvorhersehbaren und nicht nachvollziehbaren Laufzeitfehlern kommen.

Die Verwendung der Bibliothek für das sysWORXX CTR-700
Die Verwendung der Klassenbibliothek ist identisch mit der Nutzung regulärer Standardbibliotheken von Python. Die Datei „ctr700drv.py“ muss sich im Ausführungs- und Projektverzeichnis der Applikation befinden. Folgende Abbildung rechts zeigt, wie die Projektstruktur aussehen sollte.

Mit Hilfe der "import" Anweisung wird die Bibliothek in die Python-Applikation eingebunden:

from ctr700drv import Ctr700Drv

Damit wird die Klasse „Ctr700Drv“ aus der Treiberbibliothek importiert und kann von der Applikation genutzt werden. Um den Treiber zu verwenden, muss eine Instanz der Treiber-Klasse erstellt und anschließend initialisiert werden. Im Nachgang kann auf jede der importierten Methoden zugegriffen werden. Die folgende einfache Sequenz beschreibt solch eine einfache Anwendung. Nach dem Erstellen einer Instanz und der Initialisierung wird der digitale Ausgang 0 auf „True“ gesetzt und der Treiber de-initialisiert.

ctr700 = Ctr700Drv()

ctr700.init()

ctr700.set_digi_out(0, True)

ctr700.shutdown()

Programmablauf

I Start der Anwendung
Das Programm ist in der Datei „python_can_demo.py“ implementiert. Diese muss zusammen mit der Treiberbibliothek des sysWORXX CTR-700 in dem gleichen Verzeichnis abgelegt sein. Die Treiberbibliothek kann aus dem verlinken Downloadverzeichnis oder der von SYS TEC angebotenen virtuellen Maschine für die Entwicklung mit dem sysWORXX CTR-700 entnommen werden.

Aufgerufen wird die Anwendung mit dem folgenden Befehl:

python3 python_can_demo.py can0

Der Übergabeparameter „can0“ stellt dabei das zu verwendende Interface dar.

II Aufbau und Ablauf der Anwendung
Bevor die „main()“-Funktion ausgeführt wird, werden zunächst alle notwendigen Bibliotheken importiert und notwendige Konstanten definiert. Importiert werden neben den Standardbibliotheken „socket“, „struct“, „sys“ und „subprocess“ auch die Treiberbibliothek „ctr700drv“. Im Fall der beiden zuletzt genannten, wird im speziellen jeweils die Klasse „call“ und „Ctr700Drv“ importiert. Eine kurze Erklärung zu den jeweiligen Bibliotheken ist der folgenden Tabelle zu entnehmen. Weiterführende Hinweise zur Verwendung sind auf der offiziellen Website der Python-Dokumentation zu finden: https://docs.python.org/3.5/

Die verwendeten Konstanten erleichtern es, nachträglich Änderungen am Programm vorzunehmen, beispielsweise um Identifikationen anzupassen. Konkret werden sechs Variablen angelegt:
 

CAN_FRAME_FMT = "=IB3x8s"

CAN_EFF_FLAG = 0x80000000

CAN_MASTER = CAN_EFF_FLAG | 0x01000100

CAN_CTR700 = CAN_EFF_FLAG | 0x01000101

CAN_CMD_BYTE_DO = 0xa9

CAN_CMD_BYTE_D1 = 0xa8

Nach dem Import und der Konstantendefinition wird die „main()“-Funktion ausgeführt. In dieser wird überprüft, ob ein Übergabeparameter beim Programmaufruf angegeben wurde und falls nicht, wird die Anwendung mit einem Hinweis auf das Fehlen beendet.

def main():

    if len(sys.argv) != 2:

        print('Usage: {} INTERFACE'.format(sys.argv[0]))

        print('INTERFACE can be can0, can1, etc.')

        sys.exit(0)

    with App() as app:

        app.run()

Wenn der Parameter vorhanden ist, wird die Klasse „App“ initialisiert und mit der Methode „run()“ direkt ausgeführt. Diese Klasse beinhaltet alle Hauptfunktionen des Programms. An dieser Stelle kommt auch direkt eine Besonderheit von Python zum Tragen: für Klassen gibt es spezielle Methoden mit besonderer Bedeutung, wenn diese mit dem „with“-Aufruf verwendet werden: Pythons magische, oder auch Dunder-Methoden kurz für: double under (score) genannt. Dieser Aufruf ersetzt in zwei Zeilen den aus anderen Programmiersprachen bekannten und auch in Python verwendbaren „try-except-finally“-Block.

Beispielsweise startet die „__init__“-Methode immer dann, wenn eine neue Instanz einer Klasse erstellt wird, in diesem Beispiel also, bevor die Methode „run()“ ausgeführt wird.

Die folgende Tabelle zeigt einen Überblick, über die in der Applikation verwendeten Dunder-Methoden:

Die Verwendung dieser Methoden ist optional, aber empfehlenswert, da sich dadurch die aktiv verwendeten Methoden schlanker und damit lesbarer halten lassen (De-/Initialisierungen fallen weg, usw.).

Die „__init__“-Methode erfüllt hier den Zweck, dass diese eine Klasseninstanz für den Treiber des sysWORXX CTR-700 anlegt.

def __init__(self):

    self.ctr700 = Ctr700Drv()

Beim Betreten des „with“-Aufrufs wird die „__enter__“-Methode ausgeführt, welche den Treiber für das sysWORXX CTR-700 initialisiert, alle digitalen Ausgänge auf 0 setzt, anschließend das CAN-Interface mit 1000 kbit/s initialisiert, den Socket konfiguriert und an das Interface (als Parameter übergeben) bindet.

def __enter__(self):

        """

        Initialize CTR-700 Driver, reset digital inputs, set CAN

        interface an and create CAN socket

        """

        self.ctr700.init()

        # Clear all 16 digital outputs

        for element in range(16):

            self.ctr700.set_digi_out(element, False)

        set_can_interface_up()

        # Create a raw socket and bind it to the CAN interface

        self.socket_can = socket.socket(

            socket.AF_CAN, socket.SOCK_RAW, socket.CAN_RAW)

        self.socket_can.bind((sys.argv[1],))

        return self

Nach der Initialisierung aller Komponenten, wird nun die „run()“-Methode ausgeführt.

Zunächst wird auf den Empfang einer CAN-Nachricht gewartet. Dies ist ebenso wieder eine eigene Methode, welche empfangene Nachrichten übersetzt und anschließend im Terminal für den Menschen lesbar ausgibt. Sie wird in die CAN-ID aufgeteilt, die Länge der Nachrichten in Byte und den eigentlich Dateninhalt.

Sobald eine Nachricht eingetroffen ist, wird überprüft, ob diese vom vorher definierten „CAN_MASTER“ gesendet wurde. Ist dies nicht der Fall, wird die Nachricht verworfen und eine entsprechende Auswertung ausgegeben. Für den Fall, dass die Nachricht vom bestimmten „CAN_MASTER“ kommt, wird für die Weiterverarbeitung eine Überprüfung des ersten Daten-Bytes vorgenommen.

Das erste Byte (Byte 0) enthält die Information, welche weiteren Aktionen von der Applikation durchgeführt werden soll: Ein- oder Ausschalten eines digitalen Ausgangs oder das Lesen eines digitalen Eingangs. Je nach  Aktion wird eine Callback-Funktion in Form der Variable „command_handler“ ausgewählt und anschließend in

der „run()“-Methode aufgerufen. Sollte das Byte keinem der beiden Möglichkeiten entsprechen, oder die Datenlänge der Nachricht nicht der Vorgabe entsprechen (zu lang oder zu kurz), wird auch hier eine entsprechende Callback-Funktion aufgerufen.

In den jeweiligen Callback-Funktionen wird ausgewertet, welcher Inhalt im zweiten und dritten Daten-Byte (Byte 1 und 2) der CAN-Nachricht vorhanden ist. Diese Bytes bestimmen, welcher digitale Eingang gelesen, bzw. welcher digitale Ausgang geschrieben werden soll und führt die entsprechende Aktion anschließend aus. Wurde ein digitaler Eingang ausgelesen, wird anschließend eine CAN-Nachricht mit dem ausgelesenen Zustand gesendet. Eine Auswertung der durchgeführten Aktion wird ebenfalls in der Konsole bzw. dem Terminal ausgegeben.

Im letzten Schritt wird schließlich der komplette Durchlauf der Anwendung abgeschlossen. Innerhalb der „run()“-Methode wird nun auf die nächste CAN-Nachricht gewartet, um wie beschrieben erneut darauf zu reagieren.

Steuerung des sysWORXX CTR-700 über das USB-CANmodul
In der Beschreibung des Programmablaufs weiter oben wurde bereits mehrfach auf den Aufbau der CAN-Nachricht eingegangen. Die folgende Tabelle zeigt, wie diese konkret für diese Anwendung aufgebaut sein müssen.

Der CAN-Identifier dient dazu, der Applikation zu vermitteln, dass die Nachricht vom Anwender-Gerät gesendet wird. Ihr eigentlicher Verwendungszweck besteht jedoch in der Priorisierung von Nachrichten sowie in der Identifizierung des Inhalts. Ebenso wird das „Extended frame format“ verwendet. Es sorgt dafür, dass der Verwendungsraum der CAN-ID vergrößert wird (29 statt 11 Bit). In diesem Fall dient die Verwendung von 29 Bit Identifiern nur zur Veranschaulichung. Die meisten in der Praxis verwendeten CAN-Applikationen nutzen üblicherweise 11 Bit Identifier.

Das Datenbyte 0 gibt an, welche Aktion ausgeführt werden soll: 0xA8 bedeutet digitale Eingänge lesen, 0xA9 bedeutet digitale Ausgänge verändern; während das nächste Datenbyte den verwendeten Ein- bzw. Ausgang festlegt. Es darf dabei den Bereich von 0x00 bis 0x0F nicht überschreiten. Andernfalls wird die Nachricht als fehlerhaft interpretiert und verworfen

Beim Verwenden der digitalen Ausgänge wird zusätzlich noch das dritte Datenbyte ausgewertet – ist dies gleich 0x00 wird der Zustand auf „False“ (Low-Pegel) gesetzt, ist es ungleich 0x00 wird es auf „True“ (High-Pegel) gesetzt.

Die Applikation sendet selbst CAN-Nachrichten, wenn der Zustand eines digitalen Eingangs ausgelesen wird. Diese sind so aufgebaut, wie die folgende Tabelle zeigt.

Der Aufbau der Nachricht ist vergleichbar wie bereits oben beschrieben:

  • CAN-ID: Identifikation der Applikation
  • Byte 0: 0xA8 – digitaler Eingang wurde gelesen
  • Byte 1: 0x00 bis 0x0F – ausgelesener Eingang
  • Byte 2: 0x00 bis 0x01 – Zustand des Eingangs

Zusätzlich ist zu beachten, dass alle Knoten am CAN-Bus dieselbe Baudrate verwenden müssen. Hier im Beispiel sind es 1000 kbit/s.

Konfiguration des USB-CANmoduls und CANinterpreter
Wie bereits erwähnt, wird für diese Anwendung ein sysWORXX USB-CANmodul1 zusammen mit dem CANinterpreter von emotas verwendet. Damit das Modul erfolgreich mit der Applikation auf dem sysWORXX CTR-700 kommunizieren kann, muss es zunächst konfiguriert werden. Im gestarteten CANinterpreter wird über „Connection à CAN Interface Settings“ die korrekte Einstellung eingetragen. Die Abbildung zeigt, wie diese auszusehen haben.

Sind die Einstellungen vorgenommen und das Modul mit dem PC verbunden, kann über das Icon „Connect“ oben links die Verbindung aufgebaut werden. Die folgende Abbildung zeigt das Hauptfenster des CANinterpreters im verbundenen Zustand und nach dem Absenden aller drei Nachrichten.

Die auf der Konsole des sysWORXX CTR-700 ausgegebenen Informationen sind in der folgenden Abbildung zu sehen:

Abschließende Hinweise
Das vorliegende Beispiel zeigt anschaulich, wie einfach für sysWORXX CTR-700 Applikationen in Python entwickelt werden können. Jeder Python-Entwickler kann auf dieser Basis eigene Applikationen entwickeln, welche direkt mit den Schnittstellen des Gerätes interagieren. Statt dieser Anwendung, kann auch ein viel komplexeres und sicheres Übertragungsprotokoll entwickelt werden. Ebenso ist es möglich, weitere Hardware (analoge Eingänge, Relais, usw.) des sysWORXX CTR-700 mit einzubeziehen, wie z.B. den RUN-Switch, analoge Eingänge, Relais, usw.

Für den Einstieg und die generelle Verwendung des CTR-700 bieten wir auf unserer Produkt-Website eine Linux-VM zum Download an. Diese umfasst eine vorkonfigurierte Xubuntu-Installation. Ebenso beinhaltet diese die Klassenbibliothek, welche für die Softwareentwicklung mit Python benötigt wird. Dort ist auch weiteres Informationsmaterial, wie das Gerätehandbuch, SD-Karten-Image (Betriebssystem, usw.) zu finden, aber auch weiterführende Dokumentation zum sysWORXX CTR-700.