16 Bytes lange Modbus Befehle mit RevPiModIO?

Moderator: RevPiModIO

Post Reply
digohm
Posts: 35
Joined: 10 Aug 2017, 23:00

16 Bytes lange Modbus Befehle mit RevPiModIO?

Post by digohm »

Hallo,

erstmal vielen Dank für dein Projekt! Ich würde gerne auch die Modbus Kommunikation über RevPiModIO laufen lassen. Meine Befehle sind jedoch 16 Bytes lang. Insofern belegt mein Befehl mehr als nur eine Variable.
Erkennt RevPiModIO das automatisch oder wird mein Befehl abgeschnitten?

Folgenden Code hätte ich mir dazu überlegt:

Code: Select all

import revpimodio2

class MyDriver():
    def __init__(self):
        self.rpi = revpimodio2.RevPiModIO(autorefresh=True)
        self.rpi.handlesignalend() 
        self.modbus = revpimodio2.RevPiModIO(64)
                
        self.modbus.io.Output_1.replace_io("command", "X")   # "X" für Hexadezimal
        

    def cyclefunction(self, cycletools):       
        test = struct.pack('<HHHHHHHH', 7, 0, 1500, 500, 3000, 2000, 0, 0)

        self.modbus.io.command.value = test
                
    def start(self):
        self.rpi.cycleloop(self.cyclefunction, cycletime=1000) 

if __name__ == "__main__": 
    driver = MyDriver()
    driver.start()
User avatar
volker
Posts: 1046
Joined: 09 Nov 2016, 15:41

Re: 16 Bytes lange Modbus Befehle mit RevPiModIO?

Post by volker »

Hallo,
welchen Vorteil versprichst Du Dir gegenüber dem hier
viewtopic.php?f=13&t=86&p=3030#p3030
diskutierten Direktzugriff? RevPiModIO ist doch auch nur ein wrapper und benutzt genau diese Zugriffsmethoden, die in diesem thread beschrieben sind. Pack den Code aus dem thread in eine Klasse und Du hast im Prinzip was Du suchst und hier beschreibst. Die Bibliothek bringt Dir den Vorteil die IO Aufrufe zu koordinieren. Wenn Du viele Stellen im Programm hast, in denen Io-Zugriffe erledigt werden müssen, dann ist der Overhead bei lauter Einzelzugriffen signifikant höher als bei Verwendung der Bibliothek und dann hast Du einen klaren Vorteil in der Performance. Aber bei einem Einzelaufruf belibt nur eine striktere Kapselung als Vorteil, welche Du aber durch Bau einer Klasse ebenso bekommst.
Unser RevPi Motto: Don't just claim it - make it!
digohm
Posts: 35
Joined: 10 Aug 2017, 23:00

Re: 16 Bytes lange Modbus Befehle mit RevPiModIO?

Post by digohm »

Einen konkreten Vorteil habe ich mir auch nicht erhofft. Das war rein die Neugier als Techniker :)
digohm
Posts: 35
Joined: 10 Aug 2017, 23:00

Re: 16 Bytes lange Modbus Befehle mit RevPiModIO?

Post by digohm »

Eine Frage hätte ich jetzt doch noch zu Modbus RTU und RevPiModIO und zwar hängt das irgendwie zusammen?
Meine drei Motoren funktionieren nämlich nur sehr "unregelmäßig". Auf die DIO greife ich via RevPiModIO zu, die Modbus Befehle für die Motoren schreibe ich direkt ins Prozessabbild. Die Zykluszeit beträgt zur Zeit 50 ms. Jetzt wäre interessant zu wissen welches Action Intervall man in Pictory am Besten einstellt und ob in RevPiModIO Autorefresh für das Modbus Modul deaktiviert sein muss?
Es kommt nämlich regelmäßig vor, dass der Befehl nicht ins Prozessabbild geschrieben wird, oder der Befehl steht im Prozessabbild und wird aber nicht zum Motor geschickt. Ersteres sehe ich via piTest, letzteres sehe ich in der Inbetriebnahmesoftware.
Dann kommt es auch vor, dass z.B. Motor 1 funktioniert und die anderen beiden nicht, obwohl der Code exakt gleich ist und ich nur die Variable für das Prozessabbild ändere. Alle drei Motoren signalisieren jedoch via LED, dass der Bus gefunden wurde.

Die Situation hat sich gebessert seit ich das Prozessabbild beim Beenden des Python Programms zurücksetze. Kann mich damit aber auch täuschen. Jedenfalls mache ich irgendetwas noch falsch :?
User avatar
volker
Posts: 1046
Joined: 09 Nov 2016, 15:41

Re: 16 Bytes lange Modbus Befehle mit RevPiModIO?

Post by volker »

Der Modbus Master ist für solch schnelle Intervalle nicht ausgelegt. Insbesondere wenn das System auch noch mit zusätzlichen Tasks gut zu tun hat, dann empfehlen wir dringend (siehe auch Tutorials) die Zykluszeiten vom Master nicht unter 100 ms zu wählen. Ich könnte mir vorstellen, dass das System deshalb "bockig" ist. Versuch doch bitte einfach mal 200 ms Action Intervall und untersuche ob dann alle Befehle durchkommen.
piControl Zyklus und Modbus laufen übrigens nicht synchron. Es ist daher nicht möglich, dass Du Werte definitiv nur einmalig per Modbus überträgst.
Gerade wenn Du viele PiControl-Zugriffe durchführst, dann wirken diese für die Modbus Task blockierend, weil PiControl-Zugriffe jeweils atomar ausgeführt werden und die höchste Prio bekommen. RevPiModIO geht da nicht gerade schonend mit den CPU Ressourcen um, wenn Du die Bibliothek ausreizt. Vielleicht kann Sven da noch Vorschläge machen, wie Du am effektivsten RevPiModIO nutzt, ohne unnötig zu blockieren.
Unser RevPi Motto: Don't just claim it - make it!
digohm
Posts: 35
Joined: 10 Aug 2017, 23:00

Re: 16 Bytes lange Modbus Befehle mit RevPiModIO?

Post by digohm »

Danke für deine Antwort :) Ich hatte da bisher kein Gefühl was das richtige Action Intervall betrifft. Deshalb habe ich jetzt das Action Intervall auf 400 ms erhöht. Resultat war, dass der letzte Motor in der Busreihenfolge funktionierte sobald ich nach jedem Befehl lauter Nullen geschickt habe. Auf diese Art verfuhr der Motor auch mehrmals hintereinander ohne Probleme.
Dann habe ich den Code auf Motor 1 bzw. Motor 2 geändert (verändert hat sich nur die Registeradresse von Output_Word_17 auf Output_Word_1 bzw. auf Output_Word 9. Mit dem Resultat das sich nichts bewegt. Die Befehle landen jedoch wie gewünscht im Prozessabbild. Ich sende 8 Byte, somit sollte es auch zu keinen Überschneidungen kommen (In der Überschrift ist das nämlich falsch).
Als nächstes habe ich das ganze System heruntergefahren und ausgeschaltet. Wieder eingeschaltet, Programm neu gestartet, aber jetzt blinkt bei Motor 1 und Motor 2 das Buslämpchen --> Bus nicht gefunden
Motor 3 findet den Bus aber und ist noch dazu ganz am Ende angeschlossen.
Die Slaveadressen, Baudrate, etc. passen.
Bietet der RevPi Connect bezüglich Modbus einen Vorteil? Das Gerät liegt nämlich bereits auf meinem Schreibtisch und wartet noch sehnsüchtig auf eine IP Adresse.
Ich würde zu gern wissen wo das Problem liegt, da für mich unverständlich ist was ich falsch mache :?
User avatar
volker
Posts: 1046
Joined: 09 Nov 2016, 15:41

Re: 16 Bytes lange Modbus Befehle mit RevPiModIO?

Post by volker »

Dann lass uns mal von vorne anfangen.
Was genau willst Du zu einem Motor senden? Willst Du 4 hintereinander liegende Modbusregister beschreiben? Welche Action hast Du dafür definiert?
Wenn Du das jeweils in 3 Motoren schreibst, warum änderst Du irgendwelche Bytes? Würden das nicht vielmehr 3 Actions sein, die Du auf unterschiedliche Modbusadressen ausführen lassen willst?
Welche Quelle hast Du für die Actions angelegt? Kannst Du da mal einen Screenshot von den Actions und von dem zugehörigen Prozessvariablen machen? Kanst Du mal einen Export der Offsetliste auf den Screen machen und davon auch einen Screenshot?
Wenn Du dann also einen PA Bereich hast, in dem hintereinander 3 x 8 Bytes liegen, die Du versenden willst, wie schreibst Du diese Bytes ins PA? Nimmst Du dafür PiTest oder machst Du das in Python bzw. C? Machst Du das mit einem atomaren Befehl (also alle 8 Byte zusammen mit einem write) ?
Geht es um die Motoren, von denen ich schon mal die Doku von Dir bekommen habe? Ist sicher, dass Du dann die korrekte Abfolge sendest (erst Motor initialisieren, dann erst Position senden)?
Bitte beschreibe einfach mal sehr detailliert, was Du machst. Vielleicht finden wir dann zusammen das Problem und können es beseitigen.
Wichtig ist, dass Du Dir ganz sicher bist, dass die Bytes und Sequenzen so auch stimmen und die Motoren sich wirklich genau damit ansteuern lassen. Das solltest Du unbedingt vorher mit einem Tool (Qmodbus oder so) durchspielen. Wichtig ist auch, dass Du ganz generell die korrekten Registernummern und Modbusadressen ansprichst. Die Baudrate und die Anzahl der Bytes müssen natürlich auf das intervall abgestimmt sein. Dui hast es mit einem seriellen Bus zu tun, der nun mal nicht parallel Daten übertragen kann, sondern nur hintereinander. Also 9.600 bps z.B. bedeutet, dass Du pro Bit ca. 100 µs brauchst. Das sind bei 1 Byte dann mit dem Frame ca. 1 ms /byte. Bei 4 Modbusregistern über eine Modbus Action hast Du da schon mal locker 10 ms pro Action beisammen. Dann muss aber ja auch der Slave erst mal antworten, was er in der Regel nicht unmittelbar tut, sondern erst nach einer gewissen Wartezeit. Bei 3 Actions und 9.600 Bps müsstest Du also bei vieleicht 60 ms und mehr liegen. Also Du siehst, Du musst erst mal nachrechnen, welche reine Datenübertragungszeiten Du mindestens brauchst. Dann multipliziere das am besten mit 1,5 um die Intervallzeit zu bestimmen. Ist halt kein Ethernet TCP/IP...
Unser RevPi Motto: Don't just claim it - make it!
digohm
Posts: 35
Joined: 10 Aug 2017, 23:00

Re: 16 Bytes lange Modbus Befehle mit RevPiModIO?

Post by digohm »

Hallo,

ich habe mich intensiv weiter mit der Fehlersuche beschäftigt. Die Baudrate habe ich in Pictory und bei den Motoren auf 57600 erhöht. Der Bus wird seither jedes mal gefunden.
Zusätzlich habe ich ein kleines und simples Testprogramm geschrieben um die Fehlerwahrscheinlichkeit möglichst gering zu halten. Es ist jetzt so, dass die Motoren in einer Endlosschleife verfahren, jedoch wird zwischendurch immer wieder ein Befehl "verschluckt", was im Produktiveinsatz fatal wäre. In diesem Fall kommt nämlich kein Befehl beim Motor an. Woran das liegt ist mir unklar, da es sich immer um die selbe Abfolge an Befehlen handelt.

Meine Pictory Konfiguration:
Pictory.JPG
Pictory.JPG (59.23 KiB) Viewed 12001 times
Mein Code:
In diesem Beispiel verfährt absichtlich nur Motor 1 und Motor 3

Code: Select all

import revpimodio2
import struct
import time

motor1= [10, 317, "m1"] # Steigung in mm, Pictory Adresse, Name
motor2= [5, 333, "m2"]
motor3= [90, 349, "m3"]

relPositioning = 4 # Kommando 4 relative Positionierung


f=open("/dev/piControl0","wb+",0)


def ack(motor):
    f.seek(motor[1])
    ack = struct.pack('HHHHHHHH', 22,0,0,0,0,0,0,0) 
    f.write(ack)
    print("Sende Quittierung" + str(ack))
    time.sleep(2)

def calcLsbMsb(incr):
    lsb = (incr &0xFFFF)
    msb = ((incr >> 16) & 0xFFFF)
    print("lsb: " + str(lsb))
    print("msb: " + str(msb))
    value = msb * 65536 + lsb
    print(value)
    return(msb, lsb)

def relativePositioning(motor, distance, posSpeed, accelTime, decelTime):
 
    distIncr = int((distance / motor[0]) * 4096) # 4096 increments --> 10 mm
    print("Inkremente: " + str(distIncr))

    (msb, lsb) = calcLsbMsb(distIncr)

    command = struct.pack('<HHHHHH', 4, msb, lsb, posSpeed, accelTime, decelTime)
    f.seek(motor[1])
    print("relative Positionierung " + motor[2]  +": " + str(command))
    f.write(command)

def sendZero(motor):
    command = struct.pack('HHHHHHHH', 0,0,0,0,0,0,0,0)
    f.seek(motor[1])
    print("send Zero: " + str(command))
    f.write(command)

deviceList = [0, 30, 31] #30 = AIO, 31 = DIO
rpi = revpimodio2.RevPiModIOSelected(deviceList, autorefresh=True)

rpi.io.O_7.value = 1 # M1 Freigabe
rpi.io.O_8.value = 1 # M2 Freigabe
rpi.io.O_9.value = 1 # M3 Freigabe

ack(M1)
ack(M2)
ack(M3)
time.sleep(2)

while True:

    sendZero(M1)
    for x in range(0, 20):
        time.sleep(0.1)
    relativePositioning(M1, 1, 50, 500, 500)  
    for x in range(0, 40):
        time.sleep(0.1)
    sendZero(M3)
    for x in range(0, 20):
        time.sleep(0.1)
    relativePositioning(M3, 100, 50, 500, 500) 
    for x in range(0, 40):
        time.sleep(0.1)
    
    print("an")
    rpi.io.O_14.value = True
    rpi.io.O_1.value = True
    for x in range(0, 100):
        time.sleep(0.1)
    rpi.io.O_14.value = False
    rpi.io.O_1.value = False
    print("aus")

    sendZero(M1)
    for x in range(0, 20):
        time.sleep(0.1)
    relativePositioning(M1, 1, 50, 500, 500)  
    for x in range(0, 40):
        time.sleep(0.1)
    sendZero(M3)
    for x in range(0, 20):
        time.sleep(0.1)
    relativePositioning(M3, -100, 50, 500, 500) 
    for x in range(0, 40):
        time.sleep(0.1)

    print("an")
    rpi.io.O_14.value = True
    rpi.io.O_1.value = True
    time.sleep(10)
    rpi.io.O_14.value = False
    rpi.io.O_1.value = False
    print("aus")
Die Motoren sind die von damals:
schreiben.JPG
schreiben.JPG (97.59 KiB) Viewed 12001 times
relativ.JPG
relativ.JPG (64.83 KiB) Viewed 12001 times
Post Reply