Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

ObjectPascal/Delphi/Lazarus - Utrata danych przy transmisji przy użyciu TComPort

dawid.barracuda 02 Lis 2016 16:20 1281 34
  • #1 02 Lis 2016 16:20
    dawid.barracuda
    Poziom 13  

    Szanowni Forumowicze,
    pracuję sobie nad przesyłaniem danych z różnych czujników za pomocą FTDI (w module MMusb232 firmy Propox) do komputera, czyli wykorzystuję wirtualny port COM. Do FTDI dane wysyła atmega32a.
    Zebrane dane odbieram i obrabiam programem pisanym w ObjectPascal w Lazarusie. Do odbioru wykorzystuję komponent TComPort z biblioteki CPortLib.

    Przechodząc do meritum - dane, które odbieram w programie nie zawsze są kompletne. Powinienem odebrać 14 bajtów, a zdarza się, że odbiorę ich mniej, albo przyjdą jakieś śmieci. Transmisja jako taka przebiega bez zakłóceń, gdyż w Realtermie widzę wszystkie dane jak należy, gdy przez TComPort część danych czasami jest gubiona. Co istotne, najczęściej dane do programu przychodzą kompletne, gubienie ich jest sporadyczne. Niepokoi mnie to jednak, gdyż Realterm pokazuje, że wszystko jest w porządku. Nie wiem teraz, czy jest to wina moja czy komponentu.

    Oto procedura jaką odbieram dane z COMa:

    Kod: delphi
    Zaloguj się, aby zobaczyć kod


    Czym takie działanie może być spowodowane? Proszę uprzejmie o wskazówki i pozdrawiam.

    0 29
  • Pomocny post
    #2 03 Lis 2016 08:47
    sq9etc
    Poziom 12  

    Nie znam tego komponentu. Ja w Delphi używam TapdCOMPort z pakietu Async Professional.
    Domyślam się, że procedura TMainWindow.SerialRxChar jest procedurą obsługi zdarzenia nadejścia danych do bufora komponentu. Jeśli tak jest, powinieneś bufor RxBuffer zadeklarować na zewnątrz tej procedury i w każdym wywołaniu TMainWindow.SerialRxChar doklejać do niego kolejne przychodzące dane. Może się zdarzyć tak, że pełna paczka danych, której oczekujesz zostanie rozbita na dwa zdarzenia i wtedy Twoje podejście może doprowadzić do tego, że pewnych danych nie odbierzesz prawidłowo.

    0
  • Pomocny post
    #3 03 Lis 2016 12:30
    arnoldziq
    Moderator Programowanie

    Dokładnie jak kolega wspomniał powyżej.
    Poza tym, dodam tylko od siebie, że trochę kolega autor tematu namieszał z różnymi typami danych.
    W zdarzeniu nadejścia znaku ASCII ( OnRxChar ) czyta kolega dane do bufora zbudowanego z tablicy byte?
    To "niewielka" niekonsekwencja :)
    Sugeruję zdecydować się na konkretny typ danych (znaki albo bufor danych) i zastosować odpowiednie procedury odczytu nadchodzących danych.
    function ReadStr(var Str: string; Count: Integer): Integer; w zdarzeniu OnRxChar lub
    function Read(var Buffer; Count: Integer): Integer; w zdarzeniu OnRxBuf

    0
  • #4 03 Lis 2016 13:39
    dawid.barracuda
    Poziom 13  

    Ja tak myślałem, że coś z tym OnRxChar jest nie tak w moim przypadku :)
    Przyznam się, że nie mogłem sobie poradzić z innym zdarzeniem, bo nie mogłem znaleźć innego przykładu, aż do dzisiaj.
    Czułem od początku, że powinienem użyć albo OnRxBuf (bo odbieram bufor danych składający się z tej samej ilości bajtów, oraz zaczynający się i kończący konkretną liczbą) albo komponentu TComDataPacket i tam zdarzenia OnPacket. I w końcu udało mi się coś odebrać używając TComDataPacket.
    Sytuacja wygląda lepiej, ale dalej zdarza się, że odbieram uszkodzoną paczkę (jest ich jednak teraz znacznie mniej).

    Oto co nakodziłem, żeby obsłużyć ten komponent:
    1) Przed otwarciem portu definiuję znaki początku i końca paczki:

    Kod: delphi
    Zaloguj się, aby zobaczyć kod


    2) Procedura odbioru OnPacket:
    Kod: delphi
    Zaloguj się, aby zobaczyć kod

    Niepokoi mnie dalej, że odbieram dane jako string, ale bez problemu przerobiłem sobie poszczególne elementy stringa na liczby. Pytanie, czy tak jest poprawnie?
    Sugerowałem się tym, że ten komponent służy do odbierania paczek danych. Spróbuję też na OnRxBuf, ale to jak wrócę do domu :)

    Używając powyższego zauważyłem istotnie zmniejszenie się zepsutych paczek, ale dzieje się inna rzecz, mianowicie niektóre paczki jakie wyświetlam sobie na ListBoxie mają przeniesione pierwsze trzy bajty na koniec. Pokazuję i objaśniam:
    1) Prawidłowy pakiet:
    StartString:=#1 ->1 107 4 139 57 42 44 85 255 58 212 63 4<-StopString:=#4, całość to 13 bajtów.
    2) Uszkodzony pakiet:
    139 57 42 44 85 255 58 212 63 4 (1 107 4) -> początek (pierwsze 3 bajty) przeniesiony na koniec.

    Co więcej, całość działa w miarę przyzwoicie jak ustawię parametr Size obiektu DataPacket na 14, a bajtów mam 13 w całej paczce.

    Czym oba powyższe przypadki mogą być spowodowane?

    0
  • Pomocny post
    #5 03 Lis 2016 13:47
    arnoldziq
    Moderator Programowanie

    dawid.barracuda napisał:
    Pytanie, czy tak jest poprawnie?
    Jak działa, to pewnie że tak :)
    dawid.barracuda napisał:
    1) Prawidłowy pakiet:
    StartString:=#1 ->1 107 4 139 57 42 44 85 255 58 212 63 4<-StopString:=#4, całość to 13 bajtów.
    Wg mnie opis "Prawidłowy pakiet" to spora nadinterpretacja.
    Dla mnie "prawidłowym", to jest właśnie ten:
    dawid.barracuda napisał:
    (1 107 4)

    Marker początku się zgadza(#1) i marker końca się zgadza (#4).
    Nie widzi kolega nic dziwnego w tych danych; #1 ->1 107 => 4 <= 139 .... ?
    Dla ułatwienia zaznaczyłem strzałeczkami :P

    0
  • #6 03 Lis 2016 14:29
    dawid.barracuda
    Poziom 13  

    Domyślam się, że chodzi o to iż w paczce pojawił się znak końca paczki. Ale czy tego problemu nie załatwia nam określenie rozmiaru w parametrze Size komponentu ComDataPacket?

    0
  • Pomocny post
    #7 03 Lis 2016 14:48
    arnoldziq
    Moderator Programowanie

    dawid.barracuda napisał:
    Ale czy tego problemu nie załatwia nam określenie rozmiaru w parametrze Size komponentu ComDataPacket?

    Załóżmy, że jesteśmy tym komponentem. Mamy dane; marker start, marker koniec i rozmiar ramki.
    1. przychodzą jakieś dane
    2. czekamy na marker start
    3. marker znaleziony, więc rozpoczynamy zapis ramki.
    4. czekamy na marker końca lub/i koniec ramki
    5. przychodzi marker końca -> zamykamy ramkę i czyścimy bufor przychodzący
    6. Zostały pobrane tylko 3 bajty -> za mało, żeby wypełnić do rozmiaru ramki
    7. zaczynamy sczytywać dane od początku
    8. przychodzi marker końca, ale nie ma markera początku -> czytamy dalej
    9. rozmiar ramki rozstał wyczerpany -> wynik, jak na przykładzie nr 2 :/


    Tego komponentu można używać na 3 sposoby:
    1. dajemy tylko marker startu i końca -> jeżeli gdzieś w środku ramki pojawi się jeden lub drugi, dane idą w cholerę :/
    2. dajemy marker początku, końca i rozmiar ramki -> w przypadku wystąpienia markera początku/końca wewnątrz ramki, dane są przesuwane (opisywany przez kolegę przypadek)
    3. dajemy tylko rozmiar pakietu danych (ramki) bez markerów -> niby najlepsze rozwiązanie, ale w przypadku "zagubienia" jednego znaku w ramce danych, wszystkie kolejne pakiety są poszatkowane :/

    Która metodą wybrać i jakie ograniczenia wprowadzić pozostawiam koledze.
    Wg mnie, metoda nr 2 jest najlepsza i najpewniejsza, ale wymaga takiego przetworzenia danych przed wysyłką, żeby się markery pojawiały tam gdzie trzeba a nie w środku pakietu.


    P.S.: Trzeba to sprawdzić, ale jest jeszcze 4 możliwość -> podanie rozmiaru ramki i tylko markera startu. W ten sposób każde x bajtów, po każdym markerze będzie traktowane jako osobna ramka. Ale jak przypadek wystąpienia markera startu wewnątrz ramki wpłynie na ten komponent, to mogę tylko zgadywać.

    0
  • #8 03 Lis 2016 16:42
    dawid.barracuda
    Poziom 13  

    Ja również wybrałbym metodę 2., ale musiałbym coś zaczarować z ramką danych i tak ustawić markery, żeby nigdy się nie pojawiły w środku ramki. Mam taki pomysł, proszę o komentarz do niego :)
    Otóż pomyślałem, żeby zapisać dane (poza markerami, czyli 11 bajtów w środku) korzystając tylko z 7 bitów na 8 dostępnych w zmiennej jednobajtowej. Wtedy na marker mogę użyć liczb w postaci 0b10000000 i większych. Jak chciałbym to zrobić? Otóż myślałem, żeby rozciągnąć te dane przesunięciem bitowym w prawo o jedną pozycję. Będę jednak musiał wtedy zwiększyć paczkę danych, bo teraz używam 11 razy 8bit czyli 88 bitów. Po rozciągnięciu wszystkiego o jeden w prawo będę potrzebował 11 bitów więcej do zapisania danych, czyli 99 bitów -> 13 bajtów. Nie mogę przecież pozwolić na utracenie LSB każdego bajtu.
    Taka jest idea, pozostaje mi napisanie kodu na uC, który przed puszczeniem danych odpowiednio je przygotuje. Co o tym Koledzy sądzą? :)

    0
  • #9 03 Lis 2016 17:43
    arnoldziq
    Moderator Programowanie

    Przepraszam, ale uważam że pomysł jest "lekko" nie trafiony :P
    Jaki sens mają wszelkiego rodzaju skomplikowane operacje "bitowe", skoro szybciej i łatwiej zakodować te dane do dwu-bajtowych liczb hexa-decymalnych? Wtedy (pomimo zwiększenia narzutu danych) mamy pełną kontrolę nad "rodzajem" wysyłanych danych a także ze znalezieniem wolnych znaków początku i końca nie powinno być problemu.

    0
  • #10 03 Lis 2016 18:10
    dawid.barracuda
    Poziom 13  

    Ok, przyjmuję z pokorą :D
    Ale chyba nie do końca rozumiem ideę. Mam scalać np. dwie dane jednobajtowe w jedną dwubajtową i tak wysyłać?

    0
  • #11 03 Lis 2016 18:35
    arnoldziq
    Moderator Programowanie

    Nie, wręcz odwrotnie :)
    Chodzi o zakodowanie każdego bajtu informacji (0-255) do 2-bajtowego ciągu hex ($00-$FF) i wysłaniu go jako ciąg znaków.
    Np.
    Dane wyjściowe : 12,127,33,0,7
    Hex : 0C,7F,21,00,07
    Wyjście jako znaki : 0,C,7,F,2,1,0,0,0,7
    Wyjście jako bajty : 48,67,55,70,50,49,48,48,55

    Plusy
    - zawsze parzysta liczba danych
    - otrzymane znaki ASCII są zawsze z zakresu 48-70 - każda inna wartość może być użyta jako marker, suma kontrolna itd.
    Minusy
    - wysyłamy 2 razy więcej danych

    0
  • #12 04 Lis 2016 00:23
    dawid.barracuda
    Poziom 13  

    Ok, teraz rozumiem :)
    Ten minus w postaci podwojonej ilości przesyłanych danych mnie trochę niepokoi i zaraz wyjaśnię czemu.
    Otóż całość mojego 'projektu' to urządzenie z czujnikami, które przesyła bezprzewodowo dane za pomocą nrf24l01+ do centrali, która to jest podłączona przez USB (wirtualnego COMa) do PC. Jednorazowo mogę puścić chyba 31 bajtów nrf-em. Więc z 13 bajtów robi mi się 26 do przesłania i mam mały zapas na rozbudowę urządzenia... Ale w sumie mogę puścić dane w postaci dziesiętnej w eter, a zamienić na ascii-hex (czyli podwoić ich ilość) jak radziłeś wyżej dopiero w centrali zaraz przed puszczeniem na RS232... więc ten minus niejako mogę tak zredukować do "plusa ujemnego", a nie pełnego minusa :D
    Może trochę zboczę z tematu, ale mam też takie pytanie - otóż nrf automatycznie kończy nadawanie jak spotka 0x00 w buforze. Jakiej sztuczki księgowej mogę użyć, żeby nie dopuścić do puszczenia zera do bufora nadawczego nrf'a? To się co prawda zdarza bardzo rzadko, że pojawi się tam zero, ale jednak może się zdarzyć. Nie mogę też dodać 1 do każdego bajtu danych, gdyż jak któraś dana będzie równa 255 to mi się sypie od razu całość, bo zmienne mam typu uint8_t.
    I jeszcze jedno - czy komponenty do obsługi COMa z pakietu Async Professional są lepsze od tych z CPortLib?

    0
  • Pomocny post
    #13 04 Lis 2016 08:18
    sq9etc
    Poziom 12  

    Jest jeszcze jeden sposób, kiedyś coś takiego implementowałem w programie, który komunikował się z pewnym modułem.
    Owszem był narzut, ale nie dwukrotny. Oczywiście w skrajnym wypadku mógł być maksymalnie dwukrotny.
    Było to tak:
    Kodowanie:
    Jeżeli dane są w zakresie 0..EF, to bierzemy je wprost.
    Jeżeli dane są z zakresu F0..FF to wstawiamy bajt specjalny o kodzie F0 i kolejny bajt, który jest negacją bajtu oryginalnego. Widać, że w przypadku bajtów z tego zakresu podwaja się nam ilość danych.

    Dekodowanie:
    Jeżeli dane są w zakresie 0..EF, to bierzemy je wprost.
    Jeżeli znajdziemy w danych F0, to go ignorujemy, natomiast bajt po nim negujemy i otrzymujemy bajt oryginalny.

    W tym podejściu widać, że bajty specjalne są z zakresu F0..FF.
    U Ciebie jest inaczej, ale ten sposób "kodowania" da się przystosować. Możesz jako bajty specjalne traktować zakres 0..F, a jako sygnalizator bajt F.

    0
  • #14 04 Lis 2016 11:56
    dawid.barracuda
    Poziom 13  

    Negując wartości większe od 0xEF nigdy nie zbliżę się do 0xFF więc czy są jakieś przeszkody żeby dodać offset = 1 do każdego elementu przed wysyłką danych nrf-em?

    Dodano po 1 [godziny] 18 [minuty]:

    Myślę cały czas nad tym zerem no i bolą mnie jednak te miejsca charakterystyczne. Bo co będzie jak trafię bajt 0xEF i zwiększę go o 1? Dostanę 0xF0 czyli wlecę w zakres, gdzie neguję bity. Samo w sobie to jeszcze nie jest najgorsze, bo negując drugi raz i obejmując 1 dostanę oryginał ale co wtedy ze znakiem specjalnym?

    0
  • #15 04 Lis 2016 12:09
    arnoldziq
    Moderator Programowanie

    Prostota, Panowie, prostota !
    Jest bardzo dużo metod kodowania, dekodowania, itd, itp. Problem polega na tym, że każda z nich ma swoje plusy i minusy. Trzeba wybrać taką metodę, która ma więcej plusów niż minusów.
    Ale jak już wspomniałem, żadne operacje bitowe, nie są w stanie zagwarantować, że dowolny wybrany marker nie powtórzy się wewnątrz danych.
    Oczywiście wybór należy do autora projektu.

    0
  • Pomocny post
    #16 04 Lis 2016 12:40
    sq9etc
    Poziom 12  

    dawid.barracuda napisał:
    Negując wartości większe od 0xEF nigdy nie zbliżę się do 0xFF więc czy są jakieś przeszkody żeby dodać offset = 1 do każdego elementu przed wysyłką danych nrf-em?

    Tak jest w moim przypadku.

    W Twoim cała treść zawiera się między znakami #01 (SOH) i #04 (EOT), więc musisz zapewnić żeby nie wystąpiły one we właściwych danych.
    Za sygnalizator przyjmujesz bajt #0F.
    Wszystko powyżej (#10.. #FF) puszczasz bez zmian.
    Wszystko od #00 do #0F konwertujesz w następujący sposób:
    Wstawiasz bajt #0F i dodatkowo drugi bajt jak poniżej:
    BajtZanegowany = Negacja(BajtOryginalny)

    Tak wygląda kodowanie. Natomiast w dekodowaniu robisz odwrotnie.
    Wszystko powyżej (#10.. #FF) puszczasz bez zmian.
    Gdy w danych znajdziesz #0F, to go ignorujesz, natomiast bajt występujący po nim negujesz i otrzymujesz bajt oryginalny:
    BajtOryginalny = Negacja(BajtZanegowany )

    Przykład:
    Gdy kodujesz, to jeżeli w danych będzie bajt #01, to po tej operacji zostanie on zastąpiony przez dwa bajty:
    #0F,#FE.

    Przy dekodowaniu #0F służy nam jako sygnalizator, więc go odrzucamy, a #FE negujemy otrzymując #01.

    0
  • Pomocny post
    #17 04 Lis 2016 12:55
    willyvmm
    Poziom 26  

    Zdefiniuj sobie ramkę np:

    Nagłówek:
    1 -do 4 magic byte.
    1 bajt rozmiar. (moze być wiecej)

    Twoje dane w ilosci okreslonej poprzednio.

    Ew. Suma kontrolna. (polecam uzyc)

    Proste. Maly znany i staly narzut. Masz dodatkowo możliwość kontroli poprawnosci danych. Nie jesteś na sztywno zwiazany z pakietem danych, możesz zdefiniować timeout i odbirerac dane z kilku pakietów nrf i skleic je w ramke.
    Sprawdzone w wielu zastosowaniach.

    Keep it simle.

    0
  • Pomocny post
    #18 04 Lis 2016 21:57
    krru
    Poziom 32  

    arnoldziq napisał:
    dawid.barracuda napisał:
    Pytanie, czy tak jest poprawnie?
    Jak działa, to pewnie że tak :)


    W przypadku oprogramowania to jest nieprawda - jedno- czy kilku-krotne poprawne zadziałanie programu o niczym takim nie świadczy.
    W szczególności transmisja danych i protokoły to dziedzina, w której trzeba starannie wszystko zaprojektować, bo trudno przetestować wszystkie przypadki, a mamy dwa komputery, które o sobie wiedzą tyle ile prześlą. Do tego błędy transmisji, czasy przesłania danych (nie zawsze połączenie szeregowe jest bezpośrednie, może iść przez modem, przez tunel po IP itp). Chociażby fakt, że nie zawsze pierwsze przychodzące dane to początek ramki - strona nadająca mogła zacząć nadawać zanim strona odbierająca zaczęła słuchać.

    0
  • #19 04 Lis 2016 22:37
    dawid.barracuda
    Poziom 13  

    sq9etc czemu kodujemy od #00 do #0F a nie do #04? Resztę algorytmu rozumiem :)
    willyvmm - mówisz o tym, żeby nrfem przesyłać dane w kilku pakietach a nie koniecznie w jednym tzn. włączyć nadawanie kilka razy a ramkę sklejać przed puszczeniem do PC?
    krru - pierwszy raz zajmuję się na poważnie transmisją danych i tworzeniem jakichś własnych (co prawda prostych) protokołów, a muszę to zrobić dobrze, więc każda opinia od Was na ten temat jest dla mnie niezwykle istotna :)

    0
  • Pomocny post
    #20 04 Lis 2016 23:47
    willyvmm
    Poziom 26  

    Mniej-więcej.
    Zdefiniuj sobie protokół, ramkę danych itd co tam potrzebujesz i co pasuje do twoich danych. Nie narzucaj sobie ograniczeń których nie ma.

    0
  • #21 05 Lis 2016 00:06
    dawid.barracuda
    Poziom 13  

    willyvmm - piszesz o sumie kontrolnej. Wyciągam ją z czujników jeśli tylko mają taką mozliwosc. Ale czy Tobie chodzi o stworzenie sumy kontrolnej do mojej całej paczki danych?

    0
  • Pomocny post
    #22 05 Lis 2016 02:43
    rb401
    Poziom 33  

    W kwestii pakietowania danych np. do RS232 stosunkowo niedawno pojawił się ciekawy algorytm:

    Consistent Overhead Byte Stuffing

    wyróżniającym się prostotą i stałym narzutem 1 bajtu dla pakietów mniejszych od 254B (plus 1 bajt oddzielający pakiety 0x00), zupełnie niezależnie od pierwotnej zawartości. I wynikającą z tego wszystkiego łatwą detekcją początku/końca pakietu.

    Moim zdaniem algorytm warty zainteresowania, w świetle proponowanych wcześniej tutaj metod.

    dawid.barracuda napisał:
    Może trochę zboczę z tematu, ale mam też takie pytanie - otóż nrf automatycznie kończy nadawanie jak spotka 0x00 w buforze.


    Wydaje mi się że przy algorytmie COBS ta właściwość akurat bardzo się przyda. W pakietach zakodowanych COBS jest gwarantowane że nigdy nie będzie bajtów zerowych a zero jako znacznik końca pakietu skończy nadawanie i o to chodzi (a to że to zero jest nadawane to bardzo dobrze bo druga strona wie że pakiet się skończył).

    0
  • #23 05 Lis 2016 13:20
    dawid.barracuda
    Poziom 13  

    rb401 - dziękuję serdecznie za tę wskazówkę :) Artykuł przeczytałem, algorytm rozumiem. W istocie załatwia mi dwa problemy:
    1) Nie martwię się transmisją zera nrf-em, a wręcz wykorzystuję tę cechę jako zaletę.
    2) Znak końca ramki - doklejam sobie to zero w centrali na koniec ramki (bo nrf zero mi utnie) i wykorzystuję zero jako StopString:=#0 w TComDataPacket.

    Zostaje jedna kwestia - znak początku ramki StartString. Wiadomo, że muszę wybrać jakąś daną, która nie pojawi mi się w paczce danych. Wobec tego, czy mogę użyć za StartString tego samego co za StopString czyli #0? Czy wtedy komponent głupieje, bo nie rozróżnia początku od końca?

    I jak jest z tą sumą kontrolną? Jak pisałem, wyciągam sumy kontrolne z czujników jeśli mają taką opcję i na tej podstawie liczę CRC, ale czy można zrobić taki numer własnego autorstwa do całej mojej ramki danych i sprawdzać jej poprawność? Choć może powinienem zadać pytanie 'jak to zrobić' bo nie chce mi się wierzyć, że na to sposobu nie ma :)

    0
  • Pomocny post
    #24 05 Lis 2016 15:52
    rb401
    Poziom 33  

    dawid.barracuda napisał:
    Wobec tego, czy mogę użyć za StartString tego samego co za StopString czyli #0? Czy wtedy komponent głupieje, bo nie rozróżnia początku od końca?


    Możliwe.
    Ale zaglądnąłem do źródeł TComDataPacket i widzę tam że specjalnie jest obsługiwany wariant gdy StartString jest zdefiniowany jako pusty string. Wtedy wszystko co przychodzi jest traktowane jako zawartość pakietu aż do wystąpienia StopString z jego konsekwencjami. Więc może tą drogą pójść. Może taka właśnie sytuacja kiedy znaki początku i końca są takie same, jest uwzględniona w filozofii TComDataPacket.
    Ale osobiście tego nie sprawdzałem.

    Generalnie jednak mam pewne wątpliwości czy ten TComDataPacket jest w ogóle tu potrzebny. Dekodowanie COBS, które i tak trzeba zrobić, można by zrobić na bieżąco w prostej procedurce przetwarzającej przychodzące dane znak po znaku i odpowiednio rozpoznając ledwo kilka występujących w tym algorytmie stanów (start, stop, kopiowanie niezerowych danych na wyjście, odbiór bajtu "zerowego").
    Przyjmując założenie że jednostkowy przesył będzie nie większy niż 254B algorytm się znacznie upraszcza względem podanego w przykładach np. w Wikipedii.

    dawid.barracuda napisał:
    I jak jest z tą sumą kontrolną? Jak pisałem, wyciągam sumy kontrolne z czujników jeśli mają taką opcję i na tej podstawie liczę CRC


    Liczysz powiedzmy CRC i doklejasz do danych ale koniecznie robisz to przed zakodowaniem COBS. A po drugiej stronie najpierw dekodujesz COBS a później sobie sprawdzasz sumy.
    Tak że nie widzę jakiegokolwiek problemu.

    Co do obsługi pakietów COBS to mam jeszcze taki pomysł. Nie używasz TComDataPacket, a w TComPort ustawiasz EventChar rowny #0 (zresztą tak jest domyślnie).
    I piszesz tylko obsługę zdarzenia OnRxFlag. Jeśli w buforze będzie więcej znaków niż to zero to dekodujesz zawartość bufora odbiorczego algorytmem COBS i masz w tym momencie kompletny odkodowany pakiet (wtedy wywołujesz jego obsługę) albo rozpoznaną sytuację np. jakiś nieczytelnych śmieci lub powtórzonych zer i wtedy nic nie robisz.
    Tak będzie chyba najprościej. Bufor jest duży (u mnie domyślnie ma 1kB) tak że nic się nie zgubi.

    0
  • #25 07 Lis 2016 10:21
    sq9etc
    Poziom 12  

    dawid.barracuda napisał:
    sq9etc czemu kodujemy od #00 do #0F a nie do #04? Resztę algorytmu rozumiem


    Tak na wszelki wypadek. W przyszłości może się okazać, że potrzeba jednak więcej bajtów zarezerwowanych.

    0
  • #26 08 Lis 2016 22:35
    dawid.barracuda
    Poziom 13  

    Rozumiem.
    Zacząłem się zastanawiać nad pewną rzeczą. Mam np. funkcję:
    Read(var Buffer; Count: Integer): Integer;

    O ile za var Buffer wiem, że podstawiam swój bufor, to w sumie nie wiem o co chodzi z Count. Zawsze to wstawiałem na zasadzie "bo tak było na przykładzie", ale co ten parametr dokładnie oznacza?

    0
  • Pomocny post
    #27 08 Lis 2016 22:59
    rb401
    Poziom 33  

    dawid.barracuda napisał:
    "bo tak było na przykładzie", ale co ten parametr dokładnie oznacza?


    Do tego komponentu jest nawet help. Ja też z początku myślałem że nie ma i trzeba po źródłach patrzeć.
    Jeśli jesteś w Windows a przez Lazarusa nie ma dojścia do niego, to tam gdzie go doinstalowałeś jest folder help (podwójny?) i tam jest plik .hlp.

    O metodzie Read jest np.:


    Cytat:
    Reads data from input buffer.

    function Read(var Buffer; Count: Integer): Integer;

    Description
    Call Read function to read Count bytes into Buffer variable. The function does not return until Count bytes are read or timeout elapses.

    The return value is the number of bytes that are actually read.

    Note
    Buffer variable must be large enough to hold Count bytes.


    i chyba trudno mi w tej chwili coś dodać od siebie.

    0
  • #28 08 Lis 2016 23:23
    dawid.barracuda
    Poziom 13  

    No dobra, to wiele wyjaśnia.
    Napisałem taką procedurę wg Twoich wskazówek:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    Odbieram tak pojedynczo bajty. Czy może zrobić tak, że za Count wstawiam ilość bajtów w paczce? Sposób powyżej powoduje bardzo wolny odbiór danych.

    Dodano po 4 [minuty]:

    Dodam, że przy takim odbiorze nie zauważyłem ubytków w odebranych danych.

    Dodano po 9 [minuty]:

    Rozwinąłem procedurę do takiej:
    Kod: c
    Zaloguj się, aby zobaczyć kod


    Nie zauważyłem głupot w odbiorze no i w tempie pojawiania się poprzednio pojedynczych znaków mam teraz cale odebrane stringi. Czy o takie rozwiązanie Tobie chodziło czy może o jeszcze co innego?
    Mimo to odnoszę wrażenie, że całość i tak działa wolniej od Realterma, a nie widzę powodu, dla którego tak miałoby być. Spowolniam czymś swój program?

    0
  • #29 09 Lis 2016 00:15
    rb401
    Poziom 33  

    dawid.barracuda napisał:
    Czy o takie rozwiązanie Tobie chodziło czy może o jeszcze co innego?


    Chodzi mi o to (na razie rozważmy prosty, niepowikłany przypadek), że pojawia się zdarzenie OnRxFlag, które dla nas jest sygnałem że przyszedł kompletny pakiet COBS.
    W obsłudze możemy sprawdzić metodą InputCount ile mamy w buforze wejściowym. Jeśli mamy tyle ile wynosi przyjęta długość pakietu, plus to kończące zero to jest ok (choć tu nie musimy zakładać jakieś długości pakietu bo wychodzi to i tak z metody dekodowania). I w tym momencie już możemy dekodować COBS. Albo czytając znak po znaku i odpowiednio interpretując informację zapisujemy zdekodowany pakiet w jakimś globalnym buforze, albo czytając hurtem wszystko z bufora wejściowego do jakiegoś tymczasowego metodą Read z parametrem Count odczytanym metodą InputCount i wtedy dekodujemy sobie z naszego bufora (tak chyba będzie szybciej).
    Jak dojdziemy w dekodowaniu do kończącego zera, to albo ustawiamy jakąś globalną flagę, że w naszym buforze jest pakiet do obrobienia, albo przed wyjściem z obsługi OnRxFlag robimy całą obsługę (np. wyświetlenie).


    Ale to jest sytuacja gdy założymy że pakiety przychodzą pojedynczo z jakimś tam wyraźnym odstępem czasowym jeden od drugiego.
    Mam pewne (niezweryfikowane) obawy, że jeśli pakiety będą przysyłane szybko, "na styk", to w momencie obsługi zdarzenia OnRxFlag w buforze może się znaleźć jeszcze trochę znaków po tym zerze. I w takim wypadku trzeba by trochę staranniej do tego podejść.

    Niestety nie mam w tej chwili czasu na własne eksperymenty, choć ten akurat temat mnie szczególnie interesuje, ze względu na to że w kolejce mam taki projekt akurat gdzie takie niezawodne pakietowanie będzie mi potrzebne. I też akurat na styku Lazarus plus system embedded.


    Co do czytania pakietu jako łańcucha znakowego, to mam jakieś przeczucie że w standardzie COBS zostało wybrane zero jako separator, całkowicie świadomie (bo może to być każda inna liczba). Dlatego że w C/C++ może być wygodnie to obsłużone standardowymi funkcjami dotyczącymi łańcuchów znakowych).

    dawid.barracuda napisał:

    Memo1.Text:=Memo1.Text+RevS + #13#10;


    wydaje mi się że efektywniejsze będzie:
    Memo1.Lines.Add(RevS);
    Po za tym w pierwszym podejściu za każdym znakiem miałeś przerysowanie komponentu Memo1. Może właśnie dlatego tak wolno było a samo Read po jednym znaku nie było temu winne.

    0
  • #30 02 Gru 2016 20:54
    dawid.barracuda
    Poziom 13  

    Zająłem się COBSem i wyniki - póki co - wydają się być satysfakcjonujące.
    Przed wysyłką koduję dane kodem, który jest podany w artykule na Wiki. I wszystko pięknie leci nrf'em, nie ma żadnych zer oprócz tego kończącego pakiet. Wobec tego paczka, którą odbiera nrf obsługiwany przez procesor przy PC jest uboższa o to zero. W związku z tym do PC wysyłam odebraną paczkę i zero zaraz za nią.
    Ma to uważam swoje plusy - paczka danych odbierana w programie musi mieć zawsze stałą liczbę znaków i zero na końcu. Skonfigurowałem więc komponent DataPacket w taki sposób, że StartString jest ustawiony na '0', co oznacza, że program reaguje na cokolwiek jako początek pakietu. Jako EndString dałem '#0', czyli znak końca pakietu. Do tego ustawiłem rozmiar pakietu (w moim przypadku 16). I przy takim ustawieniu jak na razie wszystko śmiga, pakietów nie gubi, a przynajmniej jak na razie tego nie zauważyłem. Musiałem jedynie przerobić funkcję dekodującą pakiet COBS. Zrobiłem to tak:

    Kod: delphi
    Zaloguj się, aby zobaczyć kod


    Chciałem to zrobić w postaci funkcji, ale Delphi nie chce mi skompilować programu, w którym parametrami funkcji są wskaźniki. Szukałem informacji na ten temat, ale nic nie znalazłem. Jest to w ogóle możliwe w tym języku?

    0
  Szukaj w 5mln produktów