Koomunikation mit einem Sick Geber über RS485, Hiperface

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
kiaralle
User
Beiträge: 132
Registriert: Donnerstag 19. August 2021, 19:11

Hallo,
ich habe hier einen Sick-Geber SRM50 vor mir liegen.
Angeschlossen habe ich ihn über einen Wandler USB-RS485 von Waveshare.
Betriebssystem ist Linux. Der Wandler läuft an einem anderen Projekt tadellos

Betriebsaleitung des Gebers https://cdn.sickcn.com/media/docs/6/06/ ... 49006.pdf

Erfahrungen habe ich bis jetzt nur mit Modbus am anderen Projekt sammeln können.

Wie schaut es aber bei solch einen Geber aus.
Er wird anders angesprochen, hat eine Adresse 40h, mit 42h sollte ich die Positionen lesen können.

Mein Bastel-Code:

Code: Alles auswählen

# -*- coding: utf-8 -*-
"""
Spyder Editor

This is a temporary script file.
"""

import serial
import serial.rs485
import sys
import time
import binascii


ser = serial.Serial(
   port='/dev/ttyUSB0',
   baudrate = 9600,
   bytesize = serial.EIGHTBITS, # number of bits per bytes
   parity = serial.PARITY_EVEN, # set parity check: no parity
   stopbits = serial.STOPBITS_ONE, #number of stop bits
   timeout = .2,          #block read
   xonxoff = False,      #disable software flow control
   rtscts = False,      #disable hardware (RTS/CTS) flow control
   dsrdtr = False,        #disable hardware (DSR/DTR) flow control
   writeTimeout = .2     #timeout for write
   
)
time.sleep(0.010)



try:
    ser.is_open
    print(ser.name)
    ser.flushInput() #flush input buffer, discarding all its contents
    ser.flushOutput()#flush output buffer, aborting current output  
except Exception as e:
    print(f"Error: {e}")
    sys.exit(1)
    
i = 1 
data = [40]
# ser.write(bytes)

while i < 10:
    ser.write(data)
    ser.flush
    time.sleep(0.1)
    response = ser.readline()
    hex_string = binascii.hexlify(response).decode('utf-8')
    print(hex_string)
    i = i + 1
Ich erhalte:
/dev/ttyUSB0
00
00
00
00
00
00
00
00
00
Ist meine Vorgehensweise annähernd ok? Wie rufe ich mehrere Registerwerte auf.
So richtig verstehen tu ich es noch nicht.

Gruß Ralf
Benutzeravatar
grubenfox
User
Beiträge: 612
Registriert: Freitag 2. Dezember 2022, 15:49

Erstmal die Doku vom Hiperface-Protokoll lesen.
Diese Anleitung hier
kiaralle hat geschrieben: Sonntag 2. Juni 2024, 14:41 Betriebsaleitung des Gebers https://cdn.sickcn.com/media/docs/6/06/ ... 49006.pdf
ist ja nur beim einbauen und elektrischen anschließen des Geber hilfreich. Möglicherweise ist dies https://www.sick.com/media/docs/3/43/44 ... 059443.PDF der Link zur richtigen Doku vom Protokoll. Aber das ist nun völlig ungetestet und nur weil in der obigen Betriebsanleitung was von "HIPERFACE® - Beschreibung, Artikelnr. 8010701" steht.
kiaralle
User
Beiträge: 132
Registriert: Donnerstag 19. August 2021, 19:11

Meine Doku sollte passen. In ihr stehen ja weiter unten die Register. 😉

Ich betreibe, montiere solche Geber an einem Frequenzumrichter eines anderen Herstellers.
Bei der Motorüberholung von Servomotoren muss ich diese Geber auch mal tauschen.
Für eine Schnell-Prüfung wollte ich mir einen Tester in Python schreiben.

Ich habe ein Diagnosegerät von Sick, ist mir aber zu aufwendig 😁
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

@kiaralle: zu Deinem Programm. Variablennamen sollten keine kryptischen Abkürzungen sein, `ser` ist wohl eher `serial_port`.
is_open zu referenzieren ist unsinnig, weil das nichts macht, kann also weg. flushInput und flushOutput sind veraltet, und durch reset_input_buffer bzw. reset_output_buffer ersetzt.
Eine sinnvolle Fehlermeldung durch eine Nichtssagende Meldung zu ersetzen ist quatsch, der das try-except kann also auch weg.
Wenn man eine while-Schleife hat, in der ein Index hochgezählt wird, dann hat man eigentlich eine for-Schleife.
flush sollte man auch aufrufen!
Das ganze sollte also eher so aussehen:

Code: Alles auswählen

import serial
import time
import binascii

def main():
    with serial.Serial(
       port='/dev/ttyUSB0',
       baudrate=9600,
       bytesize=serial.EIGHTBITS, # number of bits per bytes
       parity=serial.PARITY_EVEN, # set parity check: no parity
       stopbits=serial.STOPBITS_ONE, #number of stop bits
       timeout=.2,          #block read
       xonxoff=False,      #disable software flow control
       rtscts=False,      #disable hardware (RTS/CTS) flow control
       dsrdtr=False,        #disable hardware (DSR/DTR) flow control
       writeTimeout=.2     #timeout for write  
    ) as serial_port:
        print(serial_port.name)
        serial_port.reset_input_buffer()
        serial_port.reset_output_buffer()

        data = b"\x28"
        for _ in range(10):
            serial_port.write(data)
            serial_port.flush()
            time.sleep(0.1)
            response = serial_port.readline()
            hex_string = binascii.hexlify(response).decode('utf-8')
            print(hex_string)

if __name__ == "__main__":
    main()
Laut Dokumentation gibt es Commandbytes 42h bis 57h, aber das von Dir verwendete 28h gibt es nicht. Was willst Du denn abfragen?
kiaralle
User
Beiträge: 132
Registriert: Donnerstag 19. August 2021, 19:11

Danke Sirius3,

ich habe einen Code aus dem Internet, als Vorlage, Hilfe, der auch schon drei Jahre alt sein sollte.
Da wird sich einiges geändert haben.
Ein paar Fehler hatte ich schon entdeckt , der Code läuft aber überhaupt nicht.
Deshalb werkel ich an meinem Code, um zu lernen, was wie und wo passiert.

Code: Alles auswählen

from crccheck.checksum import ChecksumXor8
import serial, time, binascii, sys

# Basic Script to read position from Hiperface encoders using RS485
# Provides a basic Function to read positions and scrambled code to send arbitrary commands.
# Sorry, I'm a python noob and haven't botherd with clases...

#initialization and open the port
ser = serial.Serial()
# Choose based on your system/ linux will have sth. like /dev/ttty...
ser.port = "/dev/ttyUSB0"
ser.baudrate = 9600
ser.bytesize = serial.EIGHTBITS #number of bits per bytes
ser.parity = serial.PARITY_EVEN #set parity check: no parity
ser.stopbits = serial.STOPBITS_ONE #number of stop bits
ser.timeout = 1          #block read
ser.xonxoff = False     #disable software flow control
ser.rtscts = False     #disable hardware (RTS/CTS) flow control
ser.dsrdtr = False       #disable hardware (DSR/DTR) flow control
ser.writeTimeout = 0.2     #timeout for write

def readPosition(numberReps, updateRate=0.2):
    data = bytearray.fromhex('66')
    data.append(ChecksumXor8.calc(data))
    print ('Attempting to make ', numberReps, ' read attemps with intervall of ', updateRate, ' seconds')
    try: 
        ser.open()
    except Exception as e:
        print ("error open serial port: " + str(e))
        sys.exit()
    if ser.isOpen():
        try:
            ser.flushInput() #flush input buffer, discarding all its contents
            ser.flushOutput()#flush output buffer, aborting current output 
            # ser.write(data)
            # time.sleep(0.010)  #give the serial port sometime to receive the data
            numOfLines = 1
            while True:
                ser.write(data)
                time.sleep(0.01)
                response = ser.read(7)
                print('>> Cycle #' + str(numOfLines))
                print('Response raw:', binascii.hexlify(response))
                position=int.from_bytes(response[2:6],'big',signed=False)
                print ('Absolute Position 32bit unsigned:', position)
                print('>> End cycle')
                numOfLines = numOfLines + 1
                time.sleep(updateRate)
                if (numOfLines >= numberReps+1):
                    break
            ser.close()
            print ('>> Finished reading')
        except Exception as e1:
            print ("error communicating...: " + str(e1))
    else:
        print ("cannot open serial port ")

# Read position

readPosition(10, 0.2)

#
#   Manual Mode
#

# Enter adress of encoder: 40 is default, FF is broadcast - current encoder: 50
adr = bytearray.fromhex("FF")
# Enter command as per list of hiperface commands (52 is status, 42 read position) 
# See hiperface spec for details
# #  https://cdn.sick.com/media/docs/5/65/865/operating_instructions_specification_hiperface%C2%AE_motor_feedback_protocol_en_im0064865.pdf
cmd = bytearray.fromhex("52")
# Enter masterdata if required (code, register requests), otherwise leave empty
masterData = bytearray.fromhex("")
data = adr + cmd + masterData
data.append(ChecksumXor8.calc(data))
# Enter the number of payload bytes you expect
# value is increased by 3 to account for adress, command ACK and checksum
readNumberOfBytes = 4
readNumberOfBytes += 3
# Sleep time before response is read
sleepTime=0.01

try: 
    ser.open()
except Exception as e:
    print ("error open serial port: " + str(e))
    sys.exit()
if ser.isOpen():
    try:
        ser.flushInput() #flush input buffer, discarding all its contents
        ser.flushOutput()#flush output buffer, aborting current output 
        numOfLines = 0
        while True:
            print('>> Start cycle')
            ser.write(data)
            time.sleep(sleepTime)
            response = ser.read(readNumberOfBytes)
            print(binascii.hexlify(response))
            # Note that some slave answers include INT values of varying bit length within response. Configure the required bits, big/little endian and signed to your liking
            # position=int.from_bytes(response[2:6],'big',signed=False)
            # print (position)
            print('>> End cycle')
            numOfLines = numOfLines + 1
            # Timer before new cycle is started
            time.sleep(0.5)
            # Integer value to limit number of iterations
            if (numOfLines >= 5):
                break
        ser.close()
    except Exception as e1:
        print ("error communicating...: " + str(e1))
else:
    print ("cannot open serial port ")




kiaralle
User
Beiträge: 132
Registriert: Donnerstag 19. August 2021, 19:11

Mit dem 00 bei meinem Code, deinem, sollte aber in die richtige Richtung gehen. Oder?
Es sollten ja zwei Werte zurück kommen. Value und Cecksum.
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

Warum versuchst Du nicht erst meinen Code zu verstehen?
Bei Deinem neuen Code gilt ja im wesentliche das selbe, was ich schon zum alten geschrieben habe.
Was für Fehler treten denn auf?
kiaralle
User
Beiträge: 132
Registriert: Donnerstag 19. August 2021, 19:11

Ich glaube, ich verstehe deinen Code soweit.
Was sendest du aber in dieser Zeile?

Code: Alles auswählen

data = b"\x28" 
Es kommt 00 als Ergebnis.
Wenn ich die Datenleitungen tausche, A+ und B-, erhalte ich dies.
40d00a
9a40d00a
9a40d00a
9a40d00a
9a40d00a
9a40d00a
9a40d00a
9a40d00a
9a40d00a
9a40d00a


So wie ich es verstehe, muss doch so etwas gesendet werden: Geräteadresse + Register

Hab noch etwas Doku gefunden.
https://cdn.sick.com/media/docs/5/65/8 ... 64865.pdf
Benutzeravatar
sparrow
User
Beiträge: 4538
Registriert: Freitag 17. April 2009, 10:28

@kiaralle: \x28 ist 40 in Hexadezimalschreibweise. Also das, was auch du schickst. Da Sirius3 ja darauf hingewiesen hat, dass es diesen Wert in der Dokumentation gar nicht gibt, ist das der Hinweis, dass dein Wer nicht passt und ich würde tippen, dass in der Dokumentation Hexadezimalwerte stehen.
kiaralle
User
Beiträge: 132
Registriert: Donnerstag 19. August 2021, 19:11

Dann bin ich jetzt total daneben.

Adresse steht da mit 40h
Position lesen mit 42h

Das sind doch die Hexadezimal Schreibweise. Oder?
Benutzeravatar
sparrow
User
Beiträge: 4538
Registriert: Freitag 17. April 2009, 10:28

Wenn das so ist, warum schickst du dann 28h?
Das hat ja Sirius3 auch schon gefragt. Die Byte-Repräsentation wäre b"\x40", wenn du 40h senden möchtest.
Du weißt was der Unterschied zwischen Hexadezimal und Dezimal ist?
kiaralle
User
Beiträge: 132
Registriert: Donnerstag 19. August 2021, 19:11

Ja, richtig.
In meinem Bastelcode war das so.
War ein Test.

Ich sende eigentlich aktuell \x40\42.
Bekomme aber immer 00 als Ergebnis.
Antworten