blog

Prototypenentwicklung mit Modbus

Programmierfachkraft, die Python, JavaScript und HTML-Code auf einem Notebook-Bildschirm codiert
Alex Pohjanpelto
Alex Pohjanpelto
Published: Sep 7, 2021
Industrielle Fertigung und Prozesse
Industrielle Messungen
Life-Science

Sie entwickeln Prototypen oder suchen nach einer kostengünstigen Möglichkeit, Feuchte und Temperatur in Ihrer Anwendung zu messen, ohne die Qualität der Messungen zu beeinträchtigen? Verfügen Sie zudem über Grundkenntnisse in Programmiersprachen wie Python? Wenn ja, hoffe ich, Ihnen zeigen zu können, wie Sie mit nur einer HMP110 Sonde, einem USB-Servicekabel (219690) und einem Computer, auf dem Python 3 installiert ist, problemlos Daten über einen längeren Zeitraum protokollieren können. Ich erkläre Ihnen die verschiedenen Abschnitte des Codes, aber wenn Sie keine Erklärung wünschen, können Sie für den gesamten Code auch zum Ende des Textes gehen. Außerdem möchte ich darauf hinweisen, dass dies nur ein Beispielskript ist, das die Modbus-Kommunikation demonstriert.

Bibliotheken

Bevor wir zum Code kommen, sehen wir uns zunächst die Bibliotheken an, die wir nutzen. Die wichtigste davon ist PyModbus. Wir verwenden sie, um mit der Sonde über Modbus RTU zu kommunizieren – genauer gesagt importieren wir ModbusSerialClient aus pymodbus.client.sync. Wir nutzen die struct-Bibliothek, um Bits in verschiedene Variablentypen zu manipulieren, die time-Bibliothek, um die Abfragerate zu ändern, die datetime-Bibliothek, um die Uhrzeit und das Datum für die Messwerte zu erhalten, und wir importieren argparse, um Argumente aus der Befehlszeile an den Code zu übergeben.

Aus pymodbus.client.sync ModbusSerialClient als ModbusClient importieren
struct importieren
time importieren
datetime importieren
argparse importieren

Parameter

Ich habe einige Argumente hinzugefügt, um die Flexibilität des Codes zu unterstützen. Wir verwenden diese, damit wir die Werte der Variablen einfach aus der Befehlszeile ändern können, ohne den Code verändern zu müssen. Die Argumente, die ich als nützlich betrachte, sind der Kommunikationsanschluss, der Name der Datei, in der die Daten gespeichert sind, die Slave-Adresse der Sonde, die Abfragerate und die Dauer der Datenprotokollierung.

parser = argparse.ArgumentParser(
    description="Modbus data logger"
)
parser.add_argument('port', help="The COM port where the probe is attached")
parser.add_argument('-f', '--file', help="The file name to store the data (default data.csv)", default="data.csv")
parser.add_argument('-a', '--address', help="The address of the probe (default 240)", default=240, type=int)
parser.add_argument('-r', "--rate", help="The poll rate in seconds (default 1)", default=1, type=float)
parser.add_argument('-l', '--length', help="The length of time in hours to data log (default 9999999)", type=float, default=9999999)
args = parser.parse_args()
 

Modbus-Verbindung

Wir müssen zuerst einen neuen seriellen Modbus-Client mit den richtigen Einstellungen für unsere Sonde initialisieren. Der Parametersatz in diesem Beispiel muss die Kommunikationsmethode, den Kommunikationsanschluss, das Antwort-Timeout, die Baud-Rate, die Stoppbit und die Parität umfassen. Die Methode lautet „rtu“ für Modbus RTU. Der Anschluss hängt von Ihrem Computer ab, daher werde ich in einem Abschnitt unten erklären, wie Sie den richtigen finden. Die anderen Parameter werden durch die Einstellungen der Sonde bestimmt – die entsprechenden Werte sind im Datenblatt der Sonde nachzulesen. Typischerweise beträgt die Baud-Rate für Vaisala Sonden 19200, die Stoppbit sind 2 und die Parität ist keine.

probe = ModbusClient(method='rtu', port=args.port, timeout=1, baudrate=19200, stopbits=2, parity='N')

 

Auslesen der Halteregister

Erstellen wir nun eine Funktion zum Auslesen der Halteregister der Sonde. Wir rufen die Methode read_holding_registers() der Modbus-Client-Instanz auf, die wir im vorherigen Abschnitt erstellt haben, um die Register auszulesen. Wir müssen die Startadresse des Halteregisters, die Anzahl der Register und die Slave-Adresse der Sonde angeben. Wir erhalten die Daten aus den Registern in 16-Bit-Wörtern im Little-Endian-Format, die wir dann in 32-Bit-Gleitkommawerte konvertieren müssen.

def holding_registers_data():
    try:        
        registers = probe.read_holding_registers(address=0,count=10, unit=args.address).registers
            except Exception as e:
        print(e)
        return False, None, None, None
    try:
        rh = data_from_register(registers, 1)
        t = data_from_register(registers, 3)  
        dp = data_from_register(registers,9)   
       
    except Exception as e:
        print(e)
        return False, None, None, None
        return True, rh, t, dp
 

Konvertieren der Register in 32-Bit-Werte

Die Register werden als 16-Bit-Ganzzahlen gespeichert, und wir müssen sie in 32-Bit-Floatwerte konvertieren. Ich habe eine Funktion erstellt, die die Registerwerte und den Registerindex übernimmt und einen 32-Bit-Gleitkommawert der Daten an diesem Index zurückgibt. Wir nutzen das struct-Modul, um diese Konvertierung durchzuführen.

def data_from_register(registers, i):
    return struct.unpack('!f', bytes.fromhex('{0:04x}'.format(registers[i]) + '{0:04x}'.format(registers[i-1])))[0]

 

Protokollieren der Daten

Da wir nun die Halteregister auslesen und die Werte in 32-Bit-Gleitkommawerte konvertieren können, müssen wir eine Funktion erstellen, die die Werte in einer CSV-Datei speichert. Dazu habe ich eine Funktion namens data_logger() erstellt. Sie ruft die Funktion holding_registers_data() auf und hängt die empfangenen Daten an eine Datei im Format datetime, relative Feuchte, Temperatur, Taupunkt an.

def data_logger():
    probe.connect()
    successful, rh, t, dp = holding_registers_data()
    if (successful):
        dt = datetime.datetime.now()
        
        try:
            with open(args.file, "a") as f:
                line = f"{dt},{rh},{t},{dp}\n"
                print(line)
                f.write(line)   
        except Exception as e:
            print(e)
        probe.close()
        time.sleep(args.rate)
        
    else:
        probe.close()
        time.sleep(0.5)
 

 

Bestimmen des Kommunikationsanschlusses der Sonde

Stellen Sie zunächst sicher, dass die Sonde richtig am Computer angeschlossen  ist.
 

Windows
Im Windows-Betriebssystem finden Sie den COM-Anschluss des Geräts im Geräte-Manager. Öffnen Sie das „Startmenü“ unten links auf dem Bildschirm, und geben Sie „Geräte-Manager“ ein, um das Geräte-Manager-Fenster zu öffnen. Es sollte als erstes Ergebnis unter „Höchste Übereinstimmung“ angezeigt werden. Öffnen Sie es, indem Sie auf das Symbol klicken oder die Eingabetaste auf der Tastatur drücken. Klicken Sie auf den Pfeil neben „Anschlüsse“ (COM und LPT), um die Anschlüsse zu erweitern. Es sollte ein Gerät als „Vaisala USB-Gerät“ mit dem COM-Anschluss daneben aufgeführt sein, in unserem Fall COM6.

Image
Determining the communication port of the probe

Linux 
Unter Linux können Sie den Kommunikationsanschluss bestimmen, indem Sie im Terminal den Befehl „dmesg | grep tty“ eingeben. Er sollte unter anderem eine Anweisung ähnlich wie „cp210x converter now attached to ttyUSBn“ zurückgeben, wobei ttyUSBn der Anschluss ist.

Ausführen des Codes

Um das Skript ausführen zu können, müssen alle Bibliotheken installiert sein. Bei Bedarf können Sie den pip-Befehl

pip3 install -U pymodbus 
verwenden, um PyModbus zu installieren. Die anderen Bibliotheken sollten bereits im Python 3-Paket enthalten sein.
Navigieren Sie in einer Eingabeaufforderung zu dem Verzeichnis, in dem das Python-Skript gespeichert ist, und geben Sie
    python Modbus_RTU -h 

ein, um Hilfe zu den Argumenten zu erhalten. Dies sollte in der Eingabeaufforderung angezeigt werden. Unten sehen Sie einen Screenshot der Ausgabe.

Image
Terminal

 

 

 

 

 

Der einzige notwendige Parameter ist der Kommunikationsanschluss, während die anderen Argumente einen Standardwert aufweisen, den Sie bei Bedarf ändern können. Das Kommunikationsanschlussargument erfordert keinen Bezeichner und kann an beliebiger Stelle nach dem Dateinamen platziert werden. Die anderen optionalen Argumente erfordern einen Bezeichner.
 Im Folgenden finden Sie ein Beispiel für einen typischen Befehl in Lang- und Kurzform:

    Python .\Modbus_RTU  --file datalog.csv --address 240 --rate 10 --length 48 COM6
    Python .\Modbus_RTU -f datalog.csv -a 240 -r 10 -l 48 COM6 
 

 

Laden Sie den gesamten Code herunter, und sehen Sie sich das Modbus 101-Webinar an.

 

Add new comment