logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Implementacja wysyłania sygnałów IR do sterowania klimatyzacją - format raw - krok po kroku

p.kaczmarek2 04 Lip 2024 21:50 1653 3
REKLAMA
MediaMarkt Black Week
  • Implementacja wysyłania sygnałów IR do sterowania klimatyzacją - format raw - krok po kroku
    Opiszę tutaj proces implementacji wysyłania sygnału IR w oparciu o PWM i timer. Wysłany tak z mikrokontrolera sygnał będzie w stanie kontrolować klimatyzację. Dodatkowo użyty format sygnału będzie zgodny z tym co przechwytuje Flipper Zero, co pozwoli na wykorzystanie przechwyconych w ten sposób sygnałów do wysyłania ich z mojej platformy. Temat zrealizuję w oparciu o MCU na którym robię swój projekt, ale myślę, że pokazane tu kroki będą analogiczne też na innych platformach.

    Plan działania
    W poprzednim temacie przyjrzeliśmy się sygnałowi IR przechwyconemu przez Flipper Zero:
    Budowa sygnału IR, przechwytywanie w Flipper Zero, zapis w formacie raw
    Wiemy już zatem, że potrzebne będzie:
    - PWM, w tym przypadku 38kHz z możliwością regulacji wypełnienia
    - jakaś forma timera aby móc odczekać dany czas by włączyć/wyłączyć PWM
    Przypominając, tak wygląda format który chcemy wysłać:
    
    name: Power
    type: raw
    frequency: 38000
    duty_cycle: 0.330000
    data: 2204 708 834 1342 882 1344 837 632 836 1392 836 615 723 738 836 633 861 684 835 658 838 710 835 657 838 656 837 641 723 762 838 1397 727 101537 2257 704 761 1417 884 1342 762 737 731 1467 836 612 828 634 760 707 787 760 835 658 761 786 835 658 761 733 836 637 827 660 763 1473 726
    

    Oznacza to, że musimy włączyć PWM 38kHz z wypełnieniem 33%, zostawić je włączone przez 2204 us, potem je wyłączyć i odczekać 708 us, potem je znów włączyć, itd.
    A więc musimy mieć jakąś tablicę do której wpiszemy te wartości czasów i będziemy wedle nich operować.
    Trzeba jakoś odczekiwać te czasy. Widzę tu dwie opcje:
    - z każdym czasem na nowo ustawiać timer na daną wartość, po upływie której nastąpi przerwanie
    - na sztywno dobrać sobie jakąś jednostkę precyzji timera
    Dla uproszczenie wybrałem drugi sposób. Przy odpowiednio małym okresie przerwania błędy będą pomijalne. Patrząc na te wartości czasów uznałem, że timer będę wywoływać co 50 us.


    Uruchomienie timera
    A więc uruchamiamy timer. Wygląda to inaczej zależnie od platformy. Implementowałem to na BK7231, więc dokumentacji praktycznie nie było. Musiałem sam wyszukać po chińskim SDK co i jak. Oto uruchomienie timera:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Dodatkowo w celu sprawdzenia czy timer działa postanowiłem "pomachać" jakimś GPIO. Ustawienie pinu w tryb wyjścia:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    No i nasze ISR - Interrupt Service Routine - obsługa przerwania od timera:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Wgrywamy, testujemy:
    Implementacja wysyłania sygnałów IR do sterowania klimatyzacją - format raw - krok po kroku
    Rzeczywiście, wszystko się zgadza. Całość można by jeszcze znacznie zoptymalizować poprzez zamianę obsługi pinu przez funkcję na bezpośrednie operacje na rejestrach, ale na razie uruchamiamy tylko timer, więc to pominę.

    Uruchomienie PWM
    Analogicznie, teraz musimy uruchomić PWM. Wszystko na razie jako osobny przykład. U mnie na BK7231 wygląda to tak:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    26000000 to częstotliwość zegara MCU, czyli 26MHz. Dzielimy ją na 38kHz, by otrzymać wartość okresu PWM. Potem wypełnienie ustalamy na połowę okresu - 50%. Działa:
    Implementacja wysyłania sygnałów IR do sterowania klimatyzacją - format raw - krok po kroku

    Uruchomienie kolejki czasów
    Teraz trzeba zastanowić się jak reprezentować będziemy dane raw w pamięci.
    Zróbmy to najprościej jak się da - bufor kołowy można wprowadzić później. Utwórzmy tablicę o stałym rozmiarze:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Teraz trzeba pamiętać co jest następne w kolejce do wysłania - powiedzmy, że będzie to wskaźnik na dany element w tablicy. Gdy jest zerem, to oznacza, że wszystko wysłaliśmy.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Trzeba też wiedzieć gdzie się zatrzymać - wprowadźmy po prostu wskaźnik stopu.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Teraz jeszcze ustalmy okres timera oraz zmienną zliczającą czas który upłynął. Przy okazji musimy pamiętać stan (wysyłamy PWM lub nie), by móc go zamieniać na odwrotny po upływie czasu:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    A teraz nasz ISR, jednakże na początek bez PWM - tylko machający pinem:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Powyższy kod zlicza ile upłynęło czasu, porównuje czas który upłynął z następnym progiem i jeśli próg został pokonany, to kod go "przeskakuje" i od bieżącego czasu odejmuje jego wartość (by nie akumulować błędu).

    Do tego też dodałem parsing komend w formacie:
    
    Send 100,500,300,500,100
    

    Ten temat nie dotyczy parsowania tekstów ASCII do bufora, ale i tak wspomnę, że użyłem strtok - kod do wglądu:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Ważne tutaj tylko by pamiętać, by nie zacząć wysyłania danych przedwcześnie, w trakcie parsowania. Dlatego wskaźnik "cur" ustawiam na pierwszy element tablicy na końcu, już po parsingu.

    Testujemy - wysyłamy "dane":

    Implementacja wysyłania sygnałów IR do sterowania klimatyzacją - format raw - krok po kroku
    Implementacja wysyłania sygnałów IR do sterowania klimatyzacją - format raw - krok po kroku
    Wszystko się zgadza. Stan wysoki jest około 100us, potem niski 500us i znów wysoki 300us. To jest to, co podałem w pliku.


    PWM wkracza do akcji
    Skoro machanie pinem wedle czasów już działa, to można podpiąć PWM. Zasadniczo tylko zmieniamy kilka linijek. W zależności od stanu (wysyłanie lub nie) ustawiamy odpowiednie wypełnienie (choć równie dobrze można by wyłączać całe PWM...):
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Pora to przetestować. U mnie w środowisku wszystko jest opakowane w komendy tekstowe, które wyglądają mniej więcej tak:
    
    // start the driver
    startDriver IR2
    // start timer 50us
    // arguments: duty_on_fraction, duty_off_fraction
    StartTimer 50 0.5 0
    // send data
    Send 3500, 1500, 500, 1000, 1000, 500,
    

    Przykładowy rezultat (zrzut ekranu z Sigrok):
    Implementacja wysyłania sygnałów IR do sterowania klimatyzacją - format raw - krok po kroku
    Po powiększeniu widać PWM (tutaj z oscyloskopu):
    Implementacja wysyłania sygnałów IR do sterowania klimatyzacją - format raw - krok po kroku
    Inny przykład:
    
    // start the driver
    startDriver IR2
    // start timer 50us
    // arguments: duty_on_fraction, duty_off_fraction, pin for sending (optional)
    StartTimer 50 0.5 0
    // send data
    Send 3200,1300,950,500,900,1300,900,550,900,650,900
    

    Rezultat - wygląda dobrze.
    Implementacja wysyłania sygnałów IR do sterowania klimatyzacją - format raw - krok po kroku
    Można by już testować z klimatyzacją, ale jeszcze moment...

    Drobne optymalizacje
    Tu można by zakończyć - ale czy na pewno?
    Pamiętajmy, że liczy się tu czas i precyzja, a funkcje biblioteczne nie zawsze są optymalne.
    Rozważmy jak aktualizujemy PWM:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Wygląda niewinnie? Ale co kryje się pod maską?
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    To dopiero wywołuje kolejne funkcje... czym jest sddev_control?
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    No no, nie wygląda to zbyt wydajnie.
    Przede wszystkim, tu jest iteracja i porównanie... łańcuchów znaków? Czym jest PWM_DEV_NAME?
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Tak, to są napisy.
    A jak wygląda ta tablica, którą iterują?
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    To dość długie. I bez tablicy hashującej? Nie no, tak nie może być, to trzeba zoptymalizować.
    Zobaczmy, gdzie trafia ostatecznie wywołanie.
    Gdzie jest użyte CMD_PWM_SINGLE_UPDATA_PARAM?
    Dopiero w PWM jest wielki switch z wieloma case:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    A to ostatecznie wywołuje:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Podsumowując, widzę tu kilka rzeczy do poprawki:
    - po pierwsze, można bezpośrednio w sterowniku IR pisać po rejestrach zamiast wołać te wiele funkcji jak w SDK
    - po drugie, te zmienne można pobierać raz i trzymać potem w pamięci:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    - po trzecie, jak zmieniam tylko pierwszy duty_cycle, to pozostałe REG_WRITE można pominąć:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    - po czwarte, jak wiemy że group będzie stałe, to co robi REG_GROUP_PWM0_T1_ADDR? To makro;
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Tę finalną wartość adresu też można policzyć raz i potem trzymać w pamięci...

    Większość tych optymalizacji wykonałem, resztę będę dodawać. Kto chce, ten znajdzie na moim repozytorium kod: https://github.com/openshwprojects/OpenBK7231T_App

    Końcowy test
    Do końcowego testu posłużyła nam klimatyzacja Galanz AUS-12HR53FA2/AS-12HR53F2F:
    Implementacja wysyłania sygnałów IR do sterowania klimatyzacją - format raw - krok po kroku
    Klimatyzacja ta nie ma WiFi, ale ma posiada pilot obsługujący wystarczającą ilość funkcji do wygodnego użytkowania:
    Implementacja wysyłania sygnałów IR do sterowania klimatyzacją - format raw - krok po kroku
    Przechwyciliśmy Flipperem Zero komendę ON:
    
    Filetype: IR signals file
    Version: 1
    # 
    name: Galanz_on
    type: raw
    frequency: 38000
    duty_cycle: 0.330000
    data: 3665 1547 605 1078 527 1151 507 498 605 415 584 415 584 1098 585 415 507 501 522 1152 507 1175 585 417 528 1154 506 497 502 523 579 1081 524 1160 550 493 584 1080 603 1077 503 502 600 416 583 1105 578 417 582 419 504 1178 582 445 477 503 599 416 583 416 506 500 524 499 579 417 506 492 507 523 579 417 582 415 508 496 553 451 601 416 583 421 501 500 549 468 584 1104 579 418 581 417 506 1176 583 416 507 503 546 1129 528 1156 504 499 550 450 601 417 583 418 505 523 526 469 583 416 507 1176 583 1101 582 417 582 418 505 500 601 417 582 419 504 1176 583 418 506 1179 580 1102 581 416 586 419 502 523 578 418 581 418 505 500 525 494 582 417 506 495 505 502 547 468 582 422 502 499 524 500 526 469 581 420 504 501 549 467 582 418 506 501 575 468 582 418 506 498 502 502 548 468 582 420 504 500 550 467 532 467 507 496 528 494 532 465 534 468 506 500 550 468 531 470 531 1177 531 1155 528 1133 550 1132 524 501 526 469 530 1152 531 467 534
    

    Komendę przekonwertowaliśmy na format dla mojego firmware (czasy oddzielone przecinkami) i... klimatyzacja ruszyła!

    [i]Do wysłania użyliśmy 'IR blaster' od Tuya z wgranym OpenBeken.

    Podsumowanie
    Wygląda na to, że moje wysyłanie IR działa. Jeszcze dodam pewnie wsparcie 1:1 takich samych plików jakie tworzy Flipper Zero, ale to już kwestia parsowania danych, więc nie jest to aż tak ciekawe i ważne jak samo wysyłanie. Dodatkowo myślę, że można by spróbować ustawiać timer na czas impulsu zamiast sprawdzać co 50μs, ale to spróbuję zrealizować później. Na ten moment wszystkie postawione założenia sobie spełniłem, więc jestem zadowolony.
    Czy ktoś z czytających implementował sam wysyłanie IR, czy każdy korzysta z gotowych rozwiązań - a jeśli tak, to jakich? Mała sztuka dla sztuki w celach edukacyjno-hobbystycznych raczej nikomu nie zaszkodzi.

    Fajne? Ranking DIY
    Pomogłem? Kup mi kawę.
    O autorze
    p.kaczmarek2
    Moderator Smart Home
    Offline 
  • REKLAMA
    MediaMarkt Black Week
  • #2 21143620
    acctr
    Poziom 37  
    p.kaczmarek2 napisał:
    Powyższy kod zlicza ile upłynęło czasu, porównuje czas który upłynął z następnym progiem i jeśli próg został pokonany, to kod go "przeskakuje" i od bieżącego czasu odejmuje jego wartość (by nie akumulować błędu).

    Nie lepiej ustawiać czas timera "dynamicznie" bez używania takich liczników? Czyli najpierw przerwanie od timera przychodzi po czasie 2204, później po 708, 834, 1342, itd.
    Pomogłem? Kup mi kawę.
  • REKLAMA
    MediaMarkt Black Week
  • #3 21143644
    p.kaczmarek2
    Moderator Smart Home
    Jak najbardziej jest taka opcja i wspomniałem o tym w tekście. Też zamierzam ją wypróbować.

    Z ciekawości spojrzałem jak jest w IRLibrary:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Czym jest space i mark?
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Oni w ogóle blokują wykonanie na ten czas, też sprawdzę tę metodę.

    Na timer zdecydowałem się też, bo zastanawiam się, czy nie dałoby się w tym samym ISR próbkować sygnału z dowolnego GPIO (bez przerwań), czy to by nie dało wystarczającej precyzji odbierania IR.

    Added after 4 [minutes]:

    Też tak patrzę na ich format RAW ale w hex, bez timingów:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Oni zakładają, że rozdzielczość to 50us:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Czyli wysyłanie "ich" formatu raw, gdzie każdy bit oznacza albo 50us ciszy albo 50us PWM też można prosto zaimplementować.
    Pomogłem? Kup mi kawę.
  • #4 21195313
    morgan_flint
    Poziom 13  
    p.kaczmarek2 napisał:
    Przechwyciliśmy komendę Flipper Zero ON:
    Kod: tekst Zwiń Zaznacz wszystko Kopiuj do schowka
    Filetype: Plik sygnałów IR
    Wersja: 1
    #
    nazwa: Galanz_on
    typ: surowy
    częstotliwość: 38000
    duty_cycle: 0.330000
    dane: 3665 1547 605 1078 527 1151 507 498 605 415 584 415 584 1098 585 415 507 501 522 1152 507 1175 585 417 528 1154 506 497 502 523 579 1081 524 1160 550 493 584 1080 603 1077 503 502 600 416 583 1105 578 417 582 419 504 1178 582 445 477 503 599 416 583 416 506 500 524 499 579 417 506 492 507 523 579 417 582 415 508 496 553 451 601 416 583 421 501 500 549 468 584 1104 579 418 581 417 506 1176 583 416 507 503 546 1129 528 1156 504 499 550 450 601 417 583 418 505 523 526 469 583 416 507 1176 583 1101 582 417 582 418 505 500 601 417 582 419 504 1176 583 418 506 1179 580 1102 581 416 586 419 502 523 578 418 581 418 505 500 525 494 582 417 506 495 505 502 547 468 582 422 502 499 524 500 526 469 581 420 504 501 549 467 582 418 506 501 575 468 582 418 506 498 502 502 548 468 582 420 504 500 550 467 532 467 507 496 528 494 532 465 534 468 506 500 550 468 531 470 531 1177 531 1155 528 1133 550 1132 524 501 526 469 530 1152 531 467 534
    .
    Przekonwertowaliśmy polecenie do formatu dla mojego oprogramowania (czasy oddzielone przecinkami) i.... klimatyzacja się uruchomiła! .
    .

    Witam!!!
    Z tego co czytałem i z moich własnych badań jakiś czas temu (musiałbym to wszystko odświeżyć, żeby móc to lepiej wyjaśnić), problem z pilotami IR do klimatyzacji polega na tym, że wysyłają one wiele informacji (w rzeczywistości wszystkie ustawienia) za każdym razem, gdy naciśniesz klawisz, a nie tylko polecenie powiązane z tym klawiszem. To jest powód, dla którego przechwytywane dane są tak długie.

    W przeciwieństwie do tego, pilot zdalnego sterowania telewizora (na przykład) wysyła tylko kod polecenia powiązany z naciśniętym klawiszem, co znacznie upraszcza replikację.

    W twoim przypadku, po wysłaniu sekwencji, klimatyzacja uruchamia się, ale prawdopodobnie ustawia również temperaturę, tryb, swing, ... na ustawienia, które miałeś podczas nagrywania sekwencji. Możesz spróbować zmienić niektóre z nich (inna żądana temperatura, wentylator zamiast ciepła lub zimna itp.) i wyłączyć go za pomocą oryginalnego pilota. Następnie włącz go ponownie, wysyłając nagraną sekwencję swojego postu, aby sprawdzić, czy wprowadza zmiany w innych parametrach, poza włączeniem.

    Postaram się znaleźć linki, w których to wszystko przeczytałem i opublikować je tutaj.

    EDIT: Przykładowo ostatni post można zobaczyć tutaj .
    EDIT2: Więcej informacji:
    https://github.com/magedrifaat/Android-AC-Remote
    https://perhof.wordpress.com/2015/03/29/rever...chi-air-conditioner-infrared-remote-commands/
    https://github.com/crankyoldgit/IRremoteESP8266/wiki/Adding-support-for-a-new-AC-protocol

    IRScrutinizer wspomniany w niektórych linkach jest łatwy do skonfigurowania za pomocą Arduino i tanich odbiorników IR (nawet odzyskanych ze starego sprzętu), a powiązane oprogramowanie bardzo pomaga w analizie przechwyconych danych (myślę, że powinno być również możliwe wklejenie do oprogramowania danych przechwyconych za pomocą Flippera).

    EDIT3: Znalazłem analizę, którą przeprowadziłem na protokole pilota mojego klimatyzatora (w załączeniu). Zarejestrowałem dane, zmieniając jeden parametr na raz, a następnie utworzyłem Excela, w którym wykryłem za pomocą formatu warunkowego zmiany z sekwencji do następnej (zaznaczając na żółto, gdy komórka różni się od powyższej), dzięki czemu mogłem wywnioskować, które bity odpowiadają każdej funkcji. Jak w przypadku wielu moich projektów, zatrzymałem się na tym i wciąż muszę wykonać ostatni krok: wdrożenie go na sprzęcie 🤦‍♂️ ... Kupiłem dwa rodzaje uniwersalnych pilotów IR z chipami Beken, które czekają na flashowanie...
REKLAMA