• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Windows.h - ReadFile komunikacja z arduino

VPS Starter Arubacloud
0 głosów
202 wizyt
pytanie zadane 24 lutego 2019 w C i C++ przez Maciej Złotorowicz Gaduła (4,230 p.)
enum errorType {
	Success = 0,
	PORTISNOTAVALIBLE = 1,
	OTHERERROR = 2,
	CANTGETPARMS = 3,
	CANTSETPARMS = 4

};
class Arduino
{
private:
	HANDLE handler;
	bool connected;
	COMSTAT status;
	errorType Errorer;
	DWORD errors;
public:
	
	Arduino() {
		connected = false;
	}
	Arduino(string &PortName,unsigned int BoudRate) {
		handler = CreateFileA(static_cast<LPCSTR>(PortName.c_str()),
			GENERIC_READ | GENERIC_WRITE,
			0,
			NULL,
			OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL,
			NULL);
		if (handler == INVALID_HANDLE_VALUE) {
			if (GetLastError() == ERROR_FILE_NOT_FOUND) {
				Errorer = PORTISNOTAVALIBLE;
			}
			else {
				Errorer = OTHERERROR;
			}
		}
		else {
			DCB SerialParam = { 0 };

			if (!GetCommState(handler, &SerialParam)) {
				Errorer = CANTGETPARMS;
			}
			else {
				SerialParam.BaudRate = BoudRate;
				SerialParam.ByteSize = 8;
				SerialParam.StopBits = ONESTOPBIT;
				SerialParam.Parity = NOPARITY;
				SerialParam.fDtrControl = DTR_CONTROL_ENABLE;
				
				if (!SetCommState(handler, &SerialParam))
				{
					Errorer = CANTSETPARMS;
				}
				else {
					connected = true;
					PurgeComm(this->handler, PURGE_RXCLEAR | PURGE_TXCLEAR);
				}
			}
		}
	}
	Arduino(const char *PortName, unsigned int BoudRate) {
		handler = CreateFileA(static_cast<LPCSTR>(PortName),
			GENERIC_READ | GENERIC_WRITE,
			0,
			NULL,
			OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL,
			NULL);
		if (handler == INVALID_HANDLE_VALUE) {
			if (GetLastError() == ERROR_FILE_NOT_FOUND) {
				Errorer = PORTISNOTAVALIBLE;
			}
			else {
				Errorer = OTHERERROR;
			}
		}
		else {
			DCB SerialParam = { 0 };

			if (!GetCommState(handler, &SerialParam)) {
				Errorer = CANTGETPARMS;
			}
			else {
				SerialParam.BaudRate = BoudRate;
				SerialParam.ByteSize = 8;
				SerialParam.StopBits = ONESTOPBIT;
				SerialParam.Parity = NOPARITY;
				SerialParam.fDtrControl = DTR_CONTROL_ENABLE;

				if (!SetCommState(handler, &SerialParam))
				{
					Errorer = CANTSETPARMS;
				}
				else {
					connected = true;
					PurgeComm(this->handler, PURGE_RXCLEAR | PURGE_TXCLEAR);
				}
			}
		}
	}
	bool TryConnect(string &PortName, unsigned int BoudRate) {
		handler = CreateFileA(static_cast<LPCSTR>(PortName.c_str()),
			GENERIC_READ | GENERIC_WRITE,
			0,
			NULL,
			OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL,
			NULL);
		if (handler == INVALID_HANDLE_VALUE) {
			if (GetLastError() == ERROR_FILE_NOT_FOUND) {
				Errorer = PORTISNOTAVALIBLE;
			}
			else {
				Errorer = OTHERERROR;
			}
			return false;
		}
		else {
			DCB SerialParam = { 0 };

			if (!GetCommState(handler, &SerialParam)) {
				Errorer = CANTGETPARMS;
				return false;
			}
			else {
				SerialParam.BaudRate = BoudRate;
				SerialParam.ByteSize = 8;
				SerialParam.StopBits = ONESTOPBIT;
				SerialParam.Parity = NOPARITY;
				SerialParam.fDtrControl = DTR_CONTROL_ENABLE;

				if (!SetCommState(handler, &SerialParam))
				{
					Errorer = CANTSETPARMS;
					return false;
				}
				else {
					connected = true;
					PurgeComm(this->handler, PURGE_RXCLEAR | PURGE_TXCLEAR);
					return true;
				}
			}
		}
	}
	bool TryConnect(const char *PortName, unsigned int BoudRate) {
		handler = CreateFileA(static_cast<LPCSTR>(PortName),
			GENERIC_READ | GENERIC_WRITE,
			0,
			NULL,
			OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL,
			NULL);
		if (handler == INVALID_HANDLE_VALUE) {
			if (GetLastError() == ERROR_FILE_NOT_FOUND) {
				Errorer = PORTISNOTAVALIBLE;
			}
			else {
				Errorer = OTHERERROR;
			}
			return false;
		}
		else {
			DCB SerialParam = { 0 };

			if (!GetCommState(handler, &SerialParam)) {
				Errorer = CANTGETPARMS;
				return false;
			}
			else {
				SerialParam.BaudRate = BoudRate;
				SerialParam.ByteSize = 8;
				SerialParam.StopBits = ONESTOPBIT;
				SerialParam.Parity = NOPARITY;
				SerialParam.fDtrControl = DTR_CONTROL_ENABLE;

				if (!SetCommState(handler, &SerialParam))
				{
					Errorer = CANTSETPARMS;
					return false;
				}
				else {
					connected = true;
					PurgeComm(this->handler, PURGE_RXCLEAR | PURGE_TXCLEAR);
					return true;
				}
			}
		}
	}

	errorType Error() {
		return Errorer;
	}
	~Arduino() {
		if (connected) {
			connected = false;
			CloseHandle(handler);
		}

	}
	bool isConnected()
	{
		if (!ClearCommError(handler, &errors, &status))
			connected = false;

		return connected;
	}
	
	string TryRead(){

	}
	string ReadUntil (char EndSing) {
		ClearCommError(this->handler, &errors, &this->status);
		char buff = 0;
		DWORD READED;
		string out;

			int ReadedSize = 0;
			while(buff != EndSing) {
				while (!(status.cbInQue > 0)) {
					Sleep(5);
					ClearCommError(this->handler, &errors, &this->status);
				}
				ReadFile(handler, &buff, 1, &READED, NULL);
				out.append(1, buff);
				ReadedSize++;
			}
			return out;
	}
};

Dzień dobry. Wpadłem dziś na pomysł by zmusić Cpp do porozumiewania się z arduino. i pisałem sobie powyższą klasę w oparciu o materiały z internetu - i mam teraz takie pytanie: Czy dało by się lepiej zrobić funkcję ReadUntil i czy ktoś obeznany z ta biblioteką mógłby dać mi kilka wskazówek dotyczących sprawnego wykorzystywania tych narzędzi. 

1 odpowiedź

0 głosów
odpowiedź 24 lutego 2019 przez j23 Mędrzec (194,920 p.)
wybrane 26 lutego 2019 przez Maciej Złotorowicz
 
Najlepsza

Kilka uwag:

  • linia 215: czas przeszły słowa read to... read :)
  • linia 220: zamiast zawiłego warunku !(status.cbInQue > 0), wystarczy status.cbInQue == 0
  • powinieneś sprawdzać, czy ClearCommError nie zwraca błędu, bo teraz wewnętrzna pętla może zawiesić program.
  • linia 218: zbędna zmienna ReadedSize (duplikuje out.size()).
  • Po co powielasz kod 4x w TryConnect i ctorach? Zrób jedną funkcję Connect i wywołuj ją w konstruktorach.
  • zablokuj możliwość kopiowania obiektów klasy Arduino.
komentarz 24 lutego 2019 przez jpacanowski VIP (101,940 p.)
  • linia 215: czas przeszły słowa read to... read :)

A z kolej, czyta się jako [red] w czasie przeszłym.
https://dictionary.cambridge.org/dictionary/english-polish/read_1
;)

komentarz 25 lutego 2019 przez Maciej Złotorowicz Gaduła (4,230 p.)

 

Z tym readed to trochę pojechałem xD
Teraz to wygląda tak + mam pytanie. niekiedy podczas startu, zdarzają się dziwne artefakty jak np

Polaczono z Arduino
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
oello   <==================
Hello

(arduino nadaje "Hello" na Serial Port)
i teraz pytanie z czego to wynika?

komentarz 25 lutego 2019 przez Maciej Złotorowicz Gaduła (4,230 p.)
//
//

#include "pch.h"
#include <iostream>
#include <chrono>
#include <string>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

using namespace std;
using namespace chrono;


#define WORK()cout << "WORK" << endl

enum errorType {
    Success = 0,
    PORTISNOTAVALIBLE = 1,
    OTHERERROR = 2,
    CANTGETPARMS = 3,
    CANTSETPARMS = 4,
    READERROR = 5,
    TIMEOUT = 6

};
class Arduino
{
private:
    const int WaitTime = 5;
    HANDLE handler;
    bool connected;
    COMSTAT status;
    errorType Errorer;
    DWORD errors;

    bool Connect(const char *PortName, unsigned int BoudRate) {
        handler = CreateFileA(static_cast<LPCSTR>(PortName),
            GENERIC_READ | GENERIC_WRITE,
            0,
            NULL,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            NULL);
        if (handler == INVALID_HANDLE_VALUE) {
            if (GetLastError() == ERROR_FILE_NOT_FOUND) {
                Errorer = PORTISNOTAVALIBLE;
            }
            else {
                Errorer = OTHERERROR;
            }
            return false;
        }
        else {
            DCB SerialParam = { 0 };

            if (!GetCommState(handler, &SerialParam)) {
                Errorer = CANTGETPARMS;
                return false;
            }
            else {
                SerialParam.BaudRate = BoudRate;
                SerialParam.ByteSize = 8;
                SerialParam.StopBits = ONESTOPBIT;
                SerialParam.Parity = NOPARITY;
                SerialParam.fDtrControl = DTR_CONTROL_ENABLE;

                if (!SetCommState(handler, &SerialParam))
                {
                    Errorer = CANTSETPARMS;
                    return false;
                }
                else {
                    connected = true;
                    PurgeComm(this->handler, PURGE_RXCLEAR | PURGE_TXCLEAR);
                    Errorer = Success;
                    return true;
                }
            }
        }
    }
komentarz 25 lutego 2019 przez Maciej Złotorowicz Gaduła (4,230 p.)
edycja 25 lutego 2019 przez Maciej Złotorowicz
public:
    
    Arduino() {
        connected = false;
    }
    Arduino(string &PortName,unsigned int BoudRate) {
        Connect(PortName.c_str(), BoudRate);
    }
    Arduino(const char *PortName, unsigned int BoudRate) {
        Connect(PortName, BoudRate);
    }
    bool TryConnect(string &PortName, unsigned int BoudRate) {
        return Connect(PortName.c_str(), BoudRate);
    }
    bool TryConnect(const char *PortName, unsigned int BoudRate) {
        return Connect(PortName, BoudRate);
    }

    errorType Error() {
        return Errorer;
    }
    const string StringError() {
        switch (Errorer)
        {
        case Success:
            return string("Wykonano prawidlowo!");
            break;
        case PORTISNOTAVALIBLE:
            return string("Pod podany port nie podlaczono zadnego urzadzenia!");
            break;
        case OTHERERROR:
            return string("Port jest zajety przez inny program!");
            break;
        case CANTGETPARMS:
            return string("Nie mozna wczytac ustawien portu");
            break;
        case CANTSETPARMS:
            return string("Nie mozna zmienic ustawien portu");
            break;
        case READERROR:
            return string("Podczas odczytu urzadzenie zostalo odlaczone!");
            break;
        case TIMEOUT:
            return string("Urzadzenie nie odpowiadalo zbyt dlugi czas!");
        default:
            break;
        }
    }
    ~Arduino() {
        if (connected) {
            connected = false;
            CloseHandle(handler);
        }

    }
    bool isConnected()
    {
        if (!ClearCommError(handler, &errors, &status))
            connected = false;

        return connected;
    }
    
    bool ReadUntil (string &out,const char EndSing, const unsigned int timeout = 5000) {
        ClearCommError(this->handler, &errors, &this->status);
        char buff = 0;
        out.clear();
        DWORD READ;
        int time = 0;

        while(buff != EndSing) {
            time = 0;
            while (status.cbInQue == 0) {
                Sleep(WaitTime);
                time += WaitTime;
                if (time > timeout || errors != 0) {
                    Errorer = TIMEOUT;
                    return false;
                }
                ClearCommError(this->handler, &errors, &this->status);
            }

            ReadFile(handler, &buff, 1, &READ, NULL);
            if (READ != 1) {
                Errorer = READERROR;
                return false;
            }
            out.append(1, buff);
        }
        return true;
    }
    bool ReadUntil(string &out, const char EndSing, unsigned int MaxSize, const unsigned int timeout = 5000) {
        ClearCommError(this->handler, &errors, &this->status);
        char buff = 0;
        out.clear();
        DWORD READ;
        int time = 0;

        while (buff != EndSing) {
            time = 0;
            while (status.cbInQue == 0) {
                Sleep(WaitTime);
                time += WaitTime;
                if (time > timeout || errors != 0) {
                    Errorer = TIMEOUT;
                    return false;
                }
                ClearCommError(this->handler, &errors, &this->status);
            }

            ReadFile(handler, &buff, 1, &READ, NULL);

            if (READ != 1) {
                Errorer = READERROR;
                return false;
            }
            out.append(1, buff);
            if (out.size() > 1024) {
                return false;
            }
        }
        return true;
    }
    
    void Sync(const char Sing = '\n', const unsigned int timeout = 20000) {
        ClearCommError(this->handler, &errors, &this->status);
        char buff = 0;
        int time = 0;

        while (buff != Sing) {
            time = 0;
            while (status.cbInQue == 0) {
                Sleep(WaitTime);
                time += WaitTime;
                if (time > timeout || errors != 0) {
                    Errorer = TIMEOUT;
                }
                ClearCommError(this->handler, &errors, &this->status);
            }

            ReadFile(handler, &buff, 1, NULL, NULL);
        }
    }
};

int main()
{
    Arduino arduino;
    while (true) {
        if (arduino.TryConnect("\\\\.\\COM4", 9600)) {
            cout << "Polaczono z Arduino" << endl;
            arduino.Sync();
            string out;
            while (arduino.isConnected()) {
                if (arduino.ReadUntil(out, '\n')) 
                    cout << out;

                if (arduino.Error() != Success) {
                    cout << arduino.StringError() << endl;
                }
            }
        }
        else {
            cout << arduino.StringError() << endl;
        }
        Sleep(100);
    }
}

Kod oddzielnie bo max 15K znaków. następnym razem wkleję na pastbina

komentarz 25 lutego 2019 przez j23 Mędrzec (194,920 p.)
edycja 26 lutego 2019 przez j23

Co Ty masz z tym powtarzaniem kodu? Przecież tak można:

	bool ReadUntil (string &out,const char EndSing, const unsigned int timeout = 5000) 
	{
		return ReadUntil(out, EndSing, std::numeric_limits<unsigned int>::max(), timeout);
	}

	bool ReadUntil(string &out, const char EndSing, unsigned int MaxSize, const unsigned int timeout = 5000) 
	{
		DWORD bytes_read;
		char buff = 0;
		DWORD time = 0;
		
		out.clear();
		
		ClearCommError(this->handler, &errors, &this->status);
 
		while (buff != EndSing && out.size() < MaxSize) 
		{
			time = timeGetTime();

			while (status.cbInQue == 0) 
			{
				Sleep(1);

				if (timeGetTime() - time >= timeout) 
				{
					Errorer = TIMEOUT;
					return false;
				}
				else if (errors != 0) 
				{
					Errorer = OTHERERROR;
					return false;
				}

				ClearCommError(this->handler, &errors, &this->status);
			}
 
			if (!ReadFile(handler, &buff, 1, &bytes_read, NULL) || bytes_read != 1) 
			{
				Errorer = READERROR;
				return false;
			}

			out.append(1, buff);
		}

		return true;
	}

	Arduino& operator= (const Arduino&) = delete;
	Arduino(const Arduino&) = delete;

z czego to wynika?

Pewny jesteś, że po stronie Arduino wszystko jest ok?

komentarz 26 lutego 2019 przez Maciej Złotorowicz Gaduła (4,230 p.)
Czy mógłbym cię jeszcze zapytać o jakieś sugestie na temat funkcji komunikacji?
(czytaj pomysły)
komentarz 26 lutego 2019 przez j23 Mędrzec (194,920 p.)
Chyba nie rozumiem pytania. O jakiej funkcji komunikacji piszesz?
komentarz 26 lutego 2019 przez Maciej Złotorowicz Gaduła (4,230 p.)
Czy może masz jakiś pomysł na inne funkcje służące do komunikacji czy te dwie są raczej wyczerpujące i pozwolą na płynną wymianę danych?
komentarz 26 lutego 2019 przez j23 Mędrzec (194,920 p.)
edycja 26 lutego 2019 przez j23

Chyba powinieneś napisać jakąś funkcję wysyłającą.

 

Zrobiłbym też metodę Read czytającą dane do bufora. ReadUntil czyta z portu po znaku, co nie jest zbytnio eleganckie (ani wydajne), no ale załóżmy, że na potrzeby czytania tekstu może być. Read byłaby dobra do czytania większych bloków danych niekoniecznie tekstowych.

komentarz 26 lutego 2019 przez Maciej Złotorowicz Gaduła (4,230 p.)
zauważyłem że bufor wejścia do arduino ma limit 63 bajtów. oznacza to że ten bufor znajduje się już na płytce. Jakie limity są dla bufra wyjścia? Czy dobrym pomysłem było by zrobienie funkcji która na oddzielnym wątku pobiera nonstop dane z portu i wpisuje je do kolejki?
komentarz 27 lutego 2019 przez j23 Mędrzec (194,920 p.)
edycja 27 lutego 2019 przez j23

oznacza to że ten bufor znajduje się już na płytce

Jak wszystko co jest w Arduino :P Bufory są zdefiniowane w źródłach i można je redefiniować (vide _SS_MAX_RX_BUFF dla SoftwareSerial i SERIAL_RX_BUFFER_SIZE/SERIAL_TX_BUFFER_SIZE dla HardwareSerial).

 

Jakie limity są dla bufra wyjścia?

Nie znam Arduino, ale z pobieżnego przejrzenia źródeł wygląda, że w SoftwareSerial wyjście nie jest buforowane, w HardwareSerial jest (ale nie musi).

Czy dobrym pomysłem było by zrobienie funkcji która na oddzielnym wątku (...)

Pomysł może i dobry, tylko problem w tym, że AFAIK w Arduino nie ma wątków ;) Chyba że piszesz o stronie Windowsa...

komentarz 27 lutego 2019 przez Maciej Złotorowicz Gaduła (4,230 p.)
	template<typename T>
	bool Read(T* Data, const unsigned int Size) {
		if (!connected) {
			return false;
		}
		ClearCommError(handler, &errors, &status);
		DWORD READ;
		ReadFile(handler, Data, Size*sizeof(T), &READ, NULL);

		if (READ != Size*sizeof(T) || errors != 0) {
			Errorer = READERROR;
			return false;
		}
		return true;
	}

Czy to wygląda/działa elegancko? Przepraszam że tak długo ale czasu nie miałem by to dokończyć.

komentarz 27 lutego 2019 przez Maciej Złotorowicz Gaduła (4,230 p.)
Tzn działa nawet fajnie tylko problem jeżeli ominie bajta/arduino nada coś ponad to
wtedy wszystko się zepsuje przydało by się chyba dodać jakąś synchronizację czy raczej jest to zbędne? Nie wiem w jakim stopniu mogę zaufać Serial Portowi.
komentarz 27 lutego 2019 przez j23 Mędrzec (194,920 p.)
edycja 27 lutego 2019 przez j23
DWORD Read(char* Data, DWORD Size) 
{
	if (!connected) return 0;

	ClearCommError(handler, &errors, &status);
	
	DWORD bytes_read;
 
	if (!ReadFile(handler, Data, Size, &bytes_read, NULL) || errors != 0) 
	{
		Errorer = READERROR;
		return 0;
	}

	return bytes_read;
}



template <
	typename T,
	typename = std::enable_if_t<std::is_pod<T>::value>
>
bool ReadAll(T* Data, DWORD Size) 
{
	char* p = reinterpret_cast<char*>(Data);
	DWORD bytes_to_read = sizeof(T) * Size;

	while(bytes_to_read > 0 && Errorer == errorType::Success)
	{
		DWORD bytes_read = Read(p, bytes_to_read);
		bytes_to_read -= bytes_read;
		p += bytes_read;
	}

	return bytes_to_read == 0;
}

Tak bym zrobił.

komentarz 27 lutego 2019 przez Maciej Złotorowicz Gaduła (4,230 p.)
nie jestem pewnien czy nie miałeś na myśli

while(bytes_to_read> 0 ...
(bo inaczej pętla działała by w nieskończoność/do momentu napotkania błędu)

i jaką przewagę ma to nad tą "prostszą" wersją?
(nie licząc tego że można teraz wywołać Read by ładnie wczytać surową listę charów)
komentarz 27 lutego 2019 przez j23 Mędrzec (194,920 p.)

nie jestem pewnien czy nie miałeś na myśli

Tak, to miałem na myśli.

 

 jaką przewagę ma to nad tą "prostszą" wersją?

Twoja wersja zakłada, że ReadFile przeczyta Size * sizeof(T) bajtów, ale może się zdarzyć, że w buforze portu nie będzie tyle bajtów i wtedy twoja funkcja zwróci błąd, co IMO jest błędnym podejściem. Dlatego zrobiłem funkcję Read, która czyta tyle bajtów, ile może, i ReadAll, która czyta dokładnie tyle, ile podano w parametrze. Dodatkowo szablon jest zabezpieczony przed czytaniem nie-POD-ów.

 

komentarz 28 lutego 2019 przez Maciej Złotorowicz Gaduła (4,230 p.)
edycja 28 lutego 2019 przez Maciej Złotorowicz
Wydawało mi sie że jeżeli bufor nie zawiera N bajtów to ReadFile zaczeka aż Ten bufor się zapełni.
komentarz 28 lutego 2019 przez j23 Mędrzec (194,920 p.)
Jakby tak było, to czwarty parametr funkcji byłby niepotrzebny.

Podobne pytania

0 głosów
1 odpowiedź 264 wizyt
pytanie zadane 31 października 2018 w C i C++ przez Aleksander Początkujący (360 p.)
0 głosów
0 odpowiedzi 247 wizyt
0 głosów
0 odpowiedzi 462 wizyt

92,455 zapytań

141,263 odpowiedzi

319,100 komentarzy

61,854 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...