Wątek przeniesiony 2018-11-08 11:55 z C/C++ przez Marooned.

SerialPort Communication

0

Witem Serdecznie wszystkich użytkowników.
Jestem nowym użytkownikiem tego forum jak i początkującym programistą. Stanąłem przed pewnym problemem, którego nie mogę rozwiązać. Mam napisany program w C++ , który analizuje obraz i podaje współrzędne środka piłeczki względem środka platformy. Moją zwrotną informacją jest współrzędna x i y. Są to liczby typu integer i obie przyjmują wartości od -500 do 500(zależy od rozdzielczości obrazu). W tym momencie pojawia się mój problem. Chciałbym, aby te dwie współrzędne zostały wysłane przez USB do Arduino w którym mam napisany program (regulator PID). Te współrzędne będą przetwarzane na ruch serwomechanizmów. Czy byłby ktoś w stanie napisać mi fragment programu na taką komunikacje i przesył danych ? Eksperymentowałem z klasą nazw "System","System::Ports" lecz nie mogę sobie z tym poradzić. Próbowałem również używać bibliotek dostępnych na Github. Walczę z tematem od kilku tygodni i na prawdę już jestem bezradny.
Z góry bardzo dziękuje za pomoc.
Serdecznie pozdrawiam :)

1

Próbowałeś WinUSB?

1

Jeżeli mówisz o System::IO::Ports to znaczy, że mówisz o C++/CLI.

Nie znam się na C++/CLI, ale w C# (który korzysta z tych samych bibliotek) to jest proste:

SerialPort sp = new SerialPort("COM6", 115200); // połączenie będzie z portem COM6 z prędkością 115200 bps
sp.Open(); // otwórz port
sp.WriteLine(new byte[] { 12, 22, 0 }, 0, 3); // wyślij ciąg bajtów: 12,22 oraz 0
sp.Close(); // zamknij port

Po stronie Arduino najłatwiej wykorzystać Serial.readBytesUntil() i wczytywać do bufora aż do napotkania 0. Potem możesz wykorzystywać swoje dane z bufora (u mnie: 12 i 22) do zrobienia czegoś dalej. Jak się nie mieszczą w jednym bajcie to trzeba będzie kombinować i wysyłać inaczej.

Klasa SerialPort umie wysyłać "gołe" ciągi znaków, ciągi bajtów i stringi.

0

jeśli ten port usb arduino jest widziano nie jako USB a jako COM to możesz użyć QT. W qt od 5 jest serialPort. Przykłady użycia w dokumentacji a z arduino na necie.

0
0x666 napisał(a):

Próbowałeś WinUSB?

Nie próbowałem tą metodą. Z tego co czytam na ten temat ten sposób wymaga utworzenia pewnego szkieletu. Czy narzucony szkielet przyjmie mój kod do obsługi kamery ?

0

Nie bardzo wiem, o jakim szkielecie piszesz. Otwierasz port funkcją CreateFile(), wywołujesz WinUsb_Initialize(), robisz to, co tam chcesz, a na koniec wywołujesz WinUSB_Free() i CloseHandle(). Pewnie będziesz musiał użyć funkcji SetupDiEnumDeviceInterfaces(), żeby pobrać nazwę "urządzenia" USB.

0
0x666 napisał(a):

Nie bardzo wiem, o jakim szkielecie piszesz. Otwierasz port funkcją CreateFile(), wywołujesz WinUsb_Initialize(), robisz to, co tam chcesz, a na koniec wywołujesz WinUSB_Free() i CloseHandle(). Pewnie będziesz musiał użyć funkcji SetupDiEnumDeviceInterfaces(), żeby pobrać nazwę "urządzenia" USB.

Mógłbyś zaprezentować jak by miało to dokładnie wyglądało ?

0
revcorey napisał(a):

jeśli ten port usb arduino jest widziano nie jako USB a jako COM to możesz użyć QT. W qt od 5 jest serialPort. Przykłady użycia w dokumentacji a z arduino na necie.

Myślę, ze interfejs graficzny jest zbędny wiec chyba ta metoda nie będzie dobra

0

Przejrzałem internety na temat komunikacji Arduino z PC via USB. Wszystkie przykładowe kody realizują komunikację przez port COM (być może oprogramowanie od mikrokontrolera tworzy wirtualny port COM). Wygląda na to, że nie ma potrzeby używania WinUSB, wystarczy zwykłe CreateFile/WriteFile/ReadFile, ewentualnie jakaś klasa do obsługi portu szeregowego.

0
0x666 napisał(a):

Przejrzałem internety na temat komunikacji Arduino z PC via USB. Wszystkie przykładowe kody realizują komunikację przez port COM (być może oprogramowanie od mikrokontrolera tworzy wirtualny port COM). Wygląda na to, że nie ma potrzeby używania WinUSB, wystarczy zwykłe CreateFile/WriteFile/ReadFile, ewentualnie jakaś klasa do obsługi portu szeregowego.

Napisałem program który współrzędne x i y wpisuje do pliku, a na końcu wstawia 0. Jak teraz odczytać te dane w Arduino ? Dalej stoję przed problemem wysłania tych danych przez USB.

#include <iostream>
#include <fstream>
#include <string>

using namespace std;
int x = -2;
int y = 3;

int main()
{
	ofstream outFile;
	outFile.open("asd.txt");
	if (outFile.good())
	{
		outFile << x << endl;
		outFile << y << endl;
		outFile << 0 << endl;
		outFile.close();
		
	}
	else cout << "Nie można otworzyć pliku!" << endl;

	return 0;
}
0

Sprawdź w menedżerze urządzeń, czy nie masz w portach (LPT i COM) czegoś na wzór USB to COM adapter/bridge/whatever. Jeśli jest, to tam będziesz miał podane na jakim porcie COM to śmiga.

https://playground.arduino.cc/Interfacing/CPPWindows <--- tu masz przykładowy kod.

0

Czy jest ktoś w stanie wytłumaczyć mi ten kod ? Jak przerobić go, aby pasował do moich potrzeb ?

char incomingData[256] = "";			// don't forget to pre-allocate memory
	//printf("%s\n",incomingData);
	int dataLength = 255;
	int readResult = 0;

	while(SP->IsConnected())
	{
		readResult = SP->ReadData(incomingData,dataLength);
		// printf("Bytes read: (0 means no data available) %i\n",readResult);
                incomingData[readResult] = 0;

        	printf("%s",incomingData);

		Sleep(500);
	}
	return 0;
0

ehh wygląda że nie wiesz ogólnie za co się bierzesz. Z dwóch stron musisz mieć oprogramowane odpowiedzialne za odbieranie/wysyłanie danych. tzn. Na procku wysyłasz dane do odpowiednich rejestrów żeby coś wysłać do PC a także masz odbiór danych z kompa.
Na PC biblioteką/frameworkiem otwierasz port com i przyjmujesz\wysyłasz dane. Jak podałem wcześniej SerialPort z qt http://doc.qt.io/qt-5/qtserialport-index.html
Na początek najpierw sprawdź sobie czy wszystko gra po stronie procka za pomocą terminte. To taki terminal dla PC co pozwala odbierać/wysyłać dane po com.

0
revcorey napisał(a):

ehh wygląda że nie wiesz ogólnie za co się bierzesz. Z dwóch stron musisz mieć oprogramowane odpowiedzialne za odbieranie/wysyłanie danych. tzn. Na procku wysyłasz dane do odpowiednich rejestrów żeby coś wysłać do PC a także masz odbiór danych z kompa.
Na PC biblioteką/frameworkiem otwierasz port com i przyjmujesz\wysyłasz dane. Jak podałem wcześniej SerialPort z qt http://doc.qt.io/qt-5/qtserialport-index.html
Na początek najpierw sprawdź sobie czy wszystko gra po stronie procka za pomocą terminte. To taki terminal dla PC co pozwala odbierać/wysyłać dane po com.

Owszem masz rację. Moja wiedza jest uboga. Nie jestem programistą dlatego sprawia mi to duży problem. Udało mi się pewne rzeczy samemu napisać, a niestety tej nie jestem w stanie dlatego zgłasza się do takich ludzi jak Ty.

0

Udało mi się napisać program który przetwarza liczbę int na kod ACSII, który jest wpisany w poszczególne miejsca w tablicy c_string[]. Program działa tak jak chcę. Jak należy teraz go wysłać do Arduino ? Jak ma wyglądać odbiór po stronie Arduino ?

#include <iostream>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "SerialClass .h"
using namespace std;



int X = -343;
char *c_string = new char[8];


int main()
{
	Serial * arduino = new Serial("COM3");
	if (arduino->IsConnected()) cout << "Hura!Jestesmy polaczeni z Arduino" << endl;
	else cout << "Sprawdz polaczenie";
	
	if (arduino->IsConnected())
	{
		int iloscx = X;
		int acsix = X;
		int lengthCountx = 0;
		for (; iloscx != 0; iloscx /= 10, lengthCountx++);
		{
			if (X <= 0)
			{
				lengthCountx = lengthCountx + 1;
				acsix = acsix * -1;
			}
		}
		int rx = 0;
		int ix = 1;
		while (ix <= lengthCountx)
		{
			rx = acsix % 10;
			acsix = acsix / 10;
			c_string[ix] = rx + 48;
			if (X < 0)
			{
				c_string[lengthCountx] = '-';
			}
			//cout << c_string[ix] << endl;
			ix++;
		}
		arduino->WriteData(c_string, 4);
		arduino->WriteData("/", 1);
	}
}
0

Chciałbym, aby bity wysłane z PC wpisywały się w tablice w Arduino. Chciałbym również, aby dał mi informację zwrotną co dokładnie otrzymał. Napisałem program, lecz nie działa. W jaki sposób mogę monitorować co się dzieje w mikrokontrolerze ?

char x[4];

void setup() 
{
  Serial.begin(9600);
}

void loop()
{
  if(Serial.available())
  {
    int i = 1;
    if (i <= 4)
    {
      x[i] = Serial.read();
      i++;
      Serial.println(x[i]);
    }
0

Jeżeli masz tablicę 4 elementów

char x[4];

To pierwszy element znajduje się pod indeksem 0, drugi 1... ostatni pod 3.

Załóżmy że w poniższym kawałku kodu i = 2

x[i] = Serial.read(); // Wpisanie do x[2], załóżmy 32
i++; // i = 3
Serial.println(x[i]); // Pod x[3] są śmieci
0

Co ty się uparłeś z tym visualem. Najpierw testuj na jakimś terminalu typu transmite.
edit:
a widzę tu jest też print w serial. Co nie zmienia faktu najpierw użyj terminala i na żywca wklej kod
https://www.arduino.cc/reference/en/language/functions/communication/serial/print/

Tak sprawdzisz czy problem to np. druty.

0

Po wczytaniu w VS poszczególnych cyfr do tablicy na końcu pojawiają mi się jakieś dziwne znaki. Jakie mogą być przyczyny pojawienia się takiego "szumu" ? Przeanalizowałem swój soft i nie widzę miejsca w którym mogłyby dziać się takie dziwne rzeczy.

syf.png

0

Baud rate masz dobrze ustawiony?

0

Może dla ułatwienia wrzucę softy. Chciałbym wyeliminować nieznane znaki z tablicy wysyłanej oraz odbieranej. Rozumiem, że baudrate w obu urządzeniach musi być takie same ? Zmieniałem jego wartość od 9600 do 1200. Dalej widnieje problem. Styk przewodów jest dobry. Widocznie wina leży po stronie kodu dlatego go tu zamieściłem.

Kod po stronie PC:

#include <iostream>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "SerialClass .h"
using namespace std;



int X = -345;
char *c_string = new char[4](); //tworzenie wkaźnika
char output[4] = {}; //tworzenie tablicy która pobiera dane z arduino


int main()
{
	Serial * arduino = new Serial("COM3");
	if (arduino->IsConnected()) cout << "Hura!Jestesmy polaczeni z Arduino" << endl;
	else cout << "Sprawdz polaczenie"; //otwarcie komunikacji z arduino
	
	if (arduino->IsConnected()) 
	{
		int lcx = X;
		int acsix = X;
		int liczba_cyfrx = 0;
		char kodx = 0;
		int ix = 0;

		for (; lcx != 0; lcx /= 10, liczba_cyfrx++); //zliczanie ilosci cyfr w liczzbie
		if (X <= 0)
		{
			c_string[liczba_cyfrx] = { '-' };
			acsix = acsix * -1;
		}
		while (ix < liczba_cyfrx)
		{
			kodx = acsix % 10; //pobieranie ostatniej cyfry z liczby
			acsix = acsix / 10; 
			c_string[ix] = kodx + 48;  //wpisywanie do tablicy w kodzie ACSII
			ix++;
		}
		cout << c_string << endl;
		arduino->WriteData(c_string, 4);
		arduino->ReadData(output, 4);
		cout << output << endl;
          }
}

Kod po stronie Arduino:

char buff[4]={};

void setup() 
{
  Serial.begin(1200);
}

void loop()
{
  Serial.readBytes(buff, 4);
  Serial.println(buff);

}

Odpowiedź układu w takim przypadku wygląda następująco:
asd.png

0

I to dobre info, teraz wiemy ze problem jest w appce na pc. parę spraw na start. Dodaj sobie do cout jakieś info w stylu "wyświetlam c_string:". Dwa to trochę dziwnie wygląda bo wygląda że piszesz jakieś bzdury w tym c_stringu. Po co używasz cstringów w ogóle? Użyj std::String, czyli zrób z int std::String i zliczanie liczby cyfr będzie trywialne. Cały ten algorytm dziwny się uprości. Trochę jest to takie dziwne że piszesz i od razu czytasz. Ja po stronie PC zawsze używałem tu javy nie c++, i pamiętam że moje rozwiązanie było na wątkach, jeden pisał na com a drugi czytał. Nie wiem co to za biblioteka ale sprawdź w dokumentacji do niej czy nie ma jakieś ostrzeżenia o tym że piszesz na com i następnie czytasz od razu.

0

Chciałbym wyeliminować nieznane znaki z tablicy wysyłanej oraz odbieranej.

Na końcu każdego c-stringa powinno znajdować się zero. Nie ustawiasz tego zera i stąd te "nieznane znaki".

char c_string[ std::numeric_limits<int>::digits10 + 3]; // bufor musi pomieścić łańcuch i zero na końcu
...

if (X <= 0)
{
	c_string[liczba_cyfrx] = { '-' };
	c_string[liczba_cyfrx + 1] = '\0';
	acsix = acsix * -1;
}

Oczywiście łańcuch powinieneś wysłać razem z tym zerem, żeby druga strona wiedziała, kiedy jest koniec.

1 użytkowników online, w tym zalogowanych: 0, gości: 1