Przesyłanie danych webSocketem

0

Cześć, mam problem z przesyłaniem i konwertowaniem danych. Przy wartościach powyżej 128 nie dostaję odpowiednich wyników.

void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length)
{
    //Serial.println("wiadomość dotarła");
    //Serial.print(webSocket.connectedClients());
    // String str = (char*)payload;
    // Serial.println(str);
    if (type == WStype_TEXT) {
        
        //Serial.println(brightness);
        String str = (char*)payload;
        Serial.println(str);
        if (str[0] == 'T') {
            int r = (int)payload[1];
            int g = (int)payload[2];
            int b = (int)payload[3];
            Serial.println(payload[1]);
            Serial.println(payload[2]);
            Serial.println(payload[3]);

Jest to część funkcji którą wykorzystuję. Przesyłam 3 wartości od 0 do 255 jako uint8 ale z jakiegoś powodu są one inne.

s ="T" + chr(color[0]) + chr(color[1]) + chr(color[2])
    print(s)
    ws.send(s)

powyżej część kodu w pythonie która odpowiada za wysyłanie danych

Przykładowe dane:
python:

(228, 227, 228)
ä ã ä

co odebrał c++:

äãä //zrzutowany do klasy String i wyświetlony przesłany komunikat
195 //kolejne odebrane wartości
164
195
0

Problem w konwersji payload na char.
Payload jest unsigned intem który przechowuje wartości 0-255 a char przechowuje wartości -127-128 stąd powyżej 128 się wykrzacza. Zmien na unsigned char

0

@Czitels:

char r = (unsigned char)payload[1];
            char g = (unsigned char)payload[2];
            char b = (unsigned char)payload[3];
            Serial.println((int)r);
            Serial.println((int)g);
            Serial.println((int)b);

Dalej dostaję te same błędy

1

Czy używasz jakieś biblioteki implementującej protokół WebSocket, czy sam to robisz?
Jeśli robisz to sam, to problem powyżej 128 bajtów musi wynikać z twojej złej implementacji protokołu.
Zależnie jaki jest rozmiar danych do wysłania informacja o rozmiarze zakodowana jest winnej ilości bajtów. Z tego co pamiętam pierwszą granicą jest właśnie 128.
Radzę doczytać RFC-6455.

Payload length: 7 bits, 7+16 bits, or 7+64 bits

The length of the "Payload data", in bytes: if 0-125, that is the
payload length. If 126, the following 2 bytes interpreted as a
16-bit unsigned integer are the payload length. If 127, the
following 8 bytes interpreted as a 64-bit unsigned integer (the
most significant bit MUST be 0) are the payload length. Multibyte
length quantities are expressed in network byte order. Note that
in all cases, the minimal number of bytes MUST be used to encode
the length, for example, the length of a 124-byte-long string
can't be encoded as the sequence 126, 0, 124. The payload length
is the length of the "Extension data" + the length of the
"Application data". The length of the "Extension data" may be
zero, in which case the payload length is the length of the
"Application data".

Jeśli piszesz własną implementację WebSocket, to zainteresuj się automatycznym oficjalnym zestawem testów protokołu WebSocket Autobahn.
Łatwe do skonfigurowania, a będziesz miał pewność, co jest już poprawnie zaimplementowane.

0

@MarekR22:
Używam webSocketServer do ESP32. W pythonie rzutuje wartości używając chr i dostaje znaki odpowiadające danym numerom ascii. Jak wysyłam to do cpp i zrzutuje sobie payload do stringa to znaki się zgadzają. Dopiero po zrzutuwaniu na inta lub samym wyświetleniu payloadu dostaje inne wartości.

1

Przepraszam zupełnie nie zrozumiałem w czym problem.
Ogarnąłem się i teraz już widzę co jet źle.

Są dwa problemy (drugi ma 3 składowe):

  • złe użycie websocket. Próbujesz przesłać dane binarne jako tekst
    Ten kod python jest bezsensu:
s ="T" + chr(color[0]) + chr(color[1]) + chr(color[2])

Mały eksperyment w konsoli python pokazuje jaka sieczka się robi:

>>> color = [233, 211, 129]
>>> s ="T" + chr(color[0]) + chr(color[1]) + chr(color[2])
>>> s
'TéÓ\x81'
>>> data = s.encode(encoding = 'UTF-8', errors = 'strict')
>>> data
b'T\xc3\xa9\xc3\x93\xc2\x81'
>>>
>>> type(data)
<class 'bytes'>
>>>

Zwróć uwagę, że websocket przy wysyłaniu tekstu wymaga, by kodowanie napisu było w UTF-8.
Efekt jest taki, że wiadomość, w postaci bajtów wydłuża ci się i zmienia w nieoczekiwany sposób.
Jeśli jako tekst będziesz próbował przesłać coś co nie będzie poprawnym UTF-8 druga strona powinna odrzucić połączanie z odpowiednim błędem.

  • złe przetwarzanie odebranego napisu w C++
    if (type == WStype_TEXT) {

        //Serial.println(brightness);
        String str = (char*)payload;
        Serial.println(str);
        if (str[0] == 'T') {
            int r = (int)payload[1];
            int g = (int)payload[2];
            int b = (int)payload[3];
  1. String str = (char*)payload; nie masz gwarancji zera kończącego, co moze prowadzić do bufffer overflow, czyli tu jest Undefined Behavior.
  2. Jak już wspomniałem dane otrzymujesz zakodowane w UTF-8, ergo znaki z poza ASCII są kodowane więcej niż jednym bajtem.
  3. Nawet jeśli kodowanie UTF-8 nie było by problemem i dane zawierałby oczekiwaną wartość, to masz błąd: int r = (int)payload[1];.
    payload[1] jest typu char, a dokładniej signed char chyli przy konwersji do int wartości powyżej 127 będą traktowane jako ujemne, a przy kolorach to raczej oczekujesz zakresu 0-255
    Żeby naprawić tą część powinno być:
int r = static_cast<unsigned char>(payload[1]);

unsigned char już automatycznie będzie promowany do int.

Ergo po stronie pythona skonstruuj bytes o pożądanej zawartości i to je wyślij (wysyłaj dane w postaci binarnej, a nie tekstowej).
Po stronie C++ odbieraj dane binarne i uważaj na konwersję char do int!

0

Ok już działa.

message = bytearray()
    flaga = 'T'
    message.append(84)
    message.append(color[0])
    message.append(color[1])
    message.append(color[2])

Chciałem uniknąć pracy z bajtami, nie miałem do tego jakoś nigdy głowy.
W c++ dodałem static casty. Konwersję do Stringa zostawię bo przydaje mi się do odbierania w wygodny sposób komend od innych clientów.
Dzięki wielkie za pomoc

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