• Witaj na Forum Arduino Polska! Zapraszamy do rejestracji!
  • Znajdziesz tutaj wiele informacji na temat hardware / software.
Witaj! Logowanie Rejestracja


Ocena wątku:
  • 0 głosów - średnia: 0
  • 1
  • 2
  • 3
  • 4
  • 5
Przesyłanie float po uart
#1
Witam Państwa,
Potrzebuję wysłać wartość np. 22,5 czyli float między dwoma arduino po UART. No i o ile za pomocą ponizszych kodów daje radę przesyłać dane typu int to nie wiem jak to zrobic wlaśnie float.
W arduino wysyłajacym stosuje te instrukcje:
Kod:
void setup() {
  Serial.begin(9600);
}

void loop()
{
  Serial.write(45);
}
Natomiast w arduino odbierającym :
Kod:
int incomingByte = 0;

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

void loop() {
 
  if (Serial.available() > 0) {

    incomingByte = Serial.read();
    Serial.println(incomingByte, DEC);
  }
}
 
Odpowiedź
#2
Masz złe przykłady, to jest dobre tylko jak jeden znak..

Kiedy chcesz wysyłać kilka znaków musisz określić jego długość przypisać do tablicy i oznaczyć koniec lub początek . A odbierający dać w peli by odbierało do tablicy o ile pojawi się początek lub dopóki nie pojawi się koniec do tablicy..

Ale jak znasz długość to nie lepiej float przerobić do init??

22,5 x 10= 225-tablica- wysyłasz - odbierasz - tablica - init/10 - float
Arduino zostało wymyślone po to, by robić dobrze jedną prostą rzecz – migać diodą. 
 
Odpowiedź
#3
Pomysł z przerobieniem z folat do int to dobry patent, ale powiedz mi jak mam rozróżnić przesyłane liczby. Chodzi mi o to że chcę przesyłać co jakiś czas dwie zmienne po uart, ale w drugim arduino muszę wiedzieć która jest która.
 
Odpowiedź
#4
Przesyłać przez UART możesz na co najmniej dwa sposoby, pierwszy to tekst gdzie przesyłasz Serial.println i masz od razu informację gdzie jest koniec wiadomości - znak końca linii, lub jako ramkę bajtów, gdzie sobie sam budujesz składnię, np. dwa określone bajty na początku, potem typ danych skoro jest ich więcej (w jednym bajcie), potem 4 bajty dla floata i na koniec ewentualnie dwa bajty dla CRC16. Lepiej byłoby stale przesyłać całą paczkę danych, wtedy ramka ma stałą długość (inta też możesz przesłać jako 32bity - int32_t lub uint32_t bez znaku), można do tego wykorzystać struktury/obiekty. W nadajniku składasz sobie taką zmienną w formie tablicy bajtów (lub od razu napełniasz całą strukturę zestawem danych), w odbiorniku analogicznie. Musisz sobie też ustawić jakiś timeout, czyli mierzysz czas od początku rozpoczęcia odbioru danych, pod dwóch pierwszych bajtach decydujesz czy to Twoje dane, potem bajt jakie dane, włączasz odliczanie czasu, jeśli w określonym czasie wskaźnik nie przesunie się na ostatni element tablicy to przesuwasz go na początek i zaczynasz zbieranie danych od nowa. Jak otrzymasz wymaganą liczbę bajtów, kopiujesz całość z bufora odbiorczego do bufora zmiennej (już wiesz jakiej), ustawiasz wskaźnik w odbiorczym na 0 (tu ma być gotowy na następne dane) i liczysz CRC. Jak CRC jest OK to wykorzystujesz dane, nie to do kosza.
Przykłady z tekstem znajdziesz tu na forum, szukaj serialevent.
Z write musisz sobie posklejać wiedzę z:
https://www.arduino.cc/reference/en/lang...ial/write/
https://forum.arduino.cc/index.php?topic=311293.0
http://feriar-lab.pl/kurs-arduino-6-uart...rduino-pc/
Przykładowe struktury były tu na forum przy okazji komunikacji NRF42l01. Chyba tak byłoby najłatwiej.
 
Odpowiedź
#5
A po co timeout??

Wiedząc co się dostaje i jakie to dane wystarczy by calosc było w buforze serial i tylko odczytujemy

Bez blokady i bez Delay. Tylko jeden for
Arduino zostało wymyślone po to, by robić dobrze jedną prostą rzecz – migać diodą. 
 
Odpowiedź
#6
Na razie nie robiłem transmisji w ramkach po uart, wystarcza mi przesył tekstem. Ale tak rozważając teoretycznie zauważam kilka potencjalnych zagrożeń.
Nawet taki AVR może wykonywać instrukcję w 50ns (20MHz, dla łatwiejszego liczenia), operacje na bajtach, 8 bitach, w 8-bitowcu są szybkie i trwają jedną- kilka instrukcji. Nie można zakładać, że w buforze czeka cała gotowa ramka, bo jak się zdarzy, że wejdziesz tam tak jak się powinno to robić, czyli że pętla "jałowa" trwa kilka us, to będzie tam 1bajt, może dwa. Przesłanie bajtu z baud 9600 trwa ~1ms, 115200 z 50us. Wolno i szybko zarazem, nawet dla 115k jest to 1000 instrukcji uC. Bufor sprzętowy jest na max 2 bajty. Zakładając, że jest już "available cośtam" odczytujesz 1-2 bajty i masz śmieci, to nawet nie jest jedna liczba float. Zakładając że masz czekać na 10 bajtów może się okazać, że jakieś inne przerwanie lub inna biblioteka zakłóciła przerwania UART na tyle us, że bajt przepadł. Teraz do kolejnego resetu zawsze będziesz odczytywał 9bajtów z jednej ramki i 1 z kolejnej. No i siedział w jakiejś pętli while nie napełni bufora przez 0.5-10ms (10ms to już 200 tys. instrukcji)?
W nadajniku UART jest oczywiście podobna sytuacja, Ty sobie robisz write(dane,10bajtów) i lecisz dalej, ale to nie koniec wysyłania, dalej biblioteka już w tle ustawia sobie kolejkę, wrzuca bajt do nadajnika i czeka na przerwanie nadajnika dopóki ma w buforze dane do wysłania, przerwanie-wysyła, przerwanie-wysyła. W tym czasie może wyjść inne przerwanie i nadawanie kolejnych bajtów jest zawieszone. Przerwanie inne się kończy, wrzucany jest kolejny bajt i tak aż wyśle te 10.
Z timeout wiesz, że jak odebrałeś 9 bajtów i jest pauza powyżej 3-10ms (dla 9600), a dane wysyłasz co 50ms, to na pewno powinieneś zacząć od nowa. Jeśli sterujesz jakimś pojazdem/latawcem to też brak danych, nowej ramki oznacza utratę komunikacji, pojazd należy zatrzymać, kopterka delikatnie zawiesić i po paru s, jeśli sterownik nie dobiegnie w zasięg samemu powoli wylądować.
Nie można też tego robić na żywioł w nadajniku, że bez delay. Może być bez delay, ale co jakiś czas. Jak widać można sobie nawet 20 razy zrobić write by wysłać kolejne 10 bajtów zanim w ogóle pierwsza ramka zostanie nadana w całości. Po kilku obiegach pętli bufor nadawczy biblioteki UART zostanie przejechany zanim tyknie sekunda.
 
Odpowiedź
#7
No tak tylko on tu chce dwie zmienne wysłać i podejrzewam że raz na 200ms pewnie mu spokojnie wystarczy...
Arduino zostało wymyślone po to, by robić dobrze jedną prostą rzecz – migać diodą. 
 
Odpowiedź
#8
Hmmm... dużo informacji Smile 
Z pomocą kolegi z innego forum napisałem to tak:
Arduino nadające: 

Cytat:
Kod:
float temp_pompy = 0;
void setup()
{
Serial.begin(115200);

}

void loop()
{
temp_pompy = 22.5;
Serial.write((byte *)&temp_pompy, sizeof(temp_pompy));
delay(2000);
temp_pompy = 27.5;
Serial.write((byte *)&temp_pompy, sizeof(temp_pompy));
delay(2000);

}
oraz odbierające:

Kod:
//biblioteka obsługi LCD na i2C
#include <LiquidCrystal_I2C.h>

//ustawienie adresu LCD
LiquidCrystal_I2C lcd(0x27, 16, 2); 



//Zmienne do komunkacji UART

float temp_pompy = 0;


void setup(){
//uruchomienia komunikacji UART
Serial.begin(115200);
//Uruchomienie LCD
lcd.init();
//podświetlanie                     
lcd.backlight();

}

void loop()
{
 

lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Temp pompy: ");
lcd.print(temp_pompy);
delay(5000);



if (Serial.available() > 0)
{
Serial.readBytes((byte *)&temp_pompy, sizeof(temp_pompy));
delay(1000);
}
Niby jest ok bo na lcd mam wartość 22,5 i po chwili 27,5 potem znowu 22,5 itd. niestety tak jest tylko przez kilka minut około 3, potem wartość temp_pompy jest już na stałe 0,00. Jakieś pomysły czemu ?? 
 
Odpowiedź
#9
Naucz się używać millis() zamiast delay. No w setup to jeszcze OK, ale potem, zanim wejdziesz w loop wyczyść bufor danych, które mogły w tym czasie przylecieć, po prostu zanim wejdziesz w loop odczytaj wszystko w pętli while do jakiejś zmiennej lokalnej bajt typu śmieć. Ale to nie daje gwarancji, czy przypadkiem nie wywalisz pierwszego bajtu z paczki 4 Twojego floata.
Użyłeś gotowej funkcji Arduino odbierającej określoną liczbę bajtów. No OK, ja bym tak nie zrobił, napisałem wcześniej czemu - odbierasz pierwszy i czekasz ileś tam us/ms na kolejne bajty. Po tym delay jest do niczego niepotrzebny. W nadajniku nadajesz co określony czas, tu od biedy delay OK, ale odbiornik ma być zawsze gotowy do odbioru i obróbki tych danych.
Ja odczytywałbym tylko 1 bajt, po zebraniu 4 miałbym float'a i go używał.
Generalnie to zrealizowałeś moje obawy w swoim kodzie - nie panujesz nad tym co kiedy przylatuje i co to jest.
Jak nauczysz się używać millis to już wiedza, że nadajesz co 20ms, a w odbiorniku od 10ms nie było danych to możesz się spodziewać, że kolejny bajt będzie pierwszy załatwia sprawę. Ale nie wtedy gdy czekasz w funkcji read ?ms, bo nie zmierzysz ile czekasz. Jeden bajt (lub ile tam jest, a max 4) i powrót do loop.
 
Odpowiedź
#10
Jakoś nie mogłem wytrzymać i podaję rybę na wędce Smile

Kod nadajnika
Kod:
//Poniżej dane do UARTa programowego
#include <SoftwareSerial.h>
SoftwareSerial S1(A2, A1);//Serial nazywa się S1 (A1 i A2 to piny UARTa)



//Daklaracja struktur (łatwo się je wysyła i odbiera przez UART przez zamianę w string)

//Struktura danych do wysłania

struct Nadawanie {
  float Temp_Pompy_1;
  float Temp_Pompy_2;
  float Temp_Pompy_3;
} DaneDoWyslania;

char StringDoNadania[sizeof(DaneDoWyslania)]; //Deklaracja stringa do nadania

unsigned long ZapamietanyCzasNadawania;
unsigned long ZapamietanyCazsZmianyTemperatury;
unsigned long ZapamietanyCzasPrintowania;


void setup() {

  S1.begin(9600); // uruchomienie UARTa programowego
  Serial.begin (9600); //uruchamiamy UART aby podglądnąć dane
}

void loop()
{
  odczyt_danych (); // podprogram odczytu danych

  writeserial_S1(); // procedura wysyłania danych w "podprogramie"

  print_dane (); // skok do podprogramu aby podejżeć dane do wysłania
                 // można pominąć (zakomentować) jak wszystko OK

}

void writeserial_S1()  //--------------------------------------------------------------------------------------------
{
  if (millis() - ZapamietanyCzasNadawania > 2000UL) // Dane będą wysyłane co 2 sekundy
  {
    memcpy(StringDoNadania, &DaneDoWyslania, sizeof(DaneDoWyslania));
    S1.write(StringDoNadania, sizeof(DaneDoWyslania));
   
    ZapamietanyCzasNadawania = millis(); // tu zapamiętujemy czas kiedy wysłaliśmy dane
  }
}


void odczyt_danych ()
{
  // tu takie "cóś" abyś widział, że zmieniają się dane co 5 sekund
 
  if ( millis() -  ZapamietanyCazsZmianyTemperatury > 5000UL) // odczyt danych co 5 sekund
  {
    DaneDoWyslania.Temp_Pompy_1 = DaneDoWyslania.Temp_Pompy_1 + 1.25 ;
    DaneDoWyslania.Temp_Pompy_2 = DaneDoWyslania.Temp_Pompy_2 + 1.75 ;
    DaneDoWyslania.Temp_Pompy_3 = DaneDoWyslania.Temp_Pompy_3 + 1.50 ;
   
    ZapamietanyCazsZmianyTemperatury = millis(); //zapamiętujeny czs odczytu danych
  }
}

void print_dane () // tu podglądamy dane do wysłania co 1 sekundę
                   // można cały podprogram usunąc jak jest OK
{
  if (millis() - ZapamietanyCzasPrintowania > 1000UL) //Wyświetl dane co 1 sekundę
  {
    Serial.print ("Temperatura pompy 1  ");
    Serial.print (DaneDoWyslania.Temp_Pompy_1 , 1); //dokładność wyświetlania 1 miejsce po przecinku
    Serial.println (" *C");
    Serial.print ("Temperatura pompy 2  ");
    Serial.print (DaneDoWyslania.Temp_Pompy_2 , 2); //dokładność wyświetlania 2 miejsca po przecinku
    Serial.println (" *C");
    Serial.print ("Temperatura pompy 3  ");
    Serial.print (DaneDoWyslania.Temp_Pompy_3 , 3); //dokładność wyświetlania 3 miejsca po przecinku
    Serial.println (" *C");
    Serial.println (" ");
    ZapamietanyCzasPrintowania = millis();
  }
}

Kod odbiornika
Kod:
//Poniżej dane do UARTa programowego
#include <SoftwareSerial.h>
SoftwareSerial S1(A2, A1);//Serial nazywa się S1 (A1 i A2 to piny UARTa)



//Daklaracja struktur (łatwo się je wysyła i odbiera przez UART przez zamianę w string)

//Struktura danych odebranych

struct Odbior {
  float Temp_Pompy_1;
  float Temp_Pompy_2;
  float Temp_Pompy_3;
} DaneOdebrane;

char StringOdebrany[sizeof(DaneOdebrane)]; //Deklaracja stringa odebranego

unsigned long ZapamietanyCzasPrintowania;


void setup() {

  S1.begin(9600); // uruchomienie UARTa programowego
  Serial.begin (9600); //uruchamiamy UART aby podglądnąć dane
}

void loop()
{
  readserial_S1 ();
  print_dane ();

}

void readserial_S1 ()
{
  if (S1.available() > 0) // jak coś pojawi się w buforze odbiorczym
  {
    delay (100); // Czekamy aby wszystkie dane dotarły (czas czekania trochę przesadzony)
    if (S1.available() == sizeof(DaneOdebrane)) // gdy rozmiar danych się zgadza z przewidywanymi to
    {
      S1.readBytes(StringOdebrany, sizeof(DaneOdebrane)); //czytamy je
      memcpy(&DaneOdebrane, StringOdebrany, sizeof(DaneOdebrane)); // i pakujemy do struktury odbiorczej
    }
    else  // jak pójdzie coś nie tak to czyścimy bufor
    {
      while (S1.available() > 0)
      {
        int czyszczenie = S1.read();
      }
    }
  }
}

void print_dane () // wyświetlamy dane otrzymane
{
  if (millis() - ZapamietanyCzasPrintowania > 1000UL) //Wyświetl dane co 1 sekundę
  {
    Serial.print ("Temperatura pompy 1  ");
    Serial.print (DaneOdebrane.Temp_Pompy_1 , 1); //dokładność wyświetlania 1 miejsce po przecinku
    Serial.println (" *C");
    Serial.print ("Temperatura pompy 2  ");
    Serial.print (DaneOdebrane.Temp_Pompy_2 , 2); //dokładność wyświetlania 2 miejsca po przecinku
    Serial.println (" *C");
    Serial.print ("Temperatura pompy 3  ");
    Serial.print (DaneOdebrane.Temp_Pompy_3 , 3); //dokładność wyświetlania 3 miejsca po przecinku
    Serial.println (" *C");
    Serial.println (" ");
    ZapamietanyCzasPrintowania = millis();
  }
}

Myślę, że na podstawie tych przykładów coś się nauczysz.
Ja też uczyłem się na przykładach to nie są moje wymysły od "0"

Wyświetlanie wyników na wyświetlaczu sobie dopisz. Masz przykład jak "wyprintować" dane ze struktury, więc powinieneś sobie poradzić.
Jak zauważysz w przykładzie jest UART programowy, aby mieć wolny UART sprzętowy do podglądu. 
To jest jeden ze sposobów realizacji przesyłania danych po UART.
 
Odpowiedź
  


Skocz do:


Przeglądający: 1 gości