Skocz do zawartości
Komentator

Kurs Qt – #2 – komunikacja z Arduino przez UART

Pomocna odpowiedź

Kurs Qt – #2 – komunikacja z Arduino przez UART

Pora na kolejny artykuł omawiający podstawy Qt. Tym razem zajmiemy się komunikacją przez port szeregowy. Dzięki temu połączymy komputer PC z Arduino (lub innym mikrokontrolerem). W ramach ćwiczeń stworzymy własny monitor portu szeregowego, który będzie mógł sterować pracą Arduino.

UWAGA, to tylko wstęp! Dalsza część artykułu dostępna jest na blogu.

Przeczytaj całość »

Poniżej znajdują się komentarze powiązane z tym wpisem.

Udostępnij ten post


Link to post
Share on other sites

Bardzo fajny poradnik 🙂

Znalazłem jednak jedną rzecz, mianowicie nie usuwasz nigdzie QSerialPort. Najprościej zamienić ten fragment kodu:

//this->device = new QSerialPort;
this->device = new QSerialPort(this); //Ustawiamy rodzica obiektu

Dzięki temu w momencie usunięcia obiektu klasy MainWindow zostanie usunięty obiekt device.

pozdrawiam i czekam na kolejne części 🙂

  • Lubię! 2

Udostępnij ten post


Link to post
Share on other sites

Bardzo przydatny poradnik :D

Czekam z niecierpliwością na kolejne części!

Super robota

Pozdrawiam :)

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Bardzo cenna uwaga (y). Dla czytelników, którzy bedą to czytać - zawsze zwalniajcie poprawnie pamięć w swoich programach niezależne od sytuacji - w tym przykładzie akurat taki memory leak to jeszcze nie jest nic złego. Tworzymy obiekt QSerialPort raz i żyje on sobie do momentu aż proces naszej aplikacji się zakończy, wtedy pamięć po naszym obiekcie powinien zwolnić system (powinien jeśli to obsługuje - dyskusja na stacku). Sprawa przybrałaby bardzo nieprzyjemny obrót gdybyśmy tworzyli nowe obiekty cyklicznie i nie zwalniali po nich pamięci, a nasz program miałby pracować przez długi czas. Więc szanujmy naszą pamięć. 

Jest tutaj jeszcze jeden wysublimowany problem: gdybyśmy otworzyli port i zamknęli aplikację (wcześniej tego portu nie zamykając) to nie wywoła się destruktor obiektu QSerialPort, który między innymi ten port zamyka gdy jest otwarty. Wtedy przy ponownym uruchomieniu aplikacji moglibyśmy się bardzo zirytować niemogąc otworzyć portu ponownie (kto miał taki problem ten wie).


Poprawka powinna się niedługo pojawić na stronie.

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Skoro masz uwagi do czytelników, pozwól że ja będę miał uwagę do pisarzy. Alokowane zasoby należy zawsze zwalniać. Poleganie na systemie świadczy o słabej jakości programu i nawet jeśli nie doprowadzi do katastrofy, należałoby tego unikać.

Jak dla mnie wystarczyłoby napisać - w przykładzie pojawił się błąd, każdemu się zdarza i koniec.

Edit: Trochę przesadziłem ze złośliwością w pierwszej wersji, dokonałem autocenzury, przepraszam.

Edytowano przez Elvis

Udostępnij ten post


Link to post
Share on other sites
5 minut temu, Elvis napisał:

Alokowane zasoby należy zawsze zwalniać

To już jako offtopicowa ciekawostka - cytat z jakiekośtam podręcznika do PHP z początku tysiąclecia (niedokładny bo z pamięci):

"Nie należy wywoływać funkcji mysql_result_free, zasoby zostaną zwolnione automatycznie po wykonaniu skryptu."

Niestety - wśród starszych "programistów" tego "języka programowania" do dziś jest to chyba dogmatem 😞

 

Udostępnij ten post


Link to post
Share on other sites
17 godzin temu, erulission napisał:

Znalazłem jednak jedną rzecz, mianowicie nie usuwasz nigdzie QSerialPort. Najprościej zamienić ten fragment kodu:


//this->device = new QSerialPort;
this->device = new QSerialPort(this); //Ustawiamy rodzica obiektu

Dzięki temu w momencie usunięcia obiektu klasy MainWindow zostanie usunięty obiekt device.

Poprawka i opis do niej znajduje się już w artykule.

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

W przypadku testowania programu z Arduino Due spotkałem się z dziwnym zjawiskiem:

Przesyłanie danych do Arduino odbywa się bez przeszkód, ale nie mogę odczytać informacji z płytki. Problem rozwiązuje się po wcześniejszym uruchomieniu Arduino IDE i włączeniu i wyłączeniu w nim monitora portu szeregowego.

Sprawdzałem program na Arduino Uno i wszystko działało bez problemu. (między programami w Arduino Uno i Due zmieniałem Serial -> SerialUSB oraz dodałem dla DUE funkcję SerialEvent, bo z tego co wiem to ta płytka takowej nie posiada).

Ktoś ma może jakiś pomysł z czego to może wynikać?

Udostępnij ten post


Link to post
Share on other sites

@klarec 

Sprawdziłem właśnie u siebie korzystając z Due - wgrałem identyczny soft jak w kursie - korzystam z Serial (Serial0) i nie mam żadnych problemów z komunikacją w obie strony. 

Czy Qt otworzyło Ci port bez problemu? Możesz wykorzystać sygnał QSerialPort::errorOccurred żeby zobaczyć błąd jeśli jakiś się pojawi:

//mainwindow.h:
private slots:
	//...
    void onErrorOccurred(QSerialPort::SerialPortError error);

// mianwindow.cpp:
MainWindow::MainWindow(QWidget *parent) :
    //...
{
    //...
    connect(device, SIGNAL(errorOccurred(QSerialPort::SerialPortError)), this, SLOT(onErrorOccurred(QSerialPort::SerialPortError)));
}

void MainWindow::onErrorOccurred(QSerialPort::SerialPortError error)
{
    qDebug() << error << device->error();
}

Może sprawa dotyczy dostępu do portu? Może np. IDE otworzyło port i go nie zamknęło, wtedy ponowne otwarcie i zamknięcie portu go odblokowało? Może problem wynika z tego, że korzystasz z SerialUSB - na Serial jest to samo?

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

@klarec dla formalności dopytam - to oryginalne Arduino czy jakiś klon? 😉

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

@Matthew11 Sprawdziłem czy występują błędy Twoją metodą, błędów nie ma: QSerialPort::NoError QSerialPort::NoError.

Spróbowałem też użyć Serial, ale wtedy nie działa komunikacja w żadną stronę, nie pokazuje też w monitorze portu szeregowego.

Kod, który użyłem na Arduino (podmienione na Serial):

const uint8_t ledPin = LED_BUILTIN;
uint32_t interval = 1000;
uint32_t previousMillis = 0;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);
}

void loop() {
  uint32_t currentMillis = millis();
  if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;
    Serial.println("Czas procesora = " + String(currentMillis));
  }
  while (Serial.available()) {
    
    char state = (char)Serial.read();
    if(state == '0') {
      digitalWrite(ledPin, 0);
    } else if(state == '1') {
      digitalWrite(ledPin, 1);
    }

    Serial.println("Potwierdzam odbior. Status diody = " + String(state));
  
}
}

 

Myślę, że to też nie jest kwestia niezamknięcia portu przez Arduino IDE, testowałem program po wgraniu i resecie Arduino, na wszelki wypadek też przy wyłączonym IDE i nadal było to samo.

 

@Treker Moje Due to klon. Może faktycznie to decyduje o działaniu programu. ( klon na CH340)

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

@klarec W takim razie problemem może być CH340. Więc pozostaje Ci faktycznie opcja z natywnym Serialem - SerialUSB. 

Edytowano przez Matthew11
  • Lubię! 1
  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites

Mam problem z NucleoF446RE a mianowicie program w Qt zadziała tylko raz. Po połączeniu mogę tylko raz zapalić diode, później musze rozłączyć i połączyć i tak w kółko. Co może być problemem?? Wygląda jakbym miał zapchany port.

Terminal działa za to bez problemowo..

Obsługa w Qt oraz nucleo... Proszę o pomoc.. już nie mam siły a chciałbym się z tym dogadać i zrozumieć żeby ruszyć dalej;/

/* USER CODE BEGIN PFP */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	uint8_t Data[50]; // Tablica przechowujaca wysylana wiadomosc.
	uint16_t size = 0; // Rozmiar wysylanej wiadomosci
{

		 // Odebrany znak zostaje przekonwertowany na liczbe calkowita i sprawdzony
		 // instrukcja warunkowa
		 switch (atoi(&Received)) {

		 case 0: // Jezeli odebrany zostanie znak 0
		 size = sprintf(Data, "STOP\n\r");
		 HAL_GPIO_WritePin(D1_GPIO_Port, D1_Pin, GPIO_PIN_RESET);
		 break;

		 case 1: // Jezeli odebrany zostanie znak 1
		 size = sprintf(Data, "START\n\r");
		 HAL_GPIO_WritePin(D1_GPIO_Port, D1_Pin, GPIO_PIN_SET);
		 break;

		 }
HAL_UART_Transmit_IT(&huart2, Data, size); // Rozpoczecie nadawania danych z wykorzystaniem przerwan
 HAL_UART_Receive_IT(&huart2, &Received, 1); // Ponowne włączenie nasłuchiwania
}
}
/* USER CODE END PFP */
void MainWindow::sendMessageToDevice(QString message)
{
    if(this->device->isOpen() && this->device->isWritable())
    {
        this->addToLogs("Wysyłam informacje do urządzenia " + message);
        this->device->write(message.toStdString().c_str());
    }
    else
    {
        this->addToLogs("Nie mogę wysłać wiadomości. Port nie jest otwarty!");
    }
}
void MainWindow::on_pushButtonLedOn_clicked()
{
    this->sendMessageToDevice("1");

}

void MainWindow::on_pushButtonLedOff_clicked()
{
    this->sendMessageToDevice("0") ;
    return;
}

 

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Cześć @dernis czy fragment "Terminal działa za to bez problemowo" mam rozumieć tak, że łącząc się przez Putty lub coś podobnego z STMem wysyłając 1 lub 0 możesz sterować diodą? Co sugeruje że problem znajduje się po stronie programu w Qt. Fragmenty aplikacji, które załączyłeś wyglądają w porządku, w on_pushButtonLedOff_clicked() masz dodatkowo return; na końcu, którego nie ma w on_pushButtonLedOn_clicked() ale nie sądzę żeby to było problemem. 

Możesz też nieco przepisać kod i odsyłać to samo co wysyła PC na zasadzie echa żeby zobaczyć co dokładnie wysyła aplikacja w Qt a co terminal, żeby sprawdzić zawartość Received. Możesz też dodać default case w switchu switch (atoi(&Received)) i za komentować case 0 - na zasadzie jak 1 to zapal, a wszystko inne zgaś. Musisz dokładniej opisać zachowanie, żeby można było powiedzieć coś więcej.

Edytowano przez Matthew11
  • Lubię! 2

Udostępnij ten post


Link to post
Share on other sites

z rozpędu.. chodziło że z realterm-a wysyłając  (z polecenia send ASCII) obsługa jest oki. Returna wlepiłem bo już próbuję wszystkiego. 

Udostępnij ten post


Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Gość
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.


×
×
  • Utwórz nowe...