Skocz do zawartości

Przesył struktur po serialport zawierający float: aplikacja Qt PC Win <-> Arduino


tbl4h

Pomocna odpowiedź

Problem: przesył struktury

struct myStr {
  char startFrame = '&';
  float temperature;
  float humidity;
  char stopFrame = '$';
}; 
//Windows 10, MSV17, QT greater than 4, cpp11 standard, przesył po serialPorcie, Arduino Uno.
//do aplikacji Qt główne funkcje obsługujące przesył to:
void MainWindow::readAllFromPort()
{       
    QIODevice *dev = qobject_cast<QIODevice *>(sender());
    QByteArray rawData;
    while(1) {
        if (dev->bytesAvailable()<WielkoscRamki)
            return; // nie ma dostępnej pełnej ramki, trzeba czekać na więcej danych
        dev->peek(data, WielkoscRamki); // podpatrz dostępne dane
        int i=0;
        for (;i<WielkoscRamki; ++i)
            if (data[i]==PoczatekRamki)
                break; // sprawdź lub szukaj poczatku ramki
        if (i!=0) {
            dev->read(data, i); // data to ignore
            continue;
        }
        rawData = dev->read(WielkoscRamki);
        std::memcpy(data,rawData,rawData.count());
        if (data[WielkoscRamki-1] == KoniecRamki) {
            qDebug() << "From readAllFromPort ";
            qDebug() << rawData.at(WielkoscRamki - 1);
            addStructToLogs(rawData);
        }
    }
}
// Oraz do wyswietlania struktury w oknie logów:
void MainWindow::addStructToLogs(QByteArray message){
    QString currentDateTime = QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss");
    myStr * m = reinterpret_cast<myStr *>(message.data());
    myStr * mm;
    qDebug() << "Seek raw message from addStruct\n";
    for(int i= 0;i<message.count();i++){
        qDebug() << QString::number(i) + "  " + message.at(i);
    }
    qDebug() << "End Seek from addStruct\n";
    std::memcpy(mm,message.constData(),10);
    ui->textEditLogs->append(currentDateTime + "\t" + m->startFrame + QString::number(m->temperature) + "   " + QString::number(m->humidity) + QChar::fromLatin1(m->stopFrame)); // 1
    ui->textEditLogs->append(currentDateTime + "\t" + mm->startFrame + QString::number(m->temperature) + "   " + QString::number(mm->humidity) + QChar::fromLatin1(mm->stopFrame)); // 1
    // ui->textEditLogs->append(QChar::fromLatin1(m->startFrame)); // Nie Działa
    ui->textEditLogs->append(QChar::fromLatin1(mm->startFrame));
    // ui->textEditLogs->append(currentDateTime + "\t" + m->startFrame + QString::number(m->temperature) + "   " + QString::number(m->humidity) + m->stopFrame); // Nie Działa
    // ui->textEditLogs->append(currentDateTime + "\t" + mm->startFrame + QString::number(mm->temperature) + "   " + QString::number(mm->humidity) + mm->stopFrame); // Nie Działa
}

 

Windows 10, MSV17, QT greater than 4, cpp11 standard, przesył po serialPorcie, Arduino Uno.

Obie funkcje zawierają komentarze odnośnie kodu.

Główny problem zła interpretacja floatów oraz po skopiowaniu structury przez memcpy czy zrzutowaniu przez reintepret_cast znak stopFrame jest źle interpretowany i w dodatku różnie wyświetlany przez okno logów. Próbowałem już:

Przesyłać dane za pomocą serialib.h --> nie można odnaleźć sys/time.h i konkretnie użyć getday... funkcji

Przesyłać dane floaty w postaci uni jako tablice bajtów:

union {

float f;

byte fab[4];

}

--> tutaj możliwe że odwrotna kolejność przesyłu naprawi problem, chociaż wątpię, też wolał bym uniknąć odwracania tablicy w arduino.

liczby w postaci uint8_t idą dobrze.

Edytowano przez tbl4h
Link do komentarza
Share on other sites

@tbl4h Ciężko z dostarczonego przez Ciebie wycinka kodu stwierdzić co jest problemem, ale na pewno wiele rzeczy jest niepokojących. Problemu doszukiwałbym się w:

  1. Sposobie nadawania danych po stronie odbiornika, nie wiem jak dane wysyłasz z mikrokontrolera - wysyłasz je w postaci struktury? Czy w postaci 2 charów i 2 floatów? Czy jeszcze jakoś inaczej. W zależności od tego jak te dane wysyłasz to w różnej formie mogą być one reprezentowane wewnątrz QByteArray po ich odebraniu.
  2. Zakładając że wysyłasz dane w postaci struktury (przez castowanie adresu obiektu wysyłanej struktury na unsigned char*/ uint8_t*) to problem może leżeć w różnym ułożeniu struktury (padding itp.) w zależności od architektury na której się to dzieje - inaczej pola mogą być ułożone przez kompilator na AVR a inaczej na x86 (po stronie maszyny na której działa Qt). Strzelam, że skoro nie masz upakowanej struktury po stronie aplikacji to nie masz jej też upakowanej na mikrokontrolerze - więc spróbuj po obu stronach oznaczyć tą strukturę, że ma być upakowana (inaczej to będzie na GCC a inaczej na MSVC ale to sobie już znajdziesz). Wypada też sprawdzić czy po obu stronach struktura jest taka samo upakowana - czy ma ten sam rozmiar. Ja to robię przez static_assert jak mam dostępny co najmniej C++11 -> static_assert(sizeof(MyStruct) == 10, "Struct must have size = 10);
  3. Obsłudze odebranych danych w metodzie readAllFromPort() wydrukuj zawartość rawData po odczytaniu całej ramki i zobacz czy jej zawartość w formie bajtów da się z reinterpret_castować na Twoją strukturę - ale to jest bezpośrednio powiązane z powyższym.

Jak robię taki prosty test w programie:

#include <QCoreApplication>
#include <QDebug>

struct /*__attribute__((packed))*/ myStr
{
    char startFrame = '&';
    float temperature;
    float humidity;
    char stopFrame = '$';
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    myStr dataOut;
    dataOut.humidity = 1.2345f;
    dataOut.temperature = 23.0f;

    QByteArray message;
    message.append(reinterpret_cast<const char *>(&dataOut), sizeof(myStr));
    qDebug() << message;

    const auto dataIn = reinterpret_cast<myStr *>(message.data());

    assert(dataOut.startFrame == dataIn->startFrame);
    assert(dataOut.temperature == dataIn->temperature);
    assert(dataOut.humidity == dataIn->humidity);
    assert(dataOut.stopFrame == dataIn->stopFrame);

    return 0;
}

To wszystko jest ok i asercje przechodzą. I w tym wypadku nie ma to znaczenia czy struktura będzie upakowana czy nie.

Ale ma to znaczenie w tym jak wygląda zawartość message, gdy struktura jest upakowana czy też nie:

qDebug() << message;
// nie upakowana: 	"&\x00\x00\x00\x00\x00\xB8""A\x19\x04\x9E?$\x00\x00\x00"
// upakowana: 		"&\x00\x00\xB8""A\x19\x04\x9E?$"

I żeby Twój program zaczął działać tak jak tego oczekujesz to najpierw musisz zadbać, żeby to co odczytujesz z mikrokontrolera miało jedną lub drugą formę. Najprościej jest upakować strukturę po obu stronach i zabezpieczyć się na przyszłość static_assertem. 

 

A już zupełnie poza tematem - jak widzę pętle while w kodzie który korzysta z Qt (gdzie można rzeczy robić asynchronicznie) to od razu budzi to wielkie wątpliwości - rozważ wykorzystanie bufora kołowego i czytanie danych bezpośrednio w slocie onReadyRead() i pakuj do niego (bufora) wszystkie odebrane dane, a dopiero potem analizuj bufor w poszukiwaniu ramek. Nie będziesz wykonywał niepotrzebnych operacji i np. przy okazji zawieszał UI (chyba że rzecz dzieje się w innym wątku, ale zakładam że nie).  

Edytowano przez Matthew11
poprawa źle przekopiowanego outputu dla upakowanej struktury
  • Lubię! 2
Link do komentarza
Share on other sites

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...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.