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

Moduł dekodera sygnału z bezprzewodowego czujnika temperatury

Limonit 10 Lut 2014 23:08 9570 10
  • Moduł dekodera sygnału z bezprzewodowego czujnika temperatury Moduł dekodera sygnału z bezprzewodowego czujnika temperatury

    Mając kilka wolnych chwil, postanowiłem skonstruować odbiornik do bezprzewodowego czujnika stacji pogodowej Oregon Scientific RMR203HG. Stacje tej marki są ogólnie dostępne, jeśli chodzi o wrażenia z użytkowania – działa bez zarzutu. Mój model wyposażony jest w pomiar temperatury i wilgotności wewnętrznej, pomiar tych samych parametrów z max 3 czujników zewnętrznych oraz zegar synchronizowany radiowo sygnałem DCF77. Dołączony czujnik posiada symbol THGN132N.

    Moduł odbiera dane z bezprzewodowego czujnika i wysyła je w sformatowanej formie poprzez UART.

    W Internecie można bez większych problemów znaleźć dokument z opisem protokołu (Oregon Scientific RF Protocol). Jest to protokół autorski. Zanim przejdę do szczegółowego opisu transmisji danych, dwa słowa o warstwie fizycznej.

    Pod względem fizycznym transmisja odbywa się w paśmie ISM 433,92MHz z modulacją OOK (zwanej niekiedy CW) - czyli bity informacji są kodowane jako impulsy emisji fali (stała częstotliwość i amplituda). Impulsy i przerwy między nimi wyznaczają dwie wartości logiczne. Do odbioru zastosowałem moduł receivera dla częstotliwości 433MHZ, który bez problemu można dostać na znanym portalu aukcyjnym lub w Chinach. Odbiornik taki posiada kilka pinów, są to zasilanie +5V, masa, wejście antenowe i wyjście danych. To ostatnie działa na prostej zasadzie. Stan wysoki jest stanem spoczynkowym i występuje, kiedy nie jest odbierany sygnał radiowy. Kiedy sygnał jest odbierany linia przyjmuje stan niski. Sygnał z tego wyjścia może być już bezpośrednio dekodowany przez mikrokontroler. Dla zasilania +5V stan wysoki na wyjściu ma nieco ponad 4V.

    Oregon stosuje 3 znane wersje protokołu, mój czujnik używa wersji v2.1 i dla takiej wersji moduł został skonstruowany. W zasadzie nie ma przeszkód, aby moduł dekodował pozostałe wersje protokołu, jednak nie dysponując takimi czujnikami, nie byłbym w stanie wykonać stosownych testów.

    Protokół w wersji 2.1 wykorzystuje kodowanie Manchester, praktycznie obowiązkowy dodatek w większości asynchronicznych protokołów radiowych. Dodatkowo każdy bit przesyłany jest dwukrotnie, przy czym za drugim razem przesyłany jest w wersji zanegowanej. Pojawia się tutaj pewna dowolność interpretacji stanów, tzn. przyjęcie za zakodowanie „1″ przejścia z 0 na 1 lub odwrotnie może zostać skompensowane przyjęciem pierwszego bitu za zanegowany lub drugiego. Dodatkowo jeszcze sygnał jest negowany przez odbiornik (wyłączone radio daje stan wysoki „1″) – zatem sama interpretacja sygnału jest dość dowolna, o ile ostatecznie na wyjściu bity przyjmą taką polaryzację, aby rezultat był poprawny. Cała ramka przesyłana jest dwukrotnie. Czujnik zaczyna nadawać około 15 sekund po włożeniu baterii, pary ramek są przesyłane następnie co około 40 sekund.





    Patrząc na poziomie wyższym, kiedy zakodowane dane zostaną złożone w ciąg zdekodowanych wartości binarnych z właściwą polaryzacją, mamy komunikat składający się z:
    1. Preambuły – 16 naprzemiennie przesyłanych 0 i 1
    2. Półbajtu (nibble) synchronizującego odpowiadającego wartości ASCII znaku ’A’
    3. Ramki danych
    4. Postambuły – dwa półbajty nieznanego przeznaczenia.

    Ramka danych różni się z zależności od czujnika. W ogólności składa się z półbajtów kodujących wartości w kodzie BCD. Dla czujnika THGN132N kolejne półbajty wyglądają tak:

    0..3 identyfikator czujnika, w tym przypadku wartość 0x1D20,

    4 numer kanał. Stacja może odbierać dane z 3 czujników na 3 różnych kanałach.

    5..6 losowy kod ustawiany po każdym resecie czujnika.

    7 znacznik słabej baterii

    8..10 Temperatura (najmniej znacząca cyfra oznacza dziesiąte części stopnia.)

    11 Znak temperatury – 0 dla dodatnich

    12..13 Wilgotność w %

    14 Nieznane przeznaczenie

    15..16 Suma kontrolna – prosta suma poprzednich półbajtów.

    17..18 Postambuła – przeznaczenie nieznane.

    Moduł został wykonany na specjalnie zaprojektowanej płytce o wymiarach 3x5cm. Znajduje się na niej oczywiście odbiornik, mikrokontroler Atmega8, stabilizator 3,3V (LD 1117A), dioda (sygnalizująca zasilanie), dwie zworki i dzielnik napięcia. W standardowym układzie układ zasilany jest napięciem 5V. Napięcie to zasila bezpośrednio odbiornik, mikrokontoroler zasilany jest z 3,3V. Wyjście odbiornika podłączone jest do mikrokontrolera poprzez dzielnik obniżający napięcie, aby było bezpieczne dla układu. Moduł posiada czteropinowe wyprowadzenie: +5V, RXD, TXD, GND – służące do zasilania oraz będące wyjściem danych w standardzie UART. Dzięki zasilaniu 3,3V układ może bez problemu działać z platformami ARM (friendlyARM, RaspberryPi). Wspomniane zworki pozwalają na przełączenie zasilania mikrokontrolera na 5V i rozłączenie dzielnika od masy umożliwiając pracę całości na 5V.

    Wyprowadzenie jest zgodne z wyprowadzeniem UART (+5V, Rxd, Txd, Gnd) na płytce mini2440 friendlyARM, aczkolwiek oczywiście nic nie stoi na przeszkodzie, aby moduł podłączyć do innych platform.

    Jak widać, istnieje również możliwość przesyłania danych do modułu – aktualnie nie jest to zaimplementowane w module, jednak w przypadku zaistnienia takiej potrzeby pozostaje tylko kwestia napisania odpowiednich procedur.

    Wyprowadzona została także większość wolnych pinów w postaci goldpinów, umożliwia to programowanie procesora w układzie i rozbudowa o inne funkcjonalności.

    Wyjście odbiornika zostało podłączone do pinu PD2 (INT0). Z powodu tak banalnego układu – nie załączam schematu.

    Krótka analiza kodu

    Działanie programu opiera się o pomiar czasu pomiędzy zmianami stanu pinu PD2. Pin ten ustawiony jest jako przerwanie zewnętrzne wyzwalane każą zmianą stanu.

    Procedura przerwania:

    Code:
    ISR(INT0_vect )
    
    {
     if bit_is_set(PIND, 2) lastbit = 1;
     else lastbit = 0;
     PORTB |= 0b00000001;
    }

    Powoduje odczyt stanu wejścia i przepisanie go do zmiennej lastbit oraz ustawienie PB0, działającego jako ICP1 i wywołanie w ten sposób obsługi przerwania przechwycenia wartości Timera1. Wejście ICP1 może zostać wyzwolone w zależności od ustawienia rejestrów zboczem rosnącym lub opadającym. W momencie, kiedy niezbędne jest wyzwolenie obydwoma zboczami można zastosować przełączanie po każdym przerwaniu zbocza wyzwalającego, albo wyzwalać je programowo (przerwanie zostanie wyzwolone również kiedy pin skonfigurowany jest jako wyjście) w sposób zastosowany w moim rozwiązaniu.

    Dekodowanie pakietu danych opiera się na budowie automatu, którego stan przechowywany jest w dedykowanej zmiennej enumeracyjnej:
    Code:
    enum global_state
    
    {
     IDLE = 0,
     PREAMBLE_DETECTED = 1,
     SYNC_AWAITING = 2,
     SYNC_RECEIVING = 3,
     DATA_RECEIVING = 4
    } STATE_DECODER;


    Stan idle jest stanem podstawowym oczekiwania na pakiet. Stan preamble_detected znalazł się w kodzie, ale ostatecznie nie jest wykorzystany. Sync_awaiting ustawiany jest po odebraniu części preambuły – 17 bitów. Sync_receiving trwa w czasie odbierania półbajtu synchronizującego. Po odebraniu półbajtu ustawiany jest data_receiving, liczone są półbajty i zapisywane do specjalnego bufora.

    Funckja retrurn_to_idle powoduje zresetowanie wszystkich liczników i stanu w momencie wykrycia nieoczekiwanej wartości w czasie dekodowania. Układ jest dzięki temu gotowy do próby odbioru kolejnej ramki:
    Code:
    void return_to_idle()
    
    {
     STATE_DECODER = IDLE;
     preamble_cntr = 0;
     bit_cntr = 0;
     bit_cntr2 = 0;
     nibble_cntr = 0;
     nibble = 0;
    }


    Pora zatem przyjrzeć się obsłudze przerwania przechwytującego Timera1:
    Code:
    ISR(TIMER1_CAPT_vect )
    
    {
    PORTB &= 0b11111110;
    period =   ICR1 - ICR1tmp;
    ICR1tmp = ICR1;

     if(period > 800 && period < 1200)
     {
      if (STATE_DECODER == IDLE ) preamble_cntr++;
      if (preamble_cntr>17 && STATE_DECODER == IDLE) {STATE_DECODER = SYNC_AWAITING; }

    if (STATE_DECODER == SYNC_RECEIVING || STATE_DECODER == DATA_RECEIVING)
      {
      bit_cntr+= 2;
      if(bit_cntr == 4)
       bit_received();
       if (bit_cntr2 == 4)
       nibble_received();
      }
     }
     else if(period > 330 && period < 600)
     {
     if (STATE_DECODER == SYNC_AWAITING) STATE_DECODER = SYNC_RECEIVING;

     if (STATE_DECODER == SYNC_RECEIVING || STATE_DECODER == DATA_RECEIVING)
     {
     bit_cntr++;
      if(bit_cntr == 4)
      {
       return_to_idle();
      }
     }
     }


    Przede wszystkim zerowana jest wartość PB0 w celu umożliwienia poprawnej obsługi kolejnego przerwania. Następnie jest obliczany czas pomiędzy zmianami stanu. Licznik pracuje z częstotliwością około 1MHz (około ponieważ pracuje na wewnętrznym oscylatorze RC), równą pracy mikrokontrolera. Zatem wartości zmiennej period odpowiadają liczbie mikrosekund. Tutaj uwaga – w kolejnej wersji programu warto zdefiniować te wartości jako stałe, uzależniając je od stałej F_CPU. dla celów szybkiego uruchomienia układu taki zapis pozostał. Przedział wartości pochodzi z faktu, że okresy odpowiadające bitom różnią się w zależności od wartości bitu (w ogólności częstotliwość transmisji wynosi 1024Hz, ale stany niskie i wysokie mają nieco inną długość). Okres wartości długiej odpowiada przesłaniu dwóch sąsiadujących ze sobą identycznych wartości, okres krótszy pojedynczej. Ponieważ jest to Manchester – inne długości trwania jednego stanu są niedopuszczalne i powodują uznanie odczytu za niepoprawny (powrót do stanu spoczynkowego).

    Poszczególne przejścia między stanami są raczej łatwe do analizy, nie będę tutaj opisywał ich szczegółowo – po wykryciu preambuły i półbitu synchronizującego układ przechodzi do odbioru danych. Wykorzystane są tutaj dwie zmienne globalne: bit_cntr – odpowiada za wyznaczenie odebranego bitu. Każdy bit danych podczas przesyłania trwa 4 krótkie okresy – czyli 4 bity występujące w niedekodowanym sygnale (bit wysyłany dwukrotnie, bit w kodowaniu Manchester kodowany jest dwoma bitami sygnału). Odebranie długiego sygnału oczywiście zwiększa licznik o 2 – sytuacja wystąpienia maks. 2 identycznych stanów po sobie jest oczywiście prawidłowa. Po odebraniu zakodowanego bitu uruchamiana jest odpowiednia funkcja opisana poniżej. Jeśli dokonana zostanie dokładniejsza analiza przebiegu sygnału, co polecam jako ćwiczenie, okaże się, że w każdym bicie danych występuje w środku długi okres. Jest to cecha, którą można wygodnie użyć do synchronizacji i określenia wartości bitu. Jednocześnie wystąpienie w tym miejscu krótkiego okresu odbierane jest jako błąd. Tutaj też uwaga – synchronizacja jest przesunięta o 1/4 bitu (1 krótki okres) do przodu z powodu, że niekoniecznie wystąpi zmiana stanu między bitami, ale zawsze w środku bitu – cecha Manchestera.

    Zmienna bit_cntr2 liczy bity w półbajcie.

    Funkcja:
    Code:
    bit_received()
    
    {
      if(lastbit)
      {
      }
      else
      {
      nibble |= 0b10000000;
      }
      nibble = nibble>>1;
      bit_cntr = 0;
      bit_cntr2++;
    }


    Jest uruchamiana po odebraniu bitu, wpisuje do zmiennej nibble odpowiednią wartość (transmisja LSB -> MSB).

    Po odebraniu kompletu bitów w półbicie:
    Code:
    nibble_received()
    
    {
     bit_cntr2 = 0;
    // USARTWriteChar('0'+ nibble_cntr);
     nibble = nibble>>3;
      if (STATE_DECODER == SYNC_RECEIVING)
      {
       if (nibble != 0x0A) return_to_idle();
       else
       STATE_DECODER = DATA_RECEIVING;

      }

      if (STATE_DECODER == DATA_RECEIVING)
      {
      message[nibble_cntr] = nibble;
      nibble_cntr++;
      if (nibble_cntr == 20) {send_temp();return_to_idle();}
      }

    }

    Wartość ta wpisywana jest w odpowiednie miejsce w buforze. W przypadku półbitu synchronizacji – sprawdzana jest jego wartość i w przypadku niezgodności następuje wyzerowanie automatu. Cały komunikat jest wysyłany przez interfejs UART osobną funkcją, której opis jest zbędny.

    W ten sposób dekodowany jest cały komunikat. Układ działa zgodnie z oczekiwaniami – pakiety odbierane są poprawnie. Praktyka wykazuje, ze ma nieco mniejszą czułość od fabrycznego odbiornika – prawdopodobnie wymaga dopracowania konstrukcji anteny. Nie zdarzają się ramki odebrane źle lub błędy sumy kontrolnej – błędy w transmisji, jeśli występują, powodują nieodebranie całej ramki.

    Sam moduł jest bazą do zastosowania w rozmaitych projektach,w których zastosowanie może mieć bezprzewodowy czujnik temperatury i wilgotności. Możliwa jest modyfikacja interfejsu, komunikacja dwustronna, rejestrowanie pomiarów. Możliwości zależą tylko od potrzeb i inwencji projektanta.

    Ten projekt, jak i inne można obejrzeć także na mojej stronie: www.elektrofanklub.pl

    W załączniku kompletny kod.

    Załączniki:

    Fajne! Ranking DIY
    Potrafisz napisać podobny artykuł? Wyślij do mnie a otrzymasz kartę SD 64GB.
  • Metal Work Pneumatic
  • Metal Work Pneumatic
  • #3 11 Lut 2014 22:46
    Limonit
    Poziom 13  

    Nadajnika nie testowałem.
    Odbiornik, jak wspomniałem jest mniej czuły niż oryginalna stacja - zasięg wynosi kilka metrów, kiedy na przeszkodzie stoją ściany. W tych modułach trzeba zastosować antenę własnej konstrukcji - myślę, że sporo od niej zależy. Być może ktoś ma jakieś rady w tym temacie.
    Podejrzewam, że w dobrych warunkach na otwartej przestrzeni zasięg nie przekroczy kilkudziesięciu metrów. Problemem w tym paśmie są spore zakłócenia od innych urządzeń tego typu.

  • #4 11 Lut 2014 23:49
    aneuro
    Poziom 16  

    Kilkanaście metrów mnie urządza i wystarczy. Myślę że w okolicy nie będzie za wiele tego typu zabawek a transmisja i tak będzie szyfrowana więc łatwo wykryć zakłocenia bo po prostu odbiornik zignoruje zakłócenia bo nie rozpozna przypadkowych sygnałów z otoczenia ;)
    Wystarczy, że nie będą non stop nadawać podobne urządzenia, a np. w przypadkowych losowych odstępach czasu tak żeby nawet kilka podobnych urządzeń na podobnej częstotliwości miało szansę przez chwilę się rozpoznać i wymienić dane...
    Sporo potencjalnych zastosowań takich małych dekoderów radiowych :arrow:

  • #5 12 Lut 2014 08:48
    Limonit
    Poziom 13  

    Jesli projektujesz od "zera" moze zainteresuj sie modulami 2,4Ghz. Zasieg powinien byc niegorszy, dodatkowo sa mniejsze i maja gotową antenę w postaci scieżkina plytce.
    Ja musialem się dostosowac do gotowej stacji.

  • #6 12 Lut 2014 11:18
    aneuro
    Poziom 16  

    Temat nowy do rozpoznania-dzięki za sugestie.
    Być może wykorzystam gotowy jakiś nadajnik z przyciskami (ten co zapodałem mi się podoba) do sterowania, a urządzenia pomiędzy sobą już mogą się komunikować w wybrany (tańszy i prostrzy sposób) i też raczej coś ala binarny kod Morse'a to będzie, no chyba że jest jakaś możliwość zaimplementować w wersji radiowej I2C ?
    Moduł dekodera sygnału z bezprzewodowego czujnika temperatury
    I2C na optozolatorach pomiędzy portem szeregowym PC a mikroprocesorami zrobiłem nie tak dawno temu i działa bezproblemowo z czujnikiem przyspieszenia i żyroskopem 3yosiowym, więc w sumie byoby fajnie I2C komunkację mieć, tyle że póĸi co nie mam pomysłu jak w wersji radiowej to zrobić, no ale w sumie powinno chyba dać radę, gdyby mieć kilka nadajników i odbiorników pracujących w 2óch pasmach?
    Wtedy wręcz wskazane by było, żeby mogły się zakłócać na tych 2 częstotliwościach, byle były w stanie coś ala ustawiać radiowe 1 i 0 na tych wybranych liniach.
    Jakieś bardzo niedrogie odbiorniki i nadaniki bitów by się przydały (bo potrzeba ich po 2a komplety na urządzenie) z możliwością dostrojenia do tej samej czętotliwości, bo producenci przypadkiem nie zmieniają jej nieznacznie, żeby zminimalizować zakłócanie się ich nawzajem?

  • #7 12 Lut 2014 16:26
    Limonit
    Poziom 13  

    I2C na zasadzie dwóch kanałów RF dla danych i zegara? No mozliwe, linia zachowywała się rzeczywiście jak OC. Tyle, że te moduły po kilka zł - podejrzewm, że są mało selektywne jesli chodzi o pasmo. Uzyłem modułu do obierania zupełnie niesparowanego z nim nadajnika - kierowałem się tylko zgodnością pasma w zakresie 433MHz - tutaj kanałów się nie ustawia, chociaż w obrębie tego pasma pewnie jakiś podział istnieje.
    Chodzi mi po głowie hasło CC1000, ale ten wymaga logiki do inicjalizacji. Podobnie moduły na 2,4GHz - bez jakiegoś, chociażby małego mikrokontrolera się nie obejdzie.
    Sprawa raczej banalna nie jest niestety.

  • #8 12 Lut 2014 19:58
    aneuro
    Poziom 16  

    Nie no mikrokontroler z I2C softwareowym to spokojne wystarczy ATTiny jakieś w DIP8 (ATTiny85 ma nawet sprzętowe wsparcie dla I2C), bo i tak urządzenie każde potrzebuje mikroprocesor tak czy inaczej do zbierania danych i obrabiania ich w czasie rzeczywistym, a od czasu do czasu zwyczajnie są odpytywane przez I2C tym sposobem nawet bezpośrednio na PC mogę teraz zbierać dane, no ale obecnie wymaga to okablowania, a fajnie byłoby pozbyć się kabli i w wersji mobilnej tylko radiowa komunikacja pozostaje.