Skocz do zawartości

Problem z połączeniem szeregowym przez USB


Pomocna odpowiedź

Napisano

Cześć,

Próbuję napisać mały program komunikujący się z arduino po USB. niestety nie działa poprawnie i niespecjalnie jestem w stanie dojść dlaczego tak sie dzieje.

Problem polega na tym, że na wyjściu dostaje dosyć losowe dane... czasami przychodzi poprawny ciąg znaków, ale przeważnie jest to albo fragment, albo nic. dodatkowo nigdy nie zapala mi zielonej diody, tak jakby arduino nigdy nie dostał poprawnego stringa

Część c++:


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <sys/ioctl.h>


int main(int argc, char *argv[])
{
 int fd, n, i;
 char buf[64];
 struct termios toptions;


 fd = open("/dev/ttyACM0", O_RDWR | O_NOCTTY);
 printf("fd opened as %i\n", fd);

 usleep(3500000);

 tcgetattr(fd, &toptions);
 cfsetispeed(&toptions, B9600);
 cfsetospeed(&toptions, B9600);
 toptions.c_cflag &= ~PARENB;
 toptions.c_cflag &= ~CSTOPB;
 toptions.c_cflag &= ~CSIZE;
 toptions.c_cflag |= CS8;
 toptions.c_lflag |= ICANON;
 tcsetattr(fd, TCSANOW, &toptions);

 char dane[] = "greenOn\n";

 write(fd, dane, sizeof(dane));
 n = read(fd, buf, 64);
 buf[n] = 0;

 printf("%i recived: %s\n", n, buf);


 return 0;
}

a część arduino:

String odebraneDane = "";
void setup()
{
 Serial.begin(9600);
 pinMode(9, OUTPUT);
}

void loop()
{
 if (Serial.available() > 0)
 {
   odebraneDane = Serial.readStringUntil('\n');
   if (odebraneDane.equals("greenOn")){
   digitalWrite(9, HIGH);
   }
   Serial.println(odebraneDane);
   Serial.read();
 }
}

resultaty wywołań:

fd opened as 3

10 recived:

fd opened as 3

3 recived:

fd opened as 3

11 recived: gregreenOn

fd opened as 3

9 recived:

czy ktos wie może co robię źle?

Pozdrawiam

Masz w zasadzie dwa oddzielne problemy:

1) Przesyłanie danych PC-> arduino i zapalanie diody

Spróbuj za pomocą terminala wysłać string 'greenOn\n', a w kodzie Arduino dodać sobie Serial.println(odebraneDane) i zobacz co odbiera Arduino. Jakbyś miał debugger to nie trzeba by się w takie rzeczy bawić. Uroki Arduino.

2) Odbieranie danych na PC

Pierwsza rzecz jaka mi przychodzi do głowy to brak oczekiwania po wysłaniu danych - zakładając, że po wysłaniu danych z PC oczekujesz odpowiedzi układu. Teoretycznie zabezpieczasz się przed tym przez wykorzystanie zmiennej 'n'.

Spróbuj uprościć problem i na Arduino napisz program, który co np. 1000ms wysyła jakiś string, a w programie na PC w pętli nieskończonej odbierasz dane (jeśli są dostępne - funkcja ioctl).

Masz w zasadzie dwa oddzielne problemy:

1) Przesyłanie danych PC-> arduino i zapalanie diody

Spróbuj za pomocą terminala wysłać string 'greenOn\n', a w kodzie Arduino dodać sobie Serial.println(odebraneDane) i zobacz co odbiera Arduino. Jakbyś miał debugger to nie trzeba by się w takie rzeczy bawić. Uroki Arduino.

To już próbowałem, jeśli wysyłam ciąg z monitora portu w adrduinoIDE to wszystko działa super. 😋 problem pojawił się tylko w przypadku mojego programu.

2) Odbieranie danych na PC

Pierwsza rzecz jaka mi przychodzi do głowy to brak oczekiwania po wysłaniu danych - zakładając, że po wysłaniu danych z PC oczekujesz odpowiedzi układu. Teoretycznie zabezpieczasz się przed tym przez wykorzystanie zmiennej 'n'.

Spróbuj uprościć problem i na Arduino napisz program, który co np. 1000ms wysyła jakiś string, a w programie na PC w pętli nieskończonej odbierasz dane (jeśli są dostępne - funkcja ioctl).

Hmmm jeśli po prostu wysyłam stringa z arduino komputer bez problemu go odbiera. poprawnie.

Cięzko powiedzieć co się dzieje, ale wrzuciłem w pentli wysyłanie i odbieranie (z 1 sekundowym sleepem) i wyszło dysyć ciekawie...

Zdarzały się odstępstwa, ale bardzo rzadko, większość prób wyglądało tak:

fd opened as 3

Cycle 0

9 send: greenOn

3 recived:

Cycle 1

9 send: greenOn

9 recived: greenOn

Cycle 2

9 send: greenOn

10 recived:

Cycle 3

9 send: greenOn

10 recived:

Cycle 4

9 send: greenOn

10 recived:

prawie zawsze w drugiej próbie wychodziło dobrze...

Szkoda, bo mając analizator uzyskałbyś wgląd co tak naprawdę dzieje się na liniach TX i RX.

Rozważ inwestycję, bo klony Saleae są na allegro po 35zł.

pewnie będzie trzeba....

Mam coraz większe przekonanie, że to jest kwestia wysyłania danych do arduino, jeśli zamiast tego co przychodzi wysyłam z powrotem cokolwiek innego, to odbieram całkiem nieźle...

[ Dodano: 13-04-2018, 18:57 ]

No dobra, wygląda że obszedłem problem i działa... przepisałem kod na jakiś znaleziony w internecie i w ten sposób to działa. Ważne jest, żeby zamiast \n jako terminatora stringu użyć innego znaku (u mnie .) z jakiegoś powodu znak końca linii się nie przesyła 😋

#include <fstream>
#include <iostream>
#include <unistd.h>
int main()
{
   std::cout << "Opening fstream" << std::endl;
   std::fstream file("/dev/ttyACM0");
    usleep(3500000);
   std::cout << "Lit the green led" << std::endl;
   file << "greenOn." << std::endl; // endl does flush, which may be important
   std::cout << "Data Sent" << std::endl;
   std::cout << "Awaiting response" << std::endl;
   std::string response;
   file >> response;
   std::cout << "Response: " << response << std::endl;

   return 0;
}

po stronie andirino:

void setup()
{
 Serial.begin(9600);
 pinMode(9, OUTPUT);
}

void loop()
{
 if (Serial.available() > 0)
 {
   String odebraneDane = Serial.readStringUntil('.');

   if (odebraneDane.equals("greenOn")){
   digitalWrite(9, HIGH);
   Serial.println("odebraneDane");
   } else {
   Serial.println(odebraneDane);
   }
 }
}
Gość es2

Jak raz jest dobrze raz źle to potencjalnymi problemami jest:

- przepełnianie bufora

- brak ram

- przerwania

Stawiam na jedna z dwu pierwszych opcji.

es2, lepiej dzisiaj w totka nie graj.

* Przepełnienie bufora - nie bierze się znikąd, program wygląda ok

* brak ram - problemy są po stronie PC, pewnie kilka GB ramu, myślisz że taki programik wykorzysta cały?

* przerwania - program nic w przerwaniach nie robi, tzn. robi wszystko za niego system operacyjny i biblioteki Arduino

Więc proponowałbym inne typowanie:

1) trzeba doczytać jak działają funkcje read() i write() pod Linuxem. Podpowiem, że nie zapisują ani nie odczytują tylu bajtów ile im się podaje jako parametr. Mogą obsłużyć mniej i nie będzie to błędem.

2) końce linii mają wiele standardów - co najmniej 4, możliwe że sterownik dodaje \r przed \n

3) pierwszy program na arduino po odebraniu linijki "pożerał" następny bajt (Serial.read()) - więc nawet jakby dostał greenOn to G zginęło

4) zapisy w systemie Linux są buforowane i ogólnie wywołanie write() nie daje żadnej gwarancji że dane zostały zapisane na dysku, czy w urządzeniu docelowym. Port szeregowy na ogół buforuje całą linię, ale jeśli chcesz mieć pewność że wysyłasz a nie tylko buforujesz, poczytaj o funkcji sync()

Ja typuję 1+2+3, wydaje mi się że 4 przypadkiem nie jest istotne - dzięki użyciu znaku końca linii właśnie.

Może być jeszcze opcja 5 - konfiguracja portu szeregowego, ale skoro czasem działa to mało prawdopodobna opcja.

Pierwszy program na Linuxa używa błędnie operatora sizeof zamiast funkcji strlen, co powoduje przesłanie dodatkowego zera. Tajemniczy Serial.read() miał pewnie pożerać owo zero 😉

Gość es2
es2, lepiej dzisiaj w totka nie graj.

* Przepełnienie bufora - nie bierze się znikąd, program wygląda ok

* brak ram - problemy są po stronie PC, pewnie kilka GB ramu, myślisz że taki programik wykorzysta cały?

* przerwania - program nic w przerwaniach nie robi, tzn. robi wszystko za niego system operacyjny i biblioteki Arduino

Opisałem potencjalne problemy po stronie Arduino.

Problemy z programem/systemem łatwo wyeliminować i użyć terminala (np Putty, Minicom). Jeśli terminal działa poprawnie, to już wiadomo gdzie szukać.

Pierwszy program na Linuxa używa błędnie operatora sizeof zamiast funkcji strlen, co powoduje przesłanie dodatkowego zera. Tajemniczy Serial.read() miał pewnie pożerać owo zero 😉

To raczej nie ma znaczenia. Po stronie arduino i tak jest ucinane wszystko po greenOn z /n włącznie. poza tym próbowałem również ustawić ręcznie wielkość.

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...