Skocz do zawartości

[MicroPython v1.23.0, ESP32] Parsowanie sygnałów 433 MHz


Pomocna odpowiedź

(edytowany)
Dnia 18.09.2024 o 11:47, ethanak napisał:

A co do zerknięcia w kody - nie pisałem tego żeby się chwalić tylko żeby pokazać, co można zdziałać z Arduino IDE i ESP32. A można duuuużo więcej...

W porządku, nie odebrałem tego tak. Na kodzie napisanym przez innych też się człowiek uczy, także spoko jest.

Odnośnie tematu trochę grzebnąłem. Poznaję go od dupy strony i domysłów, zamiast jak człowiek gdzieś o tym poczytać. Aktualnie mam brzydki kod w uPy, który działa i zbiera sygnały, następnie wyłuskuje najczęściej powtarzające się, więc nadajnikiem nie odsyłam całego otrzymanego bufora, tylko ciut cwaniej.

Posiadam 3 piloty do różnych gniazdek na "433 MHz" i jak się okazuje, we wszystkich "działające" kody mają długość 50-ciu "sygnałów" (może raczej bitów?), przy czym ostatni "bit" jest najdłuższy (2252 uS) i zdaje się być "pomijalny", wygląda jak pauza. Końcowym "bitem" jest więc zawsze 0 (tu sygnał o długości 310 uS).

[316,1137,1058,400,1050,400,1054,404,1048,406,1048,404,305,1147,306,1139,297,1139,313,1139,1054,400,311,1141,311,1141,309,1138,314,1141,1052,410,303,1139,1056,398,307,1141,1054,402,1051,404,1048,1143,304,1141,310,1147,310,2252]

Powyższe w zapisie HEX wygląda tak: 6A A5 59 56 66 B5 00

No i prosty kod "mielący" czasy sygnałów na zapis HEX:

def bity_na_int(lista_bitow):
    x = 0
    for bit in lista_bitow:
        x = x * 2 + bit
    return x

# testowo przyjmuję jedynie listy z ilością sygnałów (bitów) == 50
def sygnaly_na_hex(sygnaly):
    if len(sygnaly) != 50:
        return None
    sygnaly.pop() # usuwam ostatni sygnał, kończący ramkę? 
    prog = sum(sygnaly)/len(sygnaly) # średnia dla sygnałów
    bity = [1 if s > prog else 0 for s in sygnaly] # zamieniam sygnały na bity bazując na progu
    hex_lista = [("%.2X " % bity_na_int(bity[i : i + 8])) for i in range(0, len(bity), 8)] # tworzę liste hex-ów
    return "".join(hex_lista) # lista -> str
        
if __name__ == "__main__":
    
    # długość trwania sygnałów w uS
    s = [[316,1137,1058,400,1050,400,1054,404,1048,406,1048,404,305,1147,306,1139,297,1139,313,1139,1054,400,311,1141,311,1141,309,1138,314,1141,1052,410,303,1139,1056,398,307,1141,1054,402,1051,404,1048,1143,304,1141,310,1147,310,2252],
         [316,1137,1058,400,1050,400,1054,397,1048,406,1048,404,305,1147,306,1139,297,1139,313,1139,1054,400,311,1141,311,1141,309,1138,314,1141,1052,1047,303,1139,1056,398,307,1141,1054,402,1051,404,1048,1143,304,1141,310,1147,310,2252],
         [1220,384,1217,386,401,1201,1216,1207,400,1202,1216,1195,1215,383,1213,389,397,1205,1213,390,1212,391,1211,392,395,1206,397,1204,1214,389,396,1205,398,401,399,1202,1209,1202,401,1200,1218,386,1216,388,1214,389,1213,386,397,12331]]
    
    for i in s:
        print(sygnaly_na_hex(i))

 

Edytowano przez orb777
(edytowany)

Zmieniłem odbiornik "MX-RM-5V" na superheterodynę z racji kiepskiego zasięgu i dziwnego zachowania, tj. losowego "przeskakiwania" ze stanu wysokiego na niski lub na odwrót. Do wstępnych testów użyłem "RX500" (niby "QIACHIP") i "RXB6 v2.0" (znanej i cenionej marki "no-name"). Oba odbiorniki 433 MHz posiadają znaczny "szum" na pinie wyjściowym, który w superheterodynach jest <podobno> zjawiskiem dość normalnym, a przynajmniej tak mówią internety. Znalazłem też wpis, w którym człowiek wypowiadał się, że u niego w RXB6 nie ma szumu, w czasie "nasłuchiwania" wyjście jest czyste. Tylko nie wiem której wersji to dotyczyło, chyba wspomniał o płytce z  kwarcem oznaczonym jako "433 MHz". U mnie w RXB6 jest kwarc "13.52127". Pewnie nie ma to większego znaczenia i są równoznaczne, chociaż na forach zagranicznych spotkałem się z komentarzami, że "te z napisem 433 MHz działają lepiej". RX500 posiada kwarc "6.7458", a w nadajniku SYN115 jest "EAS13.650".

Antenę do SYN115 zrobiłem z kawałka drutu (ze skrętki), w tym przypadku długość 173 mm (można to wyliczyć). W RX500 jest fabrycznie zamontowana "sprężynka" w izolacji. Do RXB6 z ciekawości dorobiłem coś takiego, też ze skrętki. Odległość sprawdzałem na max kilka metrów, ale wszystko jest wpięte na płytce stykowej, więc raczej miarodajne nie będzie. Jednak w porównaniu do "MX-RM-5V" (też z drutem 173 mm) jest dużo lepiej.

A teraz szum. W "MX-RM-5V" wyjście na pinie DATA w trakcie "nasłuchiwania" jest w zasadzie "czyste" z drobnymi anomaliami, pomijając dziwne zmiany stanów. W obu wspomnianych superheterodynach wygląda mniej więcej tak, czyli po prostu jak "jesień średniowiecza":

szum.thumb.jpg.54a195ac25b5fb965d39fabc9421c675.jpg

Moduły natomiast różnią się "czasem ciszy" (długością trwania stanu niskiego) po odebraniu sygnałów z pilotów. RXB6 "milknie" bardzo różnie: 150-300-500-800 ms. RX500 z tego co zauważyłem przeważnie na ~38 ms (stąd "filtr" w testowym kodzie ustawiłem na max 30000us = 30 ms). Zmieniłem też sposób odbioru sygnałów, z przerwań na pętlę. Kod z przerwaniami nieco poprawiłem, ale i tak gubił czasem majtki, na liście były zdublowane stany. Tu jest w miarę ok. Powstał również prosty "filtr", który o dziwo daje radę z szumem odbiorników. Bez pośpiechu poprawiam kod do parsowania, który ogólnie jest ulepem, po poprawkach ulepem pozostanie, ale może mniej zamotanym. Wiem, że to krótki temat, "szybka piłka" (itd.), przeznaczony bardziej na arduino, ale ciekaw jestem, czy powolne wolisko zwane micropythonem da sobie z nim radę.

import esp32, machine, time, gc

# sprawdź czy wartość mieści się w tolerancji procentowej +/-
@micropython.native
def tolerancja(poprz, akt, ile_procent):
    return abs(poprz - akt) <= poprz * (ile_procent / 100)

# prosty filtr
@micropython.native
def filtr(pin, max_probek = 5, czas_sygn_min = 2000, czas_sygn_max = 30000, procent_delta = 0.8, procent_czas = 0.8):
    licz = delta_poprz = start_poprz = 0; stan = pin.value()
    while licz < max_probek:
        czas_teraz = time.ticks_us()
        while pin.value() == stan:
            pass
        delta_teraz = time.ticks_diff(time.ticks_us(), czas_teraz)
        stan = pin.value()
        if stan == 1 and czas_sygn_min < delta_teraz < czas_sygn_max: # jeżeli stan == 1, to wcześniej było 0
            licz = (licz + 1) if (tolerancja(delta_poprz, delta_teraz, procent_delta) and tolerancja(start_poprz, czas_teraz, procent_czas)) else 0
            delta_poprz, start_poprz = delta_teraz, czas_teraz
        elif delta_teraz > czas_sygn_max: # wyjscie odbiornika milknie na dłuższą chwilę, sygnał został odebrany
            break
    return licz == max_probek # jeżeli licznik osiągnął max_próbek, wtedy zwracam True, inaczej False

# odbiór sygnałów
@micropython.native
def sygnaly(pin, max_buff = 1000, czas_sygn_max = 30000):
    buff = []; czas_delta = 0
    pierwszy_stan = stan = pin.value()
    while czas_delta < czas_sygn_max and max_buff > 0:
        czas_teraz = time.ticks_us()
        while pin.value() == stan:
            pass
        czas_delta = time.ticks_diff(time.ticks_us(), czas_teraz)
        stan = pin.value()
        buff.append(czas_delta)
        max_buff -= 1
    return (None if max_buff != 0 else (buff, bool(pierwszy_stan)))

def nadajnik(pin, sygnaly, powtorz = 2, spij_ms = 300):
    pin.write_pulses(sygnaly[0] * powtorz, sygnaly[1])
    time.sleep_ms(spij_ms)

# funkcja główna - wysyła otrzymany bufor sygnałów
# odbiornik: superheterodyna 433 Mhz: "RX500" i "RXB6 v2.0" (w obu szum na wyjściu)
# nadajnik: SYN115
# anteny: drut 173 mm
if __name__ == "__main__":
    pin_odbiornik = machine.Pin(27, machine.Pin.IN)
    pin_nadajnik = esp32.RMT(0, pin = machine.Pin(23, machine.Pin.OUT), clock_div = 80, idle_level = False)
    
    print("\n[INFO]: Naciśnij i przytrzymaj przycisk pilota ...", end = "")
    while True:
        gc.collect() # zbieram śmieci
        if filtr(pin_odbiornik):
            ret = sygnaly(pin_odbiornik)
            if ret != None:
                input("\n[INFO]: Naciśnij przeciwny przycisk na pilocie, następnie ENTER aby wysłać kod ...")
                nadajnik(pin_nadajnik, ret)
                print("[INFO]: Kod wysłany.\n\nNaciśnij i przytrzymaj przycisk pilota ...", end = "")
        time.sleep_ms(500) # heterodyno uspokój się

 

Edytowano przez orb777
  • Lubię! 1

- obserwuje temat z zainteresowaniem, ponieważ jak odczytywałem TPMS (czujnik ciśnienia w oponie) w hulajnodze to przeszedłem podobną drogę.

- CC1101, RXB6, Flipper Zero na razie skończyło się na odbiorniku RTL-SDR wersja 3, wersja 4 za droga dla mnie.

- obecnie mój temat TPMS  ma niższy priorytet, powodzenia.

(edytowany)
4 godziny temu, 99teki napisał:

jak odczytywałem TPMS (czujnik ciśnienia w oponie) w hulajnodze to przeszedłem podobną drogę.

@99teki , czy jest na forum zapis tej drogi?

 

4 godziny temu, 99teki napisał:

na razie skończyło się na odbiorniku RTL-SDR wersja 3, wersja 4 za droga dla mnie

Też myślę o v4, również o "SDRplay RSP1B". Na pewno pomocne urządzenie przy takim dłubaniu.

Edytowano przez orb777
  • Lubię! 1

- niestety nie ma zapisu moich prac na tym i innych forach, miało zająć kilka godzin a zajeło za dużo.

 no i się troche nastroiłem do tematu.

(edytowany)

@99teki , czaję. Domyślam się, że u siebie masz obsługę protokołów, żeby poprawnie interpretować dane.

Też coś dłubię, ale ostatnio średnio z weną. Doszedłem jednak do wniosku, że "pauzy" wysyłane przez piloty nie są końcem sygnału (jak wcześniej interpretowałem), tylko jego początkiem. Przy nadawaniu np. 10 takich samych ramek za kolejnością nie ma większego znaczenia, czy pauzę każdorazowo wyślemy przed czy po. Jednak odbierając i grupując ramki można zauważyć, że interpretacja "pauzy" jako "po" daje błędne wyniki w grupowaniu. Dotyczy to szczególnie pilotów, które przeplatają różne kody.

Właściwy sposób interpretowania sygnałów (prawdopodobnie) wygląda więc tak:

[pauza][ramka]...[pauza][ramka]... itd.

 

 

pauza.jpg

Edytowano przez orb777
  • Lubię! 1

Ulepiłem na szybko prosty "prototyp pilota kopiującego". Nie ciąłem drucików, więc kolory są różniste/niedopasowane. Kondensatory, rezystory dałem jakie znalazłem, ale działają ok. Nie stosuję wewnętrznych rezystorów PULL_DOWN/PULL_UP z ESP32. Zobaczę, czy ruszy z "WebREPL". Jeżeli będzie gryzło się, wtedy dołożę dodatkowy AMS1117-3.3 dla stabilności modułów 433 MHz. Być może lepszy będzie mały OLED zamiast "WebREPL" przez wifi (zakłócenia, spadki napięcia na wbudowanym stabilizatorze czy cuś). Cały czas uczę się, więc błędy poganiam błędami.

pilot.thumb.jpg.bab8e1fada081fe79577da2958c62d95.jpg

 

- ESP32-WROOM-32D z chip-em: ESP32-D0WD-V3 (revision v3.0)
- SYN115 (nadajnik), RXB6 v2.0 (odbiornik)

- 2x tact switch z czapeczkami

- 4x kondensator elektrolityczny 10uF (63V); 2x kondensator ceramiczny 100nF (oznaczenie "104")

- 2x rezystor 5kΩ "PULL_UP" (przy przyciskach); 2x rezystor 10kΩ "PULL_DOWN" (przy GND, obok elektrolitów)


Nic odkrywczego, ale z ciekawości sprawdziłem jak zachowują się kondensatory "filtrujące" przy przyciskach, a także co dzieje się bez nich. Ostatni zrzut to "rozciągnięte" w czasie (do 100us) zbocze opadające na pinie 25, widać tam dokładniej "drgania":

Przycisk_tact_switch.thumb.png.36f1ea69dccf9abb13df3e1ffb0bad05.png

 

Powstał też prosty kod w micropythonie do przetestowania przycisków:

import machine, time

# używam zewnętrznych rezystorów PULL_DOWN i PULL_UP
on = machine.Pin(26, machine.Pin.IN) 
off = machine.Pin(25, machine.Pin.IN)

a, b = on.value(), off.value()
licz_a = licz_b = 1

while True:
    if a != on.value():
        a = on.value()
        print("> ON (%.4i): %i" % (licz_a, a))
        licz_a += 1    
    if b != off.value():
        b = off.value()
        print("> OFF(%.4i): %i" % (licz_b, b))
        licz_b += 1
    time.sleep_ms(5)

 

  • Lubię! 2
(edytowany)

Tryb WebREPL powodował zwisy, więc dodałem AMS1117-3.3 do zasilania nadajnika/odbiornika 433 MHz. Na pinie 5V jest ~4.8V, więc AMS ma wystarczającą różnicę 1.5V pomiędzy wyjściem/wejściem. Z 3.3V na płytce korzystają jedynie przyciski.

Testowałem przez 3 stropy z cegły (zbrojone), nie w linii prostej, czyli łącznie ~8m z przeszkodami (ściany, meble, urządzenia, itd). Gniazdka miałem w piwnicy, a ulepiony "pilot" był ~8m wyżej, zasilany przez powerbank. W okolicy rutery wifi, ale WebREPL działał całkiem nieźle (smartfon z termux-em). Wstępne kopiowanie kodu przez odbiornik 433 MHz gubiło majtki dosyć rzadko, ale powinno być zrobione na minimalną odległość, więc wynik wydaje się być akceptowalny.

Ogólnie test odległości "RXB6" z kodem roboczym wypadł lepiej niż myślałem. Z "MX-RM-5V" raczej nie uzyskałbym takiego zasięgu, toto ledwo łapało sygnał z pilota w granicach tego samego pokoju. Z "RX500" nie sprawdzałem. Sygnały na większe odległości są czasem zniekształcone czasowo, więc zamiast np. dwóch kodów widocznych jest więcej. Ostatecznie użytkownik ma sprawdzić (jednorazowo) który jest poprawny.

termux.png

pilot_ams1117-3.3.jpg

Edytowano przez orb777
  • Lubię! 1
(edytowany)

Piloty, które mam wysyłają przeważnie kody 24-bitowe (48 stanów "niski:wysoki" ze stosunkiem czasu trwania 1:3), natomiast "nadgorliwy Orno" przeplata dodatkowo kodami 32-bitowymi (64 stany), które "nic nie robią" z przekaźnikiem w gniazdku. Jednak wszystkie kody zakończone są bitem "zero", co wygląda np. tak:

pauza + 24(32) bity "właściwe" + bit "zero" (kończący, oznaczony na żółto)

bity.thumb.jpg.ffe979087a78ae3d3d8198395542cb17.jpg

 

Nie do końca rozumiem dlaczego kod nie kończy się na 24/32 bitach, tylko dodawany jest "bit zero" zaraz przed pauzą nowego sygnału. Szukawszy trochę po internetach, ale spotkawszy się z podobną uwagą, czyli "nie wiadomo - tak ma być". Może jest znacznikiem końca ramki? Bez wysłania "bitu zero" na końcu gniazdko nie reaguje. Nie mam jak tego dokładniej sprawdzić, ponieważ odbiornik dostraja się dopiero po chwili, nie pokazuje co pilot wysłał z samego początku. Sdr-a na razie nie posiadam.

Edytowano przez orb777
(edytowany)

@99teki, a jak sprawuje się rtl-sdr v3 przy odbiorze sygnałów 433/868 MHz? W komentarzu yt spotkałem się z informacją, że v4 w porównaniu do v3 jest "głucha" na częstotliwościach powyżej 100 MHz.

Pewnych rzeczy heterodyną i analizatorem stanów logicznych niestety nie uchwycę.

Edytowano przez orb777

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