Text über die Serielle Schnittstelle (COM3) einlesen
-
Hallo,
ich möchte einen Text über die Serielle-Schnittstelle (COM3) einlesen.
Dazu habe ich hier im Forum folgendes Beispiel gefunden:// ConsoleApplication3.cpp : Diese Datei enthält die Funktion "main". Hier beginnt und endet die Ausführung des Programms. // Serielle Schnittstelle ansprechen und Daten senden // https://www.c-plusplus.net/forum/topic/300637/serielle-schnittstelle-ansprechen-und-daten-senden #include <iostream> #include <stdio.h> #include "ComPort.h" ComPort::ComPort(void) { 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) if ((hCom = CreateFile(TEXT("\\\\.\\COM3"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL)) != INVALID_HANDLE_VALUE) { Dcb.DCBlength = sizeof(Dcb); GetCommState(hCom, &Dcb); Dcb.BaudRate = 4800; Dcb.fParity = false; Dcb.fNull = false; Dcb.StopBits = ONESTOPBIT; Dcb.Parity = NOPARITY; Dcb.ByteSize = 8; Dcb.EvtChar = 'X'; 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() const { DWORD mask = 0; DWORD bytesRead = 0; char buf[1024] = ""; char buf_E[1024] = ""; int i = 0; SetCommMask(hCom, EV_RXCHAR | EV_RXFLAG | EV_RXFLAG); while (true) { if (WaitCommEvent(hCom, &mask, 0)) { cout << "mask " << mask << endl; switch (mask) { case EV_RXCHAR: ReadFile(hCom, &buf[i++], 1, &bytesRead, NULL); cout << "Empfangen: " << buf[i - 1] << endl; if (buf[i - 1] == '@') exit; // Einlesen Ende wenn Zeichen '@' gesendet wird break; case EV_ERR: cout << "Fehler beim lesen" << endl; break; case EV_RXFLAG: cout << "Eventchar empfangen" << endl; break; } } } return bytesRead; } int ComPort::Write() const { DWORD BytesWritten = 0; //char buf[] = "Hallo"; char buf[] = "X"; if (!WriteFile(hCom, buf, 1, &BytesWritten, NULL)) { cout << "Error writing to Port" << endl; return -1; } return BytesWritten; } int main() { DWORD BytesRead = 0; ComPort var1; var1.InitComPort(); var1.Write(); // Zeichen 'X' senden BytesRead = var1.Read(); cout << "BytesRead " << BytesRead << endl; system("pause"); return 0; }
// ComPort.h #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() const; int Read_org() const; int Write() const; private: HANDLE hCom; }; #endif
Das Einlesen des Textes funktioniert soweit.
Wie aber kann ich das Einlesen mit "Read()" benden.
Mein Versuch war das Einlesen benden wenn z.B. das Zeichen '@' gesendet wird: " if (buf[i - 1] == '@') exit;".
Funktioniert leider nicht, das Einlesen wird nicht beendet.
Juergen
-
exit;
macht nicht das was du vermutlich willst. Es macht nämlich gar nix.exit
ist nämlich in C++ kein Keyword.exit
ist eine Standard-Library Funktion, die dein Programm beendet. Wenn du diese aufrufen wolltest, müsstest duexit(123);
schreiben, bzw. statt 123 was auch immer der Exit-Code sein soll.Nur
exit
, ohne()
ist kein Funktionsaufruf. Daher läuft dein Programm dann weiter. Nur was istexit
, ohne()
, und warum bekommst du keinen Fehler? Der Grund ist dass du in C++ als "Anweisung" sog. "Expressions" (Ausdrücke) hinschreiben kannst -- auch wenn diese nichts tun. Undexit
ist so ein Ausdruck. Er hat einen Wert (eine Referenz auf dieexit
Funktion), aber keinen Effekt. Macht in diesem Fall keinen Sinn, ist aber erlaubt. Wobei dein Compiler dir hier eine Warning geben sollte. Wie z.B.:<source>:5:5: warning: expression result unused [-Wunused-value] exit;
Wobei vermutlich willst du gar nicht
exit
aufrufen, sondern dass der Programm nach derwhile (true)
Schleife weitermacht. Wie geht das? z.B. mitgoto
:int ComPort::Read() const { ... while (true) { ... if (buf[i - 1] == '@') goto done; // Einlesen Ende wenn Zeichen '@' gesendet wird ... } done: return bytesRead; }
Wobei es einen viel eleganteren Weg gibt: du kannst dort wo du die Funktion verlassen willst einfach direkt
return bytesRead;
hinschreiben. Also einfachint ComPort::Read() const { ... while (true) { ... if (buf[i - 1] == '@') return bytesRead; // Einlesen Ende wenn Zeichen '@' gesendet wird ... } // Hier brauchst du kein return, da diese Stelle sowieso nie erreicht werden kann. }
-
ps: du solltest den Returnwert von
ReadFile
prüfen und Fehler behandeln.
-
Mir sind noch zwei weitere Dinge aufgefallen (auch wenn es nicht dein Quelltext ist):
using namespace std
sollte im Header nicht verwendet werden, überhaupt sollteusing namespace ...
in Headerdateien nicht verwendet werden, weil sie Mehrdeutigkeiten erzeugen können und das Namespace-Konzept aushebeln- seit C++11 gibt es das Schlüsselwort
nullptr
, das statt des WertesNULL
benutzt werden sollte.
-
ReadFile(hCom, &buf[i++], 1, &bytesRead, NULL);
Du solltest hier besser auf Rückgabewerte achten. In
bytesRead
kann durchaus auch 0 stehen und trotzdem springst du eine Stelle inbuf
weiter. Davon mal abgesehen kannReadFile()
auchFALSE
zurückliefern.Ferner gefällt mir deine Variable
char buf[1024]
nicht. Denn:- Aus deinem Code wird nicht ersichtlich wieviele Zeichen du in der Summe empfangen hast. Du achtest nicht auf Nullterminierung und hast auch keine Variable, welche dir die Anzahl der gespeicherten Bytes angibt.
- Du musst besser auf deine Grenzen achten. Aktuell darf die Gegenseite nur 1024 Zeichen senden, sonst gibt es einen Buffer-Overflow. Bloß das weiß die Gegenseite halt nicht.
Mein Tipp: Nimm stattdessen einen
std::vector<char>
. Zusätzlich musst du aber eine max. Größe festlegen und implementieren, falls die Gegenseite nur Blödsinn (z.B. 5 GByte ana
) sendet.
Mach dir auch die Natur der seriellen Daten klar.
In manchen Fällen kommen diese in einer Befehlsform (z.B. "at\r\n") vor. Da kann es durchaus Sinn machen, die Daten periodisch abzufragen. Wird dann ein Startzeichen gefunden, so liest die Funktion alle weiteren Zeichen ein, bis zu das Endezeichen gefunden hast oder du in einem Timeout läufst.
In anderen Fällen können diese sporadisch kommen. Dann würde ein Thread Sinn machen. Der Thread liest dann alle Zeichen ein bis er in einen Timeout rennt und leitet dann die eingelesenen Daten per Callback an dich weiter.
-
@hustbaer sagte in Text über die Serielle Schnittstelle (COM3) einlesen:
exit;
macht nicht das was du vermutlich willst. Es macht nämlich gar nix.exit
ist nämlich in C++ kein Keyword.exit
ist eine Standard-Library Funktion, die dein Programm beendet. Wenn du diese aufrufen wolltest, müsstest duexit(123);
schreiben, bzw. statt 123 was auch immer der Exit-Code sein soll.Nur
exit
, ohne()
ist kein Funktionsaufruf. Daher läuft dein Programm dann weiter. Nur was istexit
, ohne()
, und warum bekommst du keinen Fehler? Der Grund ist dass du in C++ als "Anweisung" sog. "Expressions" (Ausdrücke) hinschreiben kannst -- auch wenn diese nichts tun. Undexit
ist so ein Ausdruck. Er hat einen Wert (eine Referenz auf dieexit
Funktion), aber keinen Effekt. Macht in diesem Fall keinen Sinn, ist aber erlaubt. Wobei dein Compiler dir hier eine Warning geben sollte. Wie z.B.:<source>:5:5: warning: expression result unused [-Wunused-value] exit;
Wobei vermutlich willst du gar nicht
exit
aufrufen, sondern dass der Programm nach derwhile (true)
Schleife weitermacht. Wie geht das? z.B. mitgoto
:int ComPort::Read() const { ... while (true) { ... if (buf[i - 1] == '@') goto done; // Einlesen Ende wenn Zeichen '@' gesendet wird ... } done: return bytesRead; }
Wobei es einen viel eleganteren Weg gibt: du kannst dort wo du die Funktion verlassen willst einfach direkt
return bytesRead;
hinschreiben. Also einfachint ComPort::Read() const { ... while (true) { ... if (buf[i - 1] == '@') return bytesRead; // Einlesen Ende wenn Zeichen '@' gesendet wird ... } // Hier brauchst du kein return, da diese Stelle sowieso nie erreicht werden kann. }
Hallo,
vielen Dank für Deine hilfreichen Hinweise: mit "return bytesRead" funktioniert es (natürlich auch mit "goto").
Zum Hinweis: "Returnwert von ReadFile prüfen" kann ich dazu noch genauere Hinweise erhalten ?Juergen
-
@DocShoe sagte in Text über die Serielle Schnittstelle (COM3) einlesen:
Mir sind noch zwei weitere Dinge aufgefallen (auch wenn es nicht dein Quelltext ist):
using namespace std
sollte im Header nicht verwendet werden, überhaupt sollteusing namespace ...
in Headerdateien nicht verwendet werden, weil sie Mehrdeutigkeiten erzeugen können und das Namespace-Konzept aushebeln- seit C++11 gibt es das Schlüsselwort
nullptr
, das statt des WertesNULL
benutzt werden sollte.
Hallo,
vielen Dank für den Hinweis: werde ich berücksichtigen.Juergen
-
@Quiche-Lorraine sagte in Text über die Serielle Schnittstelle (COM3) einlesen:
ReadFile(hCom, &buf[i++], 1, &bytesRead, NULL);
Du solltest hier besser auf Rückgabewerte achten. In
bytesRead
kann durchaus auch 0 stehen und trotzdem springst du eine Stelle inbuf
weiter. Davon mal abgesehen kannReadFile()
auchFALSE
zurückliefern.Ferner gefällt mir deine Variable
char buf[1024]
nicht. Denn:- Aus deinem Code wird nicht ersichtlich wieviele Zeichen du in der Summe empfangen hast. Du achtest nicht auf Nullterminierung und hast auch keine Variable, welche dir die Anzahl der gespeicherten Bytes angibt.
- Du musst besser auf deine Grenzen achten. Aktuell darf die Gegenseite nur 1024 Zeichen senden, sonst gibt es einen Buffer-Overflow. Bloß das weiß die Gegenseite halt nicht.
Mein Tipp: Nimm stattdessen einen
std::vector<char>
. Zusätzlich musst du aber eine max. Größe festlegen und implementieren, falls die Gegenseite nur Blödsinn (z.B. 5 GByte ana
) sendet.
Mach dir auch die Natur der seriellen Daten klar.
In manchen Fällen kommen diese in einer Befehlsform (z.B. "at\r\n") vor. Da kann es durchaus Sinn machen, die Daten periodisch abzufragen. Wird dann ein Startzeichen gefunden, so liest die Funktion alle weiteren Zeichen ein, bis zu das Endezeichen gefunden hast oder du in einem Timeout läufst.
In anderen Fällen können diese sporadisch kommen. Dann würde ein Thread Sinn machen. Der Thread liest dann alle Zeichen ein bis er in einen Timeout rennt und leitet dann die eingelesenen Daten per Callback an dich weiter.
Hallo,
auch Dir vielen Dank für Deine Hinweise.
Es ist so das ich die ersten Schritte (mit C++) mit der Seriellen-Schnittstelle mache.
Ersteinmal bin ich froh ein Beispiel-Programm zu haben das funktioniert auch wenn es noch nicht fertig ist.
Deshal bin ich froh wenn ich dazu entsprechende Hinweise erhalte.Juergen
-
Dieser Beitrag wurde gelöscht!
-
Hallo,
ich habe noch eine Frage:
Wie kann ich: "TEXT("COM3:") in eine Variable speichern ?if ((hCom = CreateFile(TEXT("COM3:"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL)) != INVALID_HANDLE_VALUE)
Z.B.:
const wchar_t* comport = L"COM3";
Damit wird mir aber mit:
std::cout << "Couldn't open Port : " << comport <<std:: endl; Couldn't open Port : 0000006D67CFF438
angezeigt.
Juergen
-
@jbaben
Das ist schon richtig so, das Problem iststd::cout
, das nur mit ANSI Zeichenketten umgehen kann und dir statt des Textes die Adresse ausgibt. Wenn du Wide-Zeichenketten in der Konsole ausgeben möchtest kannst dustd::wcout
benutzen, musst dann aber von allen auszugebenden Texten dann auch die Wide-Variante benutzen, die Ausgabe von ANSI und Wide-Strings lässt sich nicht mischen.std::wcout << L"Couldn't open Port: " << comport << std::endl;
Wide-String Literale kennzeichnet man mit einem vorangestellen "L".
-
Hallo,
vielen Dank für die schnelle Antwort.
Mit "std::wcout" funktioniert die Anzeige.
Muss aber noch die Ein-Ausgabe von der RS232 testen.Juergen