Python Skript für modbus / medibus.x | Dräger Gerät

|Moppel|

Captain
Registriert
Apr. 2010
Beiträge
3.612
Liebe Community,

vorab, ich habe keine Erfahrungen mit Python. Das letzte mal habe ich vor 15 Jahren in der Schule Hangman mit Delphi programmiert. ;)
Ich verstehe jedoch die grundlegende Logik von Programmiersprachen.

Ich möchte folgendes erreichen:

An einem Dräger Primus Anästhesiegerät möchte ich die Werte für Leckage und TVi auslesen.
Der Techniker von Dräger hat mir den COM-Port des Geräts freigeschaltet und mir wurde von einer Dame vom Software Engineering das freie Programm VitalRecorder ans Herz gelegt.
Leider - und das hatte sie mir auch mitgeteilt - werden genau diese beiden Werte vom Programm nicht unterstützt, obwohl das Gerät sie über die Schnittstelle ausgibt.

Ich habe im Forum des Programmerstellers eine Anfrage gestellt. Leider wurde sie bis jetzt nicht beantwortet und es ist fraglich ob das überhaupt umgesetzt werden wird.

Deshalb habe ich mit Hilfe der Google KI nach langem hin, her, Nachfragen und Optimierungen mir ein Python Skript zurecht gebastelt.
Spoiler: Es liest tatsächlich Werte aus und schreibt sie mir in eine .csv Datei. 🥳

Leider sind die Werte nicht korrekt. Aber zuerst mal das Skript:

Python:
import csv
import time
from datetime import datetime
import serial

# Define serial port details (replace with your values)
PORT = '/dev/ttyUSB0'
BAUD_RATE = 9600

# Function to extract 16-bit signed integer from response (assuming little-endian)
def extract_int16(data):
  return data[1] + (data[0] << 8)  # Combine bytes and convert to signed integer

# Open CSV file for writing (replace with your desired filename and path)
with open('modbus_data.csv', 'w', newline='') as csvfile:
  writer = csv.writer(csvfile)
  writer.writerow(['Timestamp', 'Leakage Value', 'VTi Value'])  # CSV header row

  # Initialize serial port
  ser = serial.Serial(PORT, BAUD_RATE)

  while True:
    # ... (your request construction and sending logic)

    # Read response (assuming 8 bytes: header + 2 bytes data)
    response = ser.read(8)

    # Extract leakage and VTi data (assuming 16-bit signed integers)
    leakage_value = extract_int16(response[3:5])
    vti_value = extract_int16(response[5:7])

    # Define inspiratory tidal volume register address (decimal)
    vti_register_address = int('04', 16)  # Convert hexadecimal string to decimal

    # Define Modbus function codes (optional, but improves readability)
    READ_HOLDING_REGISTERS = 0x03  # Function code for reading holding registers

    # Build Modbus RTU request frame for VTi register
    vti_request = bytearray([vti_register_address >> 8,  # High byte of register address
                             vti_register_address & 0xFF,  # Low byte of register address
                             READ_HOLDING_REGISTERS,  # Function code
                             0x00,  # Number of registers to read (high byte)
                             0x01])  # Number of registers to read (low byte)

    # Send the VTi request
    ser.write(vti_request)

    # Read response (assuming 5 bytes)
    vti_response = ser.read(5)

    # Extract VTi data (assuming little-endian byte order)
    vti_value = extract_int16(vti_response[3:5])

    # Get current timestamp
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    # Write data to CSV file
    writer.writerow([timestamp, leakage_value, vti_value])

    # Print data from CSV to terminal (added line)
    print(f"Data written to CSV: {timestamp}, Leakage Value: {leakage_value}, VTi Value: {vti_value}")

    # Optional: Add a delay between readings
    time.sleep(1)  # Example delay of 1 second

# Close the serial port (optional, but good practice)
ser.close()

Die Werte für VTi waren auf dem Gerätedisplay durchweg bei 290 - 300 ml, während der Werte für Leakage im einstelligen ml Bereich waren.
In meine .csv Datei wurde folgendes geschrieben:
Code:
Timestamp,Leakage Value,VTi Value
2024-07-08 13:31:49,17165,20790
2024-07-08 13:32:00,20790,3355
2024-07-08 13:32:07,3355,13891
2024-07-08 13:32:18,13891,6993
2024-07-08 13:32:25,6993,17165

Für mich sieht es so aus, als würden die Werte nicht im korrekten Format erfasst bzw. dargestellt.

Hier sind relevante Stellen aus der offiziellen modbus Dokumentation von Dräger zu diesem Gerät:

1720452972236.png


1720453002390.png



Da ich weder Erfahrung mit Python oder modbus habe, weiß ich nicht wie kompliziert mein Anliegen ist.

Falls sich jemand damit auskennt ist es ja möglicherweise eine Kleinigkeit. :)

Sofern ihr weitere Informationen benötigt liefere ich sie gerne nach.


Vielen Dank schon mal fürs Durchlesen!
 
|Moppel| schrieb:
Es liest tatsächlich Werte aus und schreibt sie mir in eine .csv Datei. 🥳

Leider sind die Werte nicht korrekt.
Ich kann dir bei der Programmierung nicht helfen, aber passen die Werte in der csv.Datei zu irgendeinem anderen Wert des Gerätes?
Wenn ja, dann werden evtl. "nur" die falschen Parameter ausgelesen.

Eine andere Möglichkeit ist, dass dies die Rohwerte (z.B. Spannung, Widerstand, oder Strom) sind, die erst in mL/min umgerechnet werden müssen.
 
Ich kann nicht ausschließen, dass die Werte zu einem anderen Parameter passen.
Das Gerät gibt wirklich extrem viel aus.

Auch dein zweiterer Einwand kann natürlich korrekt sein.

Mein Problem ist nur, dass ich mangels Wissen keinen richtigen Startpunkt für die Verbesserung des Skripts habe.

Denn wie genau die Infos aus der Dräger Dokumentation im Skript verwuschtelt sein müssen ist mir nicht klar und kann auch die KI mir nicht verständlich machen.
 
Wenn ich die verlinkte PDF richtig gelesen habe, fehlt noch die "Dräger RS 232 MEDIBUS Protocol Definition". Das muss eine PDF oder ein Buch sein, wo das eigentliche Kommunikationsprotokoll drin beschrieben wird. Die PDF im ersten Post beschreibt nur die Kommandos, die bei 4 Geräten funktionieren, aber der Protokolldefinition entsprechen müssen.

Du musst also erst mal die Definition besorgen, damit du überhaupt weißt, wie du Kommandos zum Gerät schicken und die Antworten verstehen kannst.
 
  • Gefällt mir
Reaktionen: |Moppel|
Gute Idee.

Hier ist sie. 😅

Ich versuche sie mal schrittweise der KI beizubringen.

Habe die KI jetzt mit allen Inhalten die mir relevant erschienen gefüttert.

Das Skript sieht jetzt so aus:


Python:
import csv
import time
from datetime import datetime
import serial

# Define serial port details (replace with your values)
PORT = '/dev/ttyUSB0'
BAUD_RATE = 9600

# Function to extract 16-bit signed integer from response (assuming little-endian)
def extract_int16(data):
    return data[1] + (data[0] << 8)  # Combine bytes and convert to signed integer

# Define Modbus function codes
READ_HOLDING_REGISTERS = 0x03  # Function code for reading holding registers

# Define register addresses (decimal)
LEAKAGE_REGISTER_ADDRESS = int('01', 16)  # Convert hexadecimal string to decimal
VTI_REGISTER_ADDRESS = int('04', 16)  # Convert hexadecimal string to decimal

# Open CSV file for writing (replace with your desired filename and path)
with open('modbus_data.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerow(['Timestamp', 'Leakage Value', 'VTi Value'])  # CSV header row

    # Initialize serial port
    ser = serial.Serial(PORT, BAUD_RATE)

    while True:
        # Check for and handle ICC command
        data = ser.read(1)  # Read one byte to check for ICC command
        if data == b'\x51':  # Byte value for ICC command (0x51)
            print("Received ICC command from Dräger device.")
            ser.write(b'')  # Send an empty response
            continue  # Skip to the next iteration

        # Build Modbus RTU request frame for leakage register
        leakage_request = bytearray([LEAKAGE_REGISTER_ADDRESS >> 8,
                                     LEAKAGE_REGISTER_ADDRESS & 0xFF,
                                     READ_HOLDING_REGISTERS,
                                     0x00,  # Number of registers to read (high byte)
                                     0x01])  # Number of registers to read (low byte)

        # Send the leakage request
        ser.write(leakage_request)

        # Read response (assuming 5 bytes)
        leakage_response = ser.read(5)

        # Check for successful response
        if not leakage_response:
            print("Error: No response received for leakage register.")
            continue  # Skip to the next iteration

        # Extract leakage data (assuming little-endian byte order)
        leakage_value = extract_int16(leakage_response[3:5])

        # Build Modbus RTU request frame for VTi register
        vti_request = bytearray([VTI_REGISTER_ADDRESS >> 8,
                                 VTI_REGISTER_ADDRESS & 0xFF,
                                 READ_HOLDING_REGISTERS,
                                 0x00,  # Number of registers to read (high byte)
                                 0x01])  # Number of registers to read (low byte)

        # Send the VTi request
        ser.write(vti_request)

        # Read response (assuming 5 bytes)
        vti_response = ser.read(5)

        # Check for successful response
        if not vti_response:
            print("Error: No response received for VTi register.")
            continue  # Skip to the next iteration

        # Extract VTi data (assuming little-endian byte order)
        vti_value = extract_int16(vti_response[3:5])

        # Get current timestamp
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        # Write data to CSV file
        writer.writerow([timestamp, leakage_value, vti_value])

        # Print data from CSV to terminal
        print(f"Data written to CSV: {timestamp}, Leakage Value: {leakage_value}, VTi Value: {vti_value}")

        # Optional: Add a delay between readings
        time.sleep(1)  # Example delay of 1 second

# Close the serial port (optional, but good practice)
ser.close()

Danke für euren Input.
Leider kann ich frühstens morgen die Funktionalität am Gerät testen.
 
Zuletzt bearbeitet:
Oh, in der PDF steht alles, was man braucht. Guck mal auf Seite 29 und 30, da ist zuerst der Verbindungsaufbau und später dann zwei Beispiele für die Datenabfrage dargestellt.

Ehrlich gesagt, ist das aber nix für Programmieranfänger. Du hast eine neue Programmiersprache vor dir und du musst ein Kommunikationsprotokoll implementieren. Das kommt selbst im Informatikstudium erst im 2. oder 3. Semester als Projekt vor.
Ich musste damals einen Webserver implementieren und bin 2 Tage lang einem Bug hinterhergelaufen, der sich als fehlende Leerzeile entpuppte. Ich glaube, da habe ich mein erstes graues Haar bekommen. 🤬
 
  • Gefällt mir
Reaktionen: |Moppel|
Ja ich weiß, dass mein Unterfangen auf dünnen Stelzen gebaut ist.

Allerdings macht es sogar Spaß und es wäre eine gigantische Erleichterung, wenn ich die Daten auf diesem Weg auslesen könnte.
Die Alternative wäre nämlich vom Bildschirm abzuschreiben. :/

Danke dir, ich schau mir die Stellen an bzw. lasse die KI sie anschauen.

Krik schrieb:
❤️‍🩹
 
Zuletzt bearbeitet:
Zurück
Oben