Python3.4 SocketCAN

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
mgolbs
User
Beiträge: 28
Registriert: Freitag 3. Februar 2012, 13:07

Hallo,

habe auf einem BeagleBone Ubuntu 14.04 Arm mit Python3.4. Zusätzlich ist eine CAN Platine auf dem Board. Die CAN Kommunikation per "linux-can-can-utils" funktioniert. Ich habe folgendes kleine Beispiel für den Einstieg in SocketCAN+Python verwendet:

http://python.readthedocs.org/en/latest ... ocket.html

Code: Alles auswählen

import socket
import struct


# CAN frame packing/unpacking (see 'struct can_frame' in <linux/can.h>)

can_frame_fmt = "=IB3x8s"
can_frame_size = struct.calcsize(can_frame_fmt)

def build_can_frame(can_id, data):
    can_dlc = len(data)
    data = data.ljust(8, b'\x00')
    return struct.pack(can_frame_fmt, can_id, can_dlc, data)

def dissect_can_frame(frame):
    can_id, can_dlc, data = struct.unpack(can_frame_fmt, frame)
    return (can_id, can_dlc, data[:can_dlc])


# create a raw socket and bind it to the 'vcan0' interface
s = socket.socket(socket.AF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
s.bind(('vcan0',))

while True:
    cf, addr = s.recvfrom(can_frame_size)

    print('Received: can_id=%x, can_dlc=%x, data=%s' % dissect_can_frame(cf))

    try:
        s.send(cf)
    except OSError:
        print('Error sending CAN frame')

    try:
        s.send(build_can_frame(0x01, b'\x01\x02\x03'))
    except OSError:
        print('Error sending CAN frame')
Grundsätzlich gibt es eine CAN Kommunikation. Nur der Empfang von Nachrichten bringt Fehler in den Datenbytes, Werte außerhalb des möglichen Bereichs.
Received: can_id=8cffaab7, can_dlc=8, data=b'\xadf\xb0f\xb0f\xabf'
Received: can_id=8cffaab9, can_dlc=8, data=b'\x82f\x84f\x89f\x86f'
Received: can_id=8cffaabb, can_dlc=8, data=b'hcfc`cic'
Received: can_id=8cff0b83, can_dlc=8, data=b'\x00\x00\x00\x00\x00\x00\x00\x00'
Received: can_id=8cff0b86, can_dlc=8, data=b'\xef\xff\xff\xbb\x00\x00\x00\x00'
Received: can_id=8cff0b89, can_dlc=8, data=b'\xc8\x1d\x04\x0e\x0f\n\t\xff'
Received: can_id=8cffaab7, can_dlc=8, data=b'\xb0f\xaef\xacf\xacf'
Received: can_id=8cffaab9, can_dlc=8, data=b'\x85f\x85f\x87f\x87f'
Received: can_id=8cffaabb, can_dlc=8, data=b'bclcfchc'
Received: can_id=8cff0b83, can_dlc=8, data=b'\x00\x00\x00\x00\x00\x00\x00\x00'
Received: can_id=8cff0b86, can_dlc=8, data=b'\xef\xff\xff\xbb\x00\x00\x00\x00'
Received: can_id=8cff0b89, can_dlc=8, data=b'\xc8\x1d\x04\x0e\x0f\n\n\xff'
ID's und dlc passen, nur eben die Datenbytes nicht immer. linux/can.h habe ich im Projektordner verlinkt und die can.h aus den linux-can-can-utils verwendet. Könnte schon der erste Fehler sein.

Hat da jemand schon Erfahrungen gemacht, kann Tipps geben?


Gruß und Dank Markus
mgolbs
User
Beiträge: 28
Registriert: Freitag 3. Februar 2012, 13:07

mgolbs hat geschrieben:Hallo,

habe auf einem BeagleBone Ubuntu 14.04 Arm mit Python3.4. Zusätzlich ist eine CAN Platine auf dem Board. Die CAN Kommunikation per "linux-can-can-utils" funktioniert. Ich habe folgendes kleine Beispiel für den Einstieg in SocketCAN+Python verwendet:

http://python.readthedocs.org/en/latest ... ocket.html

Code: Alles auswählen

import socket
import struct


# CAN frame packing/unpacking (see 'struct can_frame' in <linux/can.h>)

can_frame_fmt = "=IB3x8s"
can_frame_size = struct.calcsize(can_frame_fmt)

def build_can_frame(can_id, data):
    can_dlc = len(data)
    data = data.ljust(8, b'\x00')
    return struct.pack(can_frame_fmt, can_id, can_dlc, data)

def dissect_can_frame(frame):
    can_id, can_dlc, data = struct.unpack(can_frame_fmt, frame)
    return (can_id, can_dlc, data[:can_dlc])


# create a raw socket and bind it to the 'vcan0' interface
s = socket.socket(socket.AF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
s.bind(('vcan0',))

while True:
    cf, addr = s.recvfrom(can_frame_size)

    print('Received: can_id=%x, can_dlc=%x, data=%s' % dissect_can_frame(cf))

    try:
        s.send(cf)
    except OSError:
        print('Error sending CAN frame')

    try:
        s.send(build_can_frame(0x01, b'\x01\x02\x03'))
    except OSError:
        print('Error sending CAN frame')
Grundsätzlich gibt es eine CAN Kommunikation. Nur der Empfang von Nachrichten bringt Fehler in den Datenbytes, Werte außerhalb des möglichen Bereichs.
Received: can_id=8cffaab7, can_dlc=8, data=b'\xadf\xb0f\xb0f\xabf'
Received: can_id=8cffaab9, can_dlc=8, data=b'\x82f\x84f\x89f\x86f'
Received: can_id=8cffaabb, can_dlc=8, data=b'hcfc`cic'
Received: can_id=8cff0b83, can_dlc=8, data=b'\x00\x00\x00\x00\x00\x00\x00\x00'
Received: can_id=8cff0b86, can_dlc=8, data=b'\xef\xff\xff\xbb\x00\x00\x00\x00'
Received: can_id=8cff0b89, can_dlc=8, data=b'\xc8\x1d\x04\x0e\x0f\n\t\xff'
Received: can_id=8cffaab7, can_dlc=8, data=b'\xb0f\xaef\xacf\xacf'
Received: can_id=8cffaab9, can_dlc=8, data=b'\x85f\x85f\x87f\x87f'
Received: can_id=8cffaabb, can_dlc=8, data=b'bclcfchc'
Received: can_id=8cff0b83, can_dlc=8, data=b'\x00\x00\x00\x00\x00\x00\x00\x00'
Received: can_id=8cff0b86, can_dlc=8, data=b'\xef\xff\xff\xbb\x00\x00\x00\x00'
Received: can_id=8cff0b89, can_dlc=8, data=b'\xc8\x1d\x04\x0e\x0f\n\n\xff'
ID's und dlc passen, nur eben die Datenbytes nicht immer. linux/can.h habe ich im Projektordner verlinkt und die can.h aus den linux-can-can-utils verwendet. Könnte schon der erste Fehler sein.

Hat da jemand schon Erfahrungen gemacht, kann Tipps geben?


Gruß und Dank Markus
Lag irgend wie an data[:can_dlc]) Habe es umgebaut, und nun läuft es erst einmal.
heidi
User
Beiträge: 5
Registriert: Dienstag 5. August 2014, 08:25

Hallo Markus,

könntest Du bitte mal erklären wie Du es umgebaut hast, damit es funktioniert?

Ich benutze eine sehr ähnliche Konfiguration - BeagleBoneBlack, Angström, Python 3.4.1, CAN vom internen Ti-Prozessor, ein eigenes Cape mit einem CAN-Treiber.

Die CAN-Kommunikation funktioniert grundsätzlich sehr gut. Das Versenden von Paketen mit cansend und die Anzeige empfangener Pakete mit candump arbeitet tadellos.

Leider bin ich noch totaler Anfänger in Sachen Python und dieses Projekt gehört mit zu meinen ersten Schritten. Daher war ich sehr dankbar, zum einarbeiten das Beispielprogramm zu finden, welches Du auch genutzt hast. Jedoch habe ich das gleiche Problem, eine gewisse Zeit klappt der Empfang ganz gut, dann kommen reichlich fehlerhafte Anzeigen, bis es dann plötzlich wieder sinnvolle Werte auswirft.
Mit meinem Anfänger-Wissen komme ich da nicht weiter.

Bin natürlich auch für jeden Tipp dankbar, der nicht von Markus kommt! :wink:


Jürgen
mgolbs
User
Beiträge: 28
Registriert: Freitag 3. Februar 2012, 13:07

Hallo Jürgen,

ist nun schon einige Wochen her... :? Code läuft seit dem 24h am Tag, 7 Tage die Woche ohne jegliche Probleme.

Versuche mal was in Richtung:

Code: Alles auswählen

def dissect_can_frame(frame):
    can_id, can_dlc, data = struct.unpack(can_frame_fmt, frame)
    return (can_id, can_dlc, data[0],data[1],data[2],data[3],data[4],data[5],data[6],data[7])


Gruß Markus
heidi
User
Beiträge: 5
Registriert: Dienstag 5. August 2014, 08:25

Markus, Danke für deine Antwort! Leider funktioniert Dein Tipp so nicht direkt.

Ich habe das Problem mal noch etwas beobachtet. Es ist nur ein Anzeige-Problem. Das automatische Echo im Beispiel-Programm sendet die korrekten Werte zurück.
Wenn eines der Datenbytes in den Nutzdaten einen darstellbaren ASCII-Code enthält, wird dieser anstatt dem numerischen Inhalt des Datenbytes angezeigt. Also wenn zum Beispiel im niederwertigsten Byte 0x21 steht, wird in der Terminal-Ausgabe ein Ausrufungszeichen dargestellt, anstatt der 0x21. Das bringt dann wohl die gesamte Ausgabeformatierung durcheinander.
Hatte einfach mal fortlaufende Zahlen gesendet und bekam so einen großen teil das ASCII-Zeichensatzes zu Gesicht.


Also anstatt richtig anzuzeigen:
Received: can_id=1, can_dlc=4, data=b'\x21\xAA\xFF\x00'
wird angezeigt:
Received: can_id=1, can_dlc=4, data=b' !\xAA\xFF\x00'
Die Anzeige wird generiert durch:
print('Received: can_id=%x, can_dlc=%x, data=%s' % dissect_can_frame(cf))
Der anzuzeigende Wert kommt von hier:
def dissect_can_frame(frame):
    can_id, can_dlc, data = struct.unpack(can_frame_fmt, frame)
    return (can_id, can_dlc, data[:can_dlc])
Hätte dazu vielleicht jemand eine Tipp? Ich sehe den Fehler einfach nicht.


Jürgen
BlackJack

@heidi: Naja das ist im Grunde kein Fehler sondern eine falsche Erwartungshaltung Deinerseits. Wenn Du die Anzeige vom Datentyp `bytes` anders haben willst, dann musst Du halt dafür sorgen das es so dargestellt wird wie Du möchtest. Das `binascii`-Modul könnte vielleicht hilfreich sein.
heidi
User
Beiträge: 5
Registriert: Dienstag 5. August 2014, 08:25

Danke, für den Hinweis auf binascii. Schaue ich mir gleich mal an.

Nur nochmal kurz zur Erwähnung, da ich es glaube nicht ganz deutlich erklärt habe: Nur die Werte die im ASCII-Zeichensatz vorkommen, werden als ihr ASCII-Symbol dargestellt, also von 0x09 bis 0x7D. Ab 0x7F werden die Werte wieder als hex-Wert dargestellt.

Es scheint mir jedoch nur ein herumbasteln an den Symptomen zu sein, wenn ich selektiv einige Werte mit binascii wieder "in Form" bringen soll.

Für mich als Anfänger sind solche Probleme natürlich erstmal ärgerlich, aber letztlich auch sehr nützlich. Schließlich lernt man ja am Besten aus seinen Fehlern.
Allerdings hatte ich gehofft, mit einem Beispiel-Programm aus der offiziellen Python-Dokumentation ( https://docs.python.org/3/library/socket.html#example ) erstmal einen problemlosen Einstieg zu haben. Es wird auf etlichen anderen Webseiten erwähnt oder in Projekten benutzt. Dort scheint es keine Probleme zu geben. Das lässt mich daran glauben, daß der Fehler irgendwo bei mir liegen muss.


Jürgen
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@heidi: wie Blackjack schon geschrieben hat, handelt es sich hierbei nicht um einen Fehler im eigentlichen Sinn. Die Frage ist, was will jemand auf dem Bildschirm sehen, wenn er einen Bytestring ausgibt. Da gibt es zwei Möglichkeiten: die einen wollen alle druckbaren Zeichen als Zeichen sehen, weil dann Text, der sich im Bytestring versteckt, für Menschen schneller lesbar ist, die anderen wollen alle Zeichen als Hex-Repräsentation haben, dafür ist binascii da. Bei den meisten Anwendungsfällen gehöre ich zur ersten Kategorie, für alles andere nehme ich `binascii.hexlify`. Ich würde mir auch wünschen, dass Bytestrings formatierbar sind. 'x' würde sich ja für den Fall anbieten.
heidi
User
Beiträge: 5
Registriert: Dienstag 5. August 2014, 08:25

Dank Sirius3 habe ich verstanden, was mir auch BlackJack schon klarmachen wollte.

Danke, an alle Beteiligten!

Ich habe das Ganze wie folgt abgeändert, damit es für mich funktioniert:

Code: Alles auswählen

import socket
import struct
import sys 
import binascii
 
 
# CAN frame packing/unpacking (see 'struct can_frame' in <linux/can.h>)
 
can_frame_fmt = "=IB3x8s"
can_frame_size = struct.calcsize(can_frame_fmt)
 
def build_can_frame(can_id, data):
    can_dlc = len(data)
    data = data.ljust(8, b'\x00')
    return struct.pack(can_frame_fmt, can_id, can_dlc, data)
 
def dissect_can_frame(frame):
    can_id, can_dlc, data = struct.unpack(can_frame_fmt, frame)
    return (can_id, can_dlc, data[:can_dlc])
 
 
# create a raw socket and bind it to the 'vcan0' interface
s = socket.socket(socket.AF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
s.bind((sys.argv[1],))
 
while True:
    cf, addr = s.recvfrom(can_frame_size)

    can_id, can_dlc, data = dissect_can_frame(cf)

    hexdata = str(binascii.hexlify(data), 'ascii')

    #für meine Anwendung muss ich die empfangenen Bytes noch umsortieren - das höchstwertige Byte bleibt dabei unbeachtet
    hexdata_swapped = hexdata[4] + hexdata[5] + hexdata[2] + hexdata[3] + hexdata[0] + hexdata[1]

    #die hex-Daten nach int umwandeln
    d = int(hexdata_swapped, 16)

    #die hex-Daten für die Anzeige formatieren
    formatted_hexdata = ' '.join(hexdata[i:i+2] for i in range(0, len(hexdata), 2))
 
    print('Received: can_id=',can_id,' can_dlc=',can_dlc,', data=', formatted_hexdata,'=', d)
 
  
Beim Aufruf kann jetzt die Can-Schnittstelle übergeben werden.
python canexample.py can0
Die Ausgabe sieht jetzt so aus:
Received: can_id= 84 , can_dlc= 4 , data= 5d 60 03 00 = 221277
Weitere Tipps sind natürlich gern gesehen.


Jürgen
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Konstanten werden normalerweise durchgehend groß geschrieben, um sie besser zu erkennen, also CAN_FRAME_FMT, bzw. CAN_FRAME_SIZE. `struct.pack` padded automatisch, Zeile 14 daher überflüssig. Bytes erst in einen Hex-String umzuwandeln, dann magische Dinge damit machen um danach in ein Int zu konvertieren, ist mehr als abenteuerlich: dafür gibt es struct.unpack!
In Deinem Fall:

Code: Alles auswählen

d = struct.unpack("=I", data)
Wenn Du sowieso den Hex-String wieder auseinanderpflückst, kann Du auch gleich über die Bytes iterieren:

Code: Alles auswählen

formatted_hexdata = ' '.join(map('{:02x}'.format, data))
Du solltest keinen Code auf Modulebene haben, und stattdessen alles in Funktionen stecken.
heidi
User
Beiträge: 5
Registriert: Dienstag 5. August 2014, 08:25

Sirius3 hat geschrieben:Konstanten werden normalerweise durchgehend groß geschrieben, um sie besser zu erkennen, also CAN_FRAME_FMT, bzw. CAN_FRAME_SIZE.
Du solltest keinen Code auf Modulebene haben, und stattdessen alles in Funktionen stecken.
Das werde ich beherzigen.
Sirius3 hat geschrieben:Wenn Du sowieso den Hex-String wieder auseinanderpflückst, kann Du auch gleich über die Bytes iterieren:

Code: Alles auswählen

formatted_hexdata = ' '.join(map('{:02x}'.format, data))
.
Funktioniert sehr gut! Aber ehrlich gesagt verstehe ich diese Zeile noch nicht. Da muss ich mir die Einzelheiten noch anlesen, gerade was 'map' und '{:02x}' angeht. Hatte ich schon erwähnt, daß ich Python-Anfänger bin? :wink:

Die Zeile

Code: Alles auswählen

d = struct.unpack('<L', data[:3]) 
hatte ich auch schonmal in meinem Programm. Das funktioniert aber natürlich nicht. JETZT weiß ich das auch und auch warum.
Der Grund: nur die drei niederwertigen Bytes sollen zu int gewandelt werden, das höchstwertige Byte soll anderen Zwecken dienen. Das habe ich halt nicht anders hinbekommen als mit meinem "magischem" Code. Gibt es da einen besseren/sinnvolleren Weg?

edit: Was mir gerade einfällt, ab und zu kann auch mal ein Frame mit 5 Daten-Bytes ankommen. Dann käme ja das struct.unpack('<L', data) auch durcheinander. Also doch wieder die Bytes manipulieren...?

Jürgen

Wird das hier eigentlich zu off-topic? Sollte ich dazu einen eigenen Thread aufmachen?
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Na wenn Du nur 24-bit Zahlen hast, dann häng doch die null einfach an den Bytestring an:

Code: Alles auswählen

d = struct.unpack('<I', data[:3] + '\0')
Antworten