Istnieje wiele sposobów na napisanie protokołu szeregowego w zależności od potrzebnej funkcjonalności i wymaganej kontroli błędów.
Niektóre z typowych rzeczy, które widzisz w protokołach point-to-point:
Koniec wiadomości
Najprostsze protokoły ASCII mają po prostu koniec sekwencji znaków wiadomości, często \r
lub \n
jako to, co jest drukowane po naciśnięciu klawisza Enter. Mogą być używane protokoły binarne 0x03
lub inne wspólne bajty.
Początek wiadomości
Problem z samym końcem wiadomości polega na tym, że nie wiesz, jakie inne bajty zostały już odebrane podczas wysyłania wiadomości. Te bajty byłyby następnie poprzedzone komunikatem i spowodowałyby błędną interpretację. Na przykład, jeśli Arduino właśnie obudził się ze snu, może być trochę śmieci w buforze szeregowym. Aby obejść ten problem, musisz rozpocząć sekwencję wiadomości. W twoim przykładzie ^
często w protokołach binarnych0x02
Sprawdzanie błędów
Jeśli komunikat może zostać uszkodzony, potrzebujemy kontroli błędów. Może to być suma kontrolna, błąd CRC lub coś innego.
Escape Characters
Może się zdarzyć, że suma kontrolna doda znak kontrolny, taki jak bajt „początek komunikatu” lub „koniec komunikatu”, lub wiadomość zawiera wartość równą znakowi kontrolnemu. Rozwiązaniem jest wprowadzenie postaci ucieczki. Znak zmiany znaczenia jest umieszczany przed zmodyfikowanym znakiem kontrolnym, tak aby rzeczywisty znak kontrolny nie był obecny. Np. Jeśli znakiem początkowym jest 0x02, za pomocą znaku ucieczki 0x10 możemy wysłać wartość 0x02 w komunikacie jako parę bajtów 0x10 0x12 (bajtowy znak kontrolny XOR)
Numer pakietu
Jeśli wiadomość jest uszkodzona, możemy poprosić o ponowne wysłanie wiadomości typu nack lub spróbować ponownie, ale jeśli wysłano wiele wiadomości, tylko ostatnia wiadomość może zostać wysłana ponownie. Zamiast tego pakietowi można nadać liczbę, która przewija się po określonej liczbie wiadomości. Na przykład, jeśli liczba ta wynosi 16, urządzenie nadawcze może przechowywać ostatnie 16 wysłanych wiadomości, a jeśli jakieś są uszkodzone, urządzenie odbierające może zażądać ponownego wysłania przy użyciu numeru pakietu.
Długość
Często w protokołach binarnych widzisz bajt długości, który informuje urządzenie odbierające, ile znaków jest w wiadomości. Dodaje to kolejny poziom sprawdzania błędów, tak jakby nie odebrano prawidłowej liczby bajtów, a następnie wystąpił błąd.
Specyficzne dla Arduino
Podczas opracowywania protokołu dla Arduino należy przede wszystkim zastanowić się, jak niezawodny jest kanał komunikacyjny. Jeśli wysyłasz za pośrednictwem większości bezprzewodowych mediów, XBee, WiFi itp., Wbudowane jest już sprawdzanie błędów i ponawianie prób, a zatem nie ma sensu umieszczać ich w protokole. Jeśli wysyłasz przez RS422 na kilka kilometrów, będzie to konieczne. To, co chciałbym uwzględnić, to początek i koniec znaków wiadomości, tak jak Ty. Moja typowa implementacja wygląda mniej więcej tak:
>messageType,data1,data2,…,dataN\n
Ograniczenie części danych przecinkiem pozwala na łatwą analizę, a wiadomość jest wysyłana za pomocą ASCII. Protokoły ASCII są świetne, ponieważ można wpisywać wiadomości na monitorze szeregowym.
Jeśli potrzebujesz protokołu binarnego, być może w celu zmniejszenia rozmiarów wiadomości, będziesz musiał zaimplementować funkcję zmiany znaczenia, jeśli bajt danych może być taki sam jak bajt kontrolny. Binarne znaki sterujące są lepsze w systemach, w których pożądane jest pełne spektrum sprawdzania błędów i ponownych prób. W razie potrzeby ładunek może nadal być ASCII.
Nie mam formalnej wiedzy specjalistycznej na temat protokołów szeregowych, ale korzystałem z nich kilka razy i mniej więcej ustaliłem ten schemat:
(Nagłówek pakietu) (bajt identyfikacyjny) (dane) (suma kontrolna fletcher16) (stopka pakietu)
Zazwyczaj robię nagłówek 2 bajty i bajt Stopka 1. Mój parser zrzuci wszystko, gdy zobaczy nowy nagłówek pakietu, i spróbuje parsować wiadomość, jeśli zobaczy stopkę. Jeśli suma kontrolna nie powiedzie się, komunikat nie zostanie porzucony, ale będzie kontynuowany dodawanie, dopóki nie zostanie znaleziony znak stopki i suma kontrolna się nie powiedzie. W ten sposób stopka musi mieć tylko jeden bajt, ponieważ kolizje nie zakłócają wiadomości.
Identyfikator jest dowolny, czasami z długością sekcji danych stanowiącą dolną część (4 bity). Można użyć drugiego bitu długości, ale zwykle nie przeszkadzam, ponieważ długość nie musi być znana z prawidłowego parsowania, więc widzenie odpowiedniej długości dla danego identyfikatora jest tylko większym potwierdzeniem, że komunikat był poprawny.
Suma kontrolna fletcher16 jest 2-bajtową sumą kontrolną o prawie takiej samej jakości jak CRC, ale jest znacznie łatwiejsza do wdrożenia. kilka szczegółów tutaj . Kod może być tak prosty:
Użyłem również systemu połączeń i reagowania na krytyczne wiadomości, w których komputer wysyła wiadomość co około 500 ms, dopóki nie otrzyma komunikatu OK z sumą kontrolną całej oryginalnej wiadomości jako danych (w tym oryginalnej sumy kontrolnej).
Ten schemat nie nadaje się oczywiście do wpisywania w terminalu, jak na przykład w twoim przykładzie. Twój protokół wydaje się całkiem dobry, ponieważ ogranicza się do ASCII i jestem pewien, że łatwiej jest w przypadku szybkiego projektu, który chcesz móc bezpośrednio czytać i wysyłać wiadomości. W przypadku większych projektów miło jest mieć gęstość protokołu binarnego i bezpieczeństwo sumy kontrolnej.
źródło
Jeśli jesteś w zgodzie ze standardami, możesz rzucić okiem na kodowanie ASN.1 / BER TLV. ASN.1 to język używany do opisywania struktur danych, stworzony specjalnie do komunikacji. BER jest metodą TLV kodowania danych ustrukturyzowanych za pomocą ASN.1. Problem polega na tym, że kodowanie ASN.1 może być co najwyżej trudne. Stworzenie pełnoprawnego kompilatora ASN.1 to projekt sam w sobie (i szczególnie trudny, pomyśl miesiące ).
Prawdopodobnie lepiej zachować tylko strukturę TLV. TLV zasadniczo składa się z trzech elementów: znacznika, długości i pola wartości. Znacznik określa typ danych (ciąg tekstowy, ciąg oktetów, liczba całkowita itp.) Oraz długość długości wartości .
W BER T oznacza również, czy wartość jest zbiorem samych struktur TLV (skonstruowany węzeł) czy bezpośrednio wartością (prymitywny węzeł). W ten sposób możesz utworzyć drzewo w formacie binarnym, podobnie jak XML (ale bez narzutu XML).
Przykład:
jest liczbą całkowitą (tag
02
) o długości wartości 1 (długość01
) i wartości -1 (wartośćFF
). W ASN.1 / BER liczby całkowite są znakami dużych liczb endianowych, ale oczywiście można użyć własnego formatu.jest sekwencją (listą) o długości 7 zawierającą dwie liczby całkowite, jedną o wartości -1 i jedną o wartości 255. Dwa kodowania liczb całkowitych razem tworzą wartość sekwencji.
Możesz po prostu wrzucić to również do dekodera online, czy to nie miłe?
Możesz również użyć nieokreślonej długości w BER, co pozwoli ci na przesyłanie strumieniowe danych. W takim przypadku musisz poprawnie przeanalizować drzewo. Uważam to za zaawansowany temat, na przykład musisz wiedzieć o szerokości i głębokości pierwszego parsowania.
Korzystanie ze schematu TLV pozwala zasadniczo wymyślić dowolną strukturę danych i zakodować ją. ASN.1 idzie o wiele dalej, dając unikalne identyfikatory (OID), możliwości wyboru (podobnie jak związki C), obejmuje inne struktury ASN.1 itp. Itp., Ale może to być przesada w projekcie. Prawdopodobnie najbardziej znanymi dziś strukturami zdefiniowanymi w ASN.1 są certyfikaty używane przez przeglądarkę.
źródło
Jeśli nie, znasz podstawy. Twoje polecenia mogą być tworzone i odczytywane zarówno przez ludzi, jak i maszyny, co jest dużym plusem. Możesz dodać sumę kontrolną, aby wykryć źle sformułowane polecenie lub polecenie uszkodzone podczas transportu, szczególnie jeśli Twój kanał zawiera długi kabel lub łącze radiowe.
Jeśli potrzebujesz siły przemysłowej (Twoje urządzenie nie może powodować obrażeń lub śmierci ani pozwolić, aby ktoś doznał obrażeń lub śmierci; potrzebujesz wysokich prędkości danych, odzyskiwania błędów, wykrywania brakujących pakietów itp.), Skorzystaj z niektórych standardowych protokołów i praktyk projektowych.
źródło