Daten aus virtuellem COM Port auslesen



  • Hallo zusammen,
    ich versuche Daten die vom einem Messgerät über einen COM Port ausgegeben werden über ein Programm einzulesen und dann zu speichern. So weit so gut. Der COM Port wird erkannt und geöffnet, die Daten werden auch übertragen nur leider in einem etwas kryptischen Format.
    Ich habe mal meinen Code hinten angehangen. Ich hoffe mir kann jemand helfen das ich die Daten dann am Ende auch lesen kann.
    MfG Nopsi

    Die Headerdatei:

    #pragma once
    
    #ifndef _COMPORT_H_
    #define _COMPORT_H_
    
    #include "windows.h"
    #include <iostream>
    
    using namespace std;
    
    class ComPort
    {
    public:
    	ComPort(void);
    	~ComPort(void);
    	void InitComPort(void);
    	int Read();
    	int Write();
    
    private:
    	HANDLE hCom;
    };
    
    #endif
    

    Der Code:

    // ConsoleApplication1.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    
    #include "WindowsProject2.h"
    #ifndef _COMPORT_H_
    #define _COMPORT_H_
    
    #include "windows.h"
    #include <iostream>
    
    using namespace std;
    
    class ComPort
    {
    public:
    	ComPort(void);
    	~ComPort(void);
    	void InitComPort(void);
    	int Read();
    	int Write();
    
    private:
    	HANDLE hCom;
    };
    
    #endif
    ComPort::ComPort()
    {
    	hCom = INVALID_HANDLE_VALUE;
    }
    
    ComPort::~ComPort(void)
    {
    	CloseHandle(hCom);
    }
    
    void ComPort::InitComPort(void)
    {
    	DCB Dcb;
    	COMMTIMEOUTS Cto;
    
    	if ((hCom = CreateFile(TEXT("COM3:"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL)) != INVALID_HANDLE_VALUE)
    	{
    
    		Dcb.BaudRate = 115200;
    		Dcb.fParity = false;
    		Dcb.fNull = false;
    		Dcb.StopBits = ONESTOPBIT;
    		Dcb.Parity = NOPARITY;
    		Dcb.ByteSize = 8;
    		SetCommState(hCom, &Dcb);
    		Cto.ReadIntervalTimeout = 0;
    		Cto.ReadTotalTimeoutMultiplier = 0;
    		Cto.ReadTotalTimeoutConstant = 0;
    		Cto.WriteTotalTimeoutMultiplier = 0;
    		Cto.WriteTotalTimeoutConstant = 0;
    		SetCommTimeouts(hCom, &Cto);
    		cout << "Port opened" << endl;
    	}
    	else
    	{
    		cout << "Couldn't open Port" << endl << "ERROR: " << GetLastError() << endl;
    	}
    }
    
    int ComPort::Read()
    {
    	DWORD BytesRead = 0;
    	char buf[100];
    
    	if (!ReadFile(hCom, buf, 1, &BytesRead, NULL))
    	{
    		cout << "Error reading from Port" << endl;
    		return -1;
    	}
    
    	cout << buf << endl;
    	return BytesRead;
    }
    
    int ComPort::Write()
    {
    	DWORD BytesWritten = 0;
    
    	char buf[] = "Hallo";
    
    	if (!WriteFile(hCom, buf, 1, &BytesWritten, NULL))
    	{
    		cout << "Error writing to Port" << endl;
    		return -1;
    	}
    
    	return BytesWritten;
    }
    int main() {
    	
    	ComPort var1;
    	var1.InitComPort();
    	var1.Read();
    	
    	system("pause");
    
    }
    
    


  • Und was erwartet du jetzt von uns? Wir kennen weder das Messgerät noch die Doku dazu, um sagen zu können in welchem Format die Daten geschickt werden (es wird wohl ein Binärformat sein).

    PS: Warum liest und schreibst du jeweils nur 1 Byte?
    Und müßtest du nicht erst Daten senden, bevor eine Antwort kommt oder sendet das Messgerät von sich aus (ununterbrochen) Daten?

    Die kryptischen Daten erhältst du übrigens bisher, weil du auf uninitialisierten Speicher zugreifst: char buf[100];.
    Ändere es mal zu

    char buf[100] = { 0 };
    

    Aber selbst dann wirst du wohl keine vernünftige Ausgabe erhalten, da wohl keine reinen C-Strings übertragen werden (die du dann auch erst passend parsen müßtest)...



  • @Nopsi Du liest einmal ein Zeichen ein.

    (Du rufst nur einmal Read auf.
    In Read wird nur ein Zeichen eingelesen.)

    Was soll das bringen?



  • @Th69 wenn ich mit putty zB den COM Port öffne wird alles normal angezeigt. das Gerät sendet die daten wenn man die ausgabe manuell startet auf dem Gerät.
    Das mit dem

    char buf[100] = { 0 };
    

    habe ich probiert und dann gibt er nur 0 aus wenn ich die Ausgabe starte.
    Was das Datenformat angeht bin ich mir ziemlich sicher das es schon strings sein müssen, da es schon ein Excel ADD-IN gibt von der Firma des Messgeräts welches auch die Daten einliest, aber leider Gottes sehr bescheiden und unvollständig, und dort wird nur der Datenabruf initialisiert aber nicht weiter mit den empfangenen Daten gemacht außer sie in Zellen zu schreiben.

    Mir geht es ja eigentlich nur drum ob es eine Möglichkeit gibt den Datentyp DWORD zu umgehen (durch einen anderen zu ersetzen) oder ihn zu konvertieren, weil soweit ich das verstanden habe wird das von der Variable BytesRead in das Array buff geschrieben und ausgegeben.

    MfG Nopsi



  • @Nopsi sagte in Daten aus virtuellem COM Port auslesen:

    weil soweit ich das verstanden habe wird das von der Variable BytesRead in das Array buff geschrieben und ausgegeben.

    Das hast du falsch verstanden.
    BytesRead enthält die Anzahl der gelesenen Byte.

    Die Daten selber stehen (bei dir) in buf.

    Da du jedoch nur ein eintiges Byte liest, bringt das alles nicht so viel.

    Warum 1 Byte? Weil du es im dritten Parameter angegeben hast: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile

    Da sollte (jetzt bei dir) ein 100 stehen (die Größe von der Definition bei buf)



  • @DirkB Vielen Dank das wars.
    Jetzt aber gleich noch eine Frage: Kann man das irgendwie dynamisch machen die größe des Arrays? weil 100 Bytes reichen leider nicht aus um alle Daten zu übertragen. kann man das eventuell direkt mit dem BytesRead verknüpfen oder eher nicht?



  • @Nopsi
    Nimm einen std::vector<char> und häng´ die gelesenen Bytes hinten an. Der Consumer muss dann nur prüfen, ob genügend Daten für einen gültigen Datensatz vorliegen und kann dann diesen Datensatz behandeln und aus dem Vektor entfernen.

    Reader: Hängt Daten hinten an den Vektor an
    Consumer: Prüft auf Vollständigkeit und entfernt Daten ggf. vom Anfang des Vektors



  • Lese in einer Schleife solange Daten ein, wie BytesRead > 0.
    Du kannst die Daten dann als ein std::string zusammensetzen und zurückgeben (achte aber auf das terminierende Null-Byte im char-Array).



  • @Nopsi sagte in Daten aus virtuellem COM Port auslesen:

    Was das Datenformat angeht bin ich mir ziemlich sicher das es schon strings sein müssen, da es schon ein Excel ADD-IN gibt von der Firma des Messgeräts welches auch die Daten einliest,

    Dann solltest du auf jeden Fall rausfinden wie das Protokoll wirklich aussieht

    mit dem freien Microsoft Tool PortMon
    https://docs.microsoft.com/en-us/sysinternals/downloads/portmon

    oder z.B. dem Free Serial Analyzer
    https://freeserialanalyzer.com/

    kannst dur dir die Daten die über die Leitung gehen anschauen während die Original-Software läuft



  • @Nopsi Du willst ja sicher auch irgendwie die Daten verarbeiten.
    Im Augenblick gibst du das gelesene ja nur auf der Konsole aus.

    Messgeräte haben (meist) ein festes Protokoll - entweder ein Terminierungszeichen, eine feste Anzahl Daten oder es wird vorher die Anzahl übertragen.

    Davon hängt es dann auch ab, wie du die Daten liest.



  • @llm ich habe den Verkehr über die Schnittstelle schon mit dem FreeSerialPort Monitor ausgelsen und da werden wirklich nur die Daten (4 Messwerte zB 35.015 µm, der Mittelwert und die Zeit/Datum ausgegeben).
    @DirkB Ja ich würde die gerne in eine csv datei abspeichern um sie später in einer Datenbank oder sonst irgendwie weiter zu verwenden.

    Ich habe aber mittlerweile beim recherchieren ein etwas einfacheres Programm gefunden welches das ganze auch live macht.

    class SerialRead
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Serial read init");
                SerialPort port = new SerialPort("COM3", 115200, Parity.None, 8, StopBits.One);
                port.Open();
                while (true)
                {
                    Console.WriteLine(port.ReadLine());
                }
    
            }
    

    Da muss man nicht mal die Datenübertragung starten auf dem Gerät sondern wenn man die 4 messungen gemacht hat und das gerät einen Block erzeugt wird dieser automatisch über den COM 3 Port ausgelesen.
    Kann ich die durch Console.Readline() eingelesenen Daten nicht irgenwie direkt in eine Datei schreiben? es müsste nicht mal in der konsole angezeigt werden.

    MfG Nopsi



  • @Nopsi sagte in Daten aus virtuellem COM Port auslesen:

    Kann ich die durch Console.Readline() eingelesenen Daten nicht irgenwie direkt in eine Datei schreiben? es müsste nicht mal in der konsole angezeigt werden.

    Ja, natürlich. Datei zum schreiben öffnen und die gelesenen Daten dorthin schreiben.
    Am Ende Datei schließen.

    Du kannst aber auch von der Konsole die Ausgabeumleitung mittels > benutzen.

    Programmname > Dateiname.txt



  • Ich habe jetzt zur Ausgabe folgenden Code geschrieben:

    using System;
    using System.IO.Ports;
    using System.Threading;
    using System.IO;
    using System.Text;
    
    
    
    namespace SerialReadTest
    {
        class DirAppend
        {
            public static void Main()
            {
                using (StreamWriter w = File.AppendText("log.txt"))
                {
                   
                }
    
                using (StreamReader r = File.OpenText("log.txt"))
                {
                    DumpLog(r);
                }
            }
            public static void DumpLog(StreamReader r)
            {
                
                var fileStream = new StreamWriter(@"C:\Users\User\source\repos\ConsoleApplication2\Debug\log.txt", true);
               
    
    
    
                string line;
                Console.WriteLine("Serial read init");
                SerialPort port = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One);
                port.Open();
                while (true)
                {
                    line = port.ReadLine(); 
                    Console.WriteLine(port.ReadLine());
    
                   
                    fileStream.WriteLine(line);
                    Console.WriteLine(line);
                }
                }
            }
        }
    

    nur leider kommt in der log datei nix an. Kannd a jemand helfen?



  • Kannst du ein Hex-Dump oder sowas (wo man auch wirklich alle Bytes sieht) von dem Protokoll einstellen - dann müssen wir hier nicht raten wenn du dann noch Probleme hast da etwas sauber zu verarbeiten - sind die Daten wirklich Newline terminiert?

    nur leider kommt in der log datei nix an. Kannd a jemand helfen?

    deine Baudrate ist in dem C# Code anders

    line = port.ReadLine();
    Console.WriteLine(port.ReadLine());

    du schreibst erst in die line-Variable und liesst dann noch mal vom Port? d.h. du verlierst Daten durch die Console.WriteLine-Ausgabe (die du 2 mal machst?)



  • @llm Ja das hatte ich nur noch drin um zu sehen ob in der line variable auch was drin steht oder nicht.
    habe das problem mittlerweile gelöst.

    Hier der abschließende funktionierende Code:

    using System;
    using System.IO.Ports;
    using System.Threading;
    using System.IO;
    using System.Text;
    
    
    
    namespace SerialReadTest
    {
        class DirAppend
        {
            public static void Main()
            {
                using (StreamWriter w = File.AppendText("log.txt"))
                {
                   
                }
    
                using (StreamReader r = File.OpenText("log.txt"))
                {
                    DumpLog(r);
                }
            }
            public static void DumpLog(StreamReader r)
            {
                var fileStream = new StreamWriter(@"C:\Users\User\source\repos\ConsoleApp2\ConsoleApp2\bin\Debug\log1.txt");
                fileStream.AutoFlush = true;
            
                string line;
                Console.WriteLine("Serial read init");
                SerialPort port = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One);
                port.Open();
                while (true)
                {
                    line = port.ReadLine(); 
                    Console.WriteLine(port.ReadLine());
    
                   
                    fileStream.WriteLine(line);
                   
                    
                }
                }
            }
        }
    

    Vielen Dank für eure Hilfe. Hat mir super viel Zeit erspart. 🙂



  • @Nopsi sagte in Daten aus virtuellem COM Port auslesen:

                    line = port.ReadLine(); 
                    Console.WriteLine(port.ReadLine());
    
                   
                    fileStream.WriteLine(line);
    

    das ist immer noch falsch

    die Daten aus der Konsolen-Ausgabe landen nicht in deiner Datei (die verschluckst du einfach)

    so wäre es richtig

    line = port.ReadLine(); 
    Console.WriteLine(line );
    fileStream.WriteLine(line);
    

Log in to reply