![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/3221612800_1643065166_thumb.jpg)
Witajcie moi drodzy.
Zapraszam na relację z postępów prac nad otwartym firmware dla BK7231T. Temat ten napisany będzie w formie tutoriala, przedstawię tu krok po kroku jak opracowałem własny, wieloplatformowy (działający na Windowsie i BK7231T) mini-serwer HTTP, system konfiguracji pinów przez panel WWW oraz jak połączyłem go z MQTT i docelowo z Home Assistant. Do tematu załączę pełne kody do pobrania i projekt Visual Studio dla tych którzy chcą się pobawić moim serwerem pod Windowsem.
O co chodzi?
W celu wyjaśnienia całej inicjatywy i celu projektu odsyłam do pierwszej części sprzed dwóch miesięcy:
https://www.elektroda.pl/rtvforum/topic3850712.html
Temat zakłada, że czytelnik przyswoił wiedzę z poprzedniej części!
Zawartość tematu
W tym temacie przedstawię:
- jak napisać własny prosty mini-serwerek HTTP w języku C na Windowsie
- jak przygotować ten serwerek by był wieloplatformowy i uruchomić go na WB2S/BK7231T
- jak zorganizować sprytnie konfigurację pinów dla BK7231T w stylu Tasmoty
- jak ostatecznie zorganizowałem konfigurację pinów oraz wsparcie dla MQTT dla mojego otwartego wsadu dla BK7231T
- jak w przypadku bieżącej wersji projektu uruchomić mój wsad na własnym układzie z serii BK7231T (WB2S, WB3S, może inne) i podpiąć go do Home Assistant przez MQTT
Czemu serwer będzie wieloplatformowy?
Z następujących powodów:
- pozwoli mi to wydajnie tworzyć stronkę WWW konfiguratora i zarządzania modułem BK7231T na Windowsie bez posiadania tego modułu fizycznie, bez potrzeby wgrywania na niego wsadu co trwa długo, itp.
- pozwoli mi to użyć tego samego konfiguratora i strony w przypadku innej rodziny układów którą też zamierzam "otwierać", a konkretniej XR809
- poniekąd wymusi to na mnie lepszą organizację projektu i utrzymywanie wszystkiego tak by się kompilowało na wszystkich platformach i podkreśli bardziej podział na moduły
UWAGA - podstawy C i wyszukiwarki
Temat postaram się przedstawić najbardziej ciekawie jak potrafię, ale z oczywistych względów nie mam jak tu tłumaczyć wszystkiego linia po linii, jak również nie będę tłumaczyć absolutnych podstaw C. W razie czego polecam zapoznać się z materiałami na temat samego tego języka oraz z dokumentacją Winsocka i działaniem HTTP oraz TCP. Dodatkowo w temacie pozwalam sobie na pewne uproszczenia, które nie powinny mieć miejsca w docelowym produkcie, takie jak np. używanie niebezpiecznej funkcji strcat (nie sprawdza ona czy w docelowym buforze jest miejsce na to co kopiujemy).
Własny serwer HTTP - organizacja pracy
Własny serwer HTTP postanowiłem napisać w Visual Studio C++ 2008 Express Edition. Jest to IDE w którym najwięcej pracowałem i znam je dość dobrze, więc wybór dla mnie był oczywisty, ale Wy możecie użyć dowolnego innego kompilatora C/C++.
W tym IDE oczywiście tworzy się programy na Windowsa - serwer HTTP w początkowej fazie będzie tworzony i testowany na tej platformie.
Własny serwer HTTP - punkt początkowy
HTTP to protokół operujący na poziomie TCP (nie mylić z UDP). TCP zostało wybrane tutaj przez twórców HTTP ze względu na rzetelność transmisji, a dokładniej wsparcie retransmisji zagubionych pakietów, czego nie oferuje UDP (UDP jest bezpołączeniowe). Zaczniemy więc od prostego serwera TCP napisanego pod Winsockiem (który zasadniczo jest praktycznie 1:1 zgodny z socketami pod Linuxem i też tymi na BK7231T):
Code: c
Powyższy kod po uruchomieniu otwiera nasłuch na porcie 80 (nie możemy mieć go zajętego na naszej maszynie - jak mamy np. Apache (np. w pakiecie Xampp) to port będzie już zajęty i program go nie przejmie), dzięki czemu możemy do niego "zagadać" z przeglądarki internetowej (w zasadzie to można by użyć dowolnego innego portu - w adresie URL można zawrzeć port docelowy, ale nie komplikujmy).
W razie pojawienia się klienta kod powyżej go obsługuje i utrzymuje z nim połączenie TCP.
Najbardziej interesują nas trzy sekcje tego kodu.
Tu następuje odebranie pakietu (recv blokuje program do momentu odebrania czegoś, choć w przypadku Berkeley sockets może być nieblokujące):
Code: c
Tu tworzymy tekst, który wyślemy:
Code: c
Tu go wysyłamy:
Code: c
Czy to działa?
Uruchamiamy program:
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/5429470400_1643037166_thumb.jpg)
W przeglądarce trzeba wpisać jakiś adres. Można też wpisać IP. Nasze IP (te w sieci LAN - typu 192.168 itp) można poznać np. poprzez komendę ipconfig, ale przecież jest łatwiejszy sposób na odniesienie się do naszej maszyny. Można użyć adresu localhost, czyli 127.0.0.1:
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/1912638400_1643037311_thumb.jpg)
Jak widzimy, serwer odpowiedział "This is an example reply from my server". A co odebrał?
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/1370630600_1643037344_thumb.jpg)
Widzimy tu treść przykładowego zapytania GET. Poniżej umieszczam ją jako tekst, dla wygody:
GET / HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: " Not;A Brand";v="99", "Microsoft Edge";v="97", "Chromium";v="97"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 Edg/97.0.1072.69
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: pl,en;q=0.9,en-GB;q=0.8,en-US;q=0.7
Najważniejsza jest możliwość nawigowania po stronach - zobaczmy, jak zmieni się pakiet gdy zapytamy np. o podstronę index:
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/4085670400_1643037564_thumb.jpg)
Nasz index pojawia się tutaj:
GET /index HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive
A jak wygląda sytuacja z argumentami zapytania GET? To są te argumenty które nawet widzimy na Elektrodzie gdy piszemy wiadomość, np:
https://www.elektroda.pl/rtvforum/privmsg.php?mode=reply&p=6195972
Jak nasz serwer je wyświetli?
W ten sam sposób:
GET /rtvforum/privmsg.php?mode=reply&p=6195972 HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive
Zaraz się zajmiemy tego parsingiem, ale najpierw jeszcze drobna poprawka.
Nasz program wyświetla dziwne znaki po odebranym pakiecie:
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/9754169200_1643037694_thumb.jpg)
To dlatego, że pakiet nie zawiera tzw. NULL terminating character, czyli bajtowego zera oznaczającego koniec napisu w ASCII. Dodamy je ręcznie:
Code: c
Dodałem ustawienie następnego kolejnego bajtu (spoza pakietu, bo adresujemy tablice w C od 0) na 0, czyli na te słynne NULL. Dodatkowo zmniejszyłem rozmiar dostępnego bufora o 1, by w razie czego nie wyjść poza jego zakres.
Własny serwer HTTP - parsing bieżącej strony
Parsing bieżącej strony, czyli wyłuskanie docelowego adresu zasobu na serwerze, zrealizuję całkiem od 0. Można to zrobić na różne sposoby, można skopiować ten adres do innej zmiennej (takie sprytniejsze strcpy), albo można go sprawdzać "w miejscu" i tylko otrzymywać rezultat true/false czy jest on zgodny z danym...
Ale na początek musimy sprawdzić, czy to w ogóle jest zapytanie GET. Do tego przygotowałem własną funkcję http_startsWith:
Code: c
Funkcja po kolei porównuje dwa ciągi znaków, przechodzi znak po znaku. ++ zwiększa wskaźnik o jedną jednostkę (tutaj wskazuje na char, typ o rozmiarze bajta, więc o jeden bajt).
Teraz obsługa pakietu wygląda tak:
Code: c
Program odrzucać będę zapytania bez nagłówka "GET". Teraz przejdźmy dalej, zróbmy funkcję, która sprawdza czy adres zasobu żądany przez klienta jest zgodny z daną nazwą:
Code: c
Funkcja działa podobnie jak poprzednia, z tą różnicą, że przerywa porównywanie w momencie napotkania spacji lub pytajnika. To dlatego, że w zapytaniu:
https://www.elektroda.pl/rtvforum/privmsg.php?mode=reply&p=6196006
adres zasobu (pliku php) kończy się przed pytajnikiem, dalej są argumenty GET.
Spróbujmy użyć naszej funkcji:
Code: c
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/8382359800_1643038460_thumb.jpg)
To działa! Rozpoznaje o jaką stronę pytamy.
Własny serwer HTTP - parsing argumentów
Teraz pora wczytać jakoś argumenty które znajdują się po znaku zapytania w treści żądania GET. Format argumentów jest dość prosty, mamy znak zapytania, potem nazwę zmiennej, znak równości, wartość zmiennej i jeśli jest coś dalej, to znak & i następną zmienną.
rtvforum/privmsg.php?mode=reply&p=6196006
To też wczytamy samodzielnie. Tym razem jednak przerzucimy wartość zmiennej do osobnego bufora. W tym przypadku istnieje duże ryzyko problemów typu "buffer overflow" i w wersji produkcyjnej należy dokonać wszelkich starań by nie było to możliwe, ale w przypadku tej demonstracji nie będę tego głębiej omawiać. Dodam jedynie, że przekroczenie rozmiaru bufora pozwalać może nawet na zdalne wykonanie złośliwego kodu w naszym programie...
Code: c
Mam tutaj osobno funkcję sprawdzającą nazwę argumentu oraz osobno kopiującą jego zawartość do zewnętrznego bufora. Ważna jest tutaj obsługa znaków takich jak & i ' ' oraz bajtowe zero, które mówią nam, że musimy przerwać parsing.
Nie twierdzę tutaj, że moje podejście jest najlepsze (bo tutaj iteruję w tym wywołaniu funkcji http_getArg wszystkie argumenty, a gdy będę chciał wczytać 5 argumentów różnych, to będę 5 razy od 0 iterować wszystkie), ale na taką skalę to nie ma żadnego znaczenia.
W ramach demonstracji tego parsingu zróbmy prosty kalkulator:
Code: c
Program sprytnie sprawdza, czy oba argumenty są dostępne. Jeśli tak, to wyświetla ich sumę (atoi zamienia napis na integer, a sprintf zamienia m. in. integery na napis), a jeśli nie, to pokazuje odpowiedni komunikat:
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/7492782900_1643039063_thumb.jpg)
Program działa! Nasz własny kalkulator napisany od 0 w HTTP już umie liczyć.
Teraz jeszcze trzeba wysłać odpowiedź zgodnie ze sztuką... a nie jako zwykły napis.
Własny serwer HTTP - wysyłanie odpowiedzi
Co takiego powinien wysłać nam serwer w odpowiedzi na żądanie GET?
Najlepiej przekonać się w praktyce. Użyłem do tego Wiresharka - narzędzia do przechwytywania pakietów:
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/6137094100_1643040581_thumb.jpg)
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/6104054400_1643040607_thumb.jpg)
Widać, że odpowiedź składa się z dwóch warstw:
- warstwa HTTP
- sama treść odpowiedzi (z reguły html, choć może też być obrazek, itp, w zależności od content type)
Odpowiedź można uprościć do:
HTTP/1.1 200 OK\r\n
Content-Type: text/plain\r\n
\r\n
Najpierw mamy słynny kod 200 OK (analogicznie do 404 które pewnie kojarzycie), a potem rodzaj zawartości. Potem jedna pusta linia - ona też jest ważna.
Wpiszmy to w kod:
Code: c
Oczywiście Content-type mogą też być inne (np. dla obrazka), ale tu tego nie potrzebujemy. Format text/plain będzie dla plików Javascript, które być może w przyszłości też się przydadzą.
Oraz przygotujmy funkcję pomocniczą inicjującą nagłówek HTTP:
Code: c
No i zróbmy jakąś zawartość strony HTTP:
Code: c
Czy to działa?
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/6554901700_1643040775_thumb.jpg)
Nasza pierwsza strona HTML napisana na własnym serwerze HTTP już działa!
Własny serwer HTTP - wieloplatformowość
Odeszliśmy troszkę od tematu WB2S/BK7231T, ale teraz już pora do niego wrócić.
Zastanówmy się - jak możemy zorganizować nasz serwer, by jednocześnie działał na Windowsie i na WB2S?
Trzeba dokonać cięcia - wydzielamy rzeczy Windowsowe w jedno miejsce, w HTTPowe w drugie.
Od teraz mój stary plik main.c (gdzie jest kod winsocka) nazywa się win_main.c i zawiera m. in:
Code: c
Funkcja HTTP_ProcessPacket jest w innym pliku, ona bierze za argument wejściowy pakiet oraz wyjściowy bufor (tak tak, trzeba potem dodać kontrolę czy nie przekraczamy zakresu).
Funkcja HTTP_ProcessPacket od teraz jest w nowym pliku - new_http.c:
Code: c
Oczywiście trzeba też zrobić nagłówek (plik .h, header i móc go #include) - ale zakładam, że znamy C...
Code: c
Ta funkcja pozwala nam użyć naszego serwera na dowolnej platformie, w tym na BK7231T.
Na BK7231T wpinamy się w kod TCP (tylko ustawiamy, by nasłuchiwał na porcie 80) w ten sposób:
Code: c
Kod z innych platform można też Visualu wyłączyć w ustawieniach pliku:
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/6928389200_1643068099_thumb.jpg)
Albo poprzez preprocesor (to jest wygodne dla sekcji kodu):
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/3072506500_1643068173_thumb.jpg)
Potem w kodzie używamy:
Code: c
Instrukcje preprocesora wykonują się w trakcie kompilacji.
I zasadniczo wszystko gotowe - serwer działa na dwóch platformach!
Oczywiście, to nie jest takie proste jak się wydaje - w przypadku użycia wątków należy pamiętać m. in. o rozsądnym zarządzaniu zasobami oraz o tym, by nie używać niepoprawnie zmiennych globalnych, ale "thread-safety" to już na inny temat zostawię...
Bardziej zaawansowane strony
Analogicznie utworzyłem stronę konfiguracyjną dla mojego wsadu dla BK7231T:
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/7048135300_1643041689_thumb.jpg)
Code: c
Code: c
Druga strona:
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/1501852600_1643041819_thumb.jpg)
Code: c
Myślę, że powyższe przykłady dobrze przedstawiają jak wygląda tworzenie HTML z poziomu kodu. Oczywiście w przypadku konfiguratora pinów jest to bardziej skomplikowane...
Konfigurator pinów dla BK7231
Temat HTML na razie możemy zamknąć. Drugą ważną rzeczą którą musiałem opracować dla własnego firmware był konfigurator pinów w stylu tego z Tasmoty. Podstawowe założenie było takie, że ma on działać też na Windowsie by można było wygodnie testować.
Konfigurator Tasmoty wygląda tak:
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/7443406700_1643041528_thumb.jpg)
Tak wygląda mój odpowiednik:
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/1121761700_1643041578_thumb.jpg)
Puste pola to miejsce na numer kanału (przekaźnika), kanałów może być dowolna ilość (no dobra, w sumie to tylko 32, a czemu to wyjaśni się potem).
Od strony implementacji zdecydowałem się reprezentować piny i kanały następującą strukturą:
Code: c
Roles to są role pinów, a channels to kanały.
Role opisuje też enumeracja:
Code: c
Sufiks "_n" oznacza negację, czyli np. dioda LED która zamiast świecić się gdy dany kanał jest ustawiony na 1, to świeci się gdy ustawiony jest na 0.
Z tych struktur można wyciągnąć już pewne wnioski:
- możemy mieć kilka przekaźników na kanale 1 i one będą wspólnie pracować
- typy Relay i LED tak naprawdę działają tak samo, są tylko dla wygody użytkownika rozdzielone
- możemy mieć np. Relay bez przycisku (wtedy steruje się go tylko przez internet), jak również przycisk bez Relay/bez LED (co teraz się nie przyda, ale docelowo pewnie dodam możliwość wyzwalania zdarzeń)
Nagłówek systemu pinów nazywa się new_pins.h i ma następującą treść:
Code: c
Funkcje powyżej stanowią interfejs pinów wywoływany z obsługi HTTP:
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/1523025200_1643042242_thumb.jpg)
Sam kod generujący tę stronę przedstawiam poniżej:
Code: c
Przede wszystkim widać tutaj jak kod obsługi HTTP korzysta z interfejsu systemu pinów. Drugie co widzimy to to, że przyciski są generowane automatycznie na bazie tego co ustawił użytkownik. Kolejną rzeczą jest obsługa przełączania stanu przycisków - czyli odebranie argumentu GET i wywołanie CHANNEL_Toggle.
Jest tu nieco miejsca na ulepszenia - teraz żądanie GET zawsze wykonuje toggle, więc jeśli po drodze ktoś inny nam przełączy przekaźnik to możemy być zaskoczeni, że pozornie "nie działa" chwilowo strona. Tak samo np. strona nie odświeża się sama (można dać javascript krótki by się sama odświeżała albo odświeżać jej samą część poprzez Ajax).
Jeszcze popatrzmy na implementację konfiguratora, poniżej zrzut ekranu i kod:
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/5108447900_1643042594_thumb.jpg)
Code: c
Dla wygody dodałem wykrywanie tego ile rzeczywiście zmieniło się pól.
UWAGA: To wszystko powyżej (razem z przełączaniem wartości kanałów/przekaźników) można w moim wsadzie testować w wersji na Windows! Nie trzeba nawet mieć WB2S by to rozwijać i nad tym pracować. Bardzo wygodne.
Podpięcie obsługi pinów dla BK7231
Mój konfigurator pinów wraz z serwerem HTTP działają w pełni na Windowsie. Jest to możliwe dlatego, że kod obsługi pinów powiązany z BK7231T jest odpowiednio wydzielony w pliku new_pins.c:
Code: c
Natomiast sama zmiana wartości kanału jest niezależna od platformy:
Code: c
Tak, jeden int reprezentuje wartości kanałów - jeden bit to jeden kanał. Dlatego limitem ilości kanałów jest teraz 32, 32 bity w integerze na platformie 32-bitowej.
Podpięcie obsługi MQTT (wysyłanie zmian kanałów do serwera MQTT)
O samym MQTT pisałem już w pierwszej części. Tutaj pokażę tylko kiedy i jak rozsyłana jest informacja o tym, że użytkownik nacisnął fizyczny przycisk na obudowie i zmienił stan urządzenia.
Służy do tego ten callback (wskaźnik na funkcję):
Code: c
Zobaczmy, jak wygląda użycie CHANNEL_SetChangeCallback, już po stronie BK7231T, w tuya_device.c:
Code: c
Kod w powyższej wersji nie rozróżnia tego który kanał został zmodyfikowany, jedynie po prostu wykonuje MQTT publish wartości kanału pierwszego (CHANNEL_Check(1)) do serwera MQTT z którym jest połączony.
Podpięcie obsługi MQTT (odbieranie poleceń z Home Assistant)
Analogicznie zrealizowana jest komunikacja w drugą stronę. Od MQTT można odebrać polecenie zmiany wartości kanału na inną (na 1 lub 0) i wtedy musimy użyć CHANNEL_Set by ją propagować dalej do pinów.
Odbiór tego przez MQTT wygląda tak:
Code: c
Mamy osobno funkcje dla publish (nazwa zmiennej) i data (jej wartość), ale w tej chwili tego nie używam by nie komplikować. Po prostu zakładam, że jest jeden kanał i to starcza dla prostych sytuacji.
Możliwość zrobienia konfiguracji na sztywno w kodzie
Bieżąca wersja projektu nie wspiera zapisu ustawień do pamięci Flash, więc warto jest skonfigurować wszystko pod nasze urządzenie w kodzie. U mnie wygląda to tak:
Code: c
Ustawienia powyżej działają dobrze na SmartSwitch Tuya WL-SW01_16 16A:
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/8086816100_1620677696_thumb.jpg)
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/7376866800_1642985473_thumb.jpg)
Podłączenie do Home Assistant
Podłączenie do Home Assistant jest bardzo proste. Wymaga modyfikacji pliku configuration.yaml, gdzie podajemy m. in. jakiego publish MQTT nasłuchujemy:
switch:
- platform: mqtt # Again, it's an MQTT device
name: "WB2S" # Choose an easy-to-recognize name
state_topic: "wb2s/get" # Topic to read the current state
command_topic: "wb2s/set" # Topic to publish commands
qos: 1
payload_on: 0 # or "on", depending on your MQTT device
payload_off: 1 # or "off", depending on your MQTT device
retain: true # or false if you want to wait for changes
[i]UWAGA: ostateczna wersja kodu z tego tematu wymaga innego formatu nazw MQTT, już nie wb2s/get tylko wb2s/1/get gdzie 1 to nazwa kanału/przekaźnika, szczegóły później.
Analogicznie, po stronie WB2S też trzeba uzupełnić namiary na nasz serwer MQTT oraz odpowiednio nazwy komend:
Code: c
Code: c
Code: c
Pełny kod w załączniku na końcu tematu.
Prezentacja działania - wersja z jednym przekaźnikiem
Poniżej prezentacja działania na filmiku:
Jak widać (mam nadzieję - bo są problemy z odtwarzaczem elektrody na niektórych przeglądarkach) Home Assistant momentalnie reaguje na naciśnięcie przycisku fizycznego na urządzeniu, a urządzenie reaguje momentalnie na kliknięcie zmiany stanu na stronie Home Assistant. Komunikacja odbywa się sprawnie i szybko, jest bardzo responsywna.
Jedyne czego brakuje to odświeżania własnej strony urządzenia, ale to można dodać dość łatwo poprzez skrypt JS.
Wsparcie wielu przekaźników na jednym urządzeniu
Wsparcie wielu przekaźników na jednym urządzeniu okazało się być dość proste do zrealizowania. Udało się to przede wszystkim dzięki tzw. MQTT wildcards, które pozwalają nasłuchiwać wielu tematów jednocześnie.
Zmodyfikowane ustawienie subskrybcji z wildcard (znak +) wygląda tak:
Code: c
Ten kod powyżej służy do odbierania informacji o zmianach z Home Assistant przez MQTT.
Samo odbieranie subskrybcji wymaga teraz wyłuskania i zapisania indeksu kanału (jak na razie zrobiłem to w banalny sposób, potem się poprawi):
Code: c
Wildcard (plus) zastępowany jest indeksem kanału.
Samo rozgłoszenie zmiany z poziomu WB2S/WB3S wygląda teraz tak:
Code: c
Po prostu w wiadomości zachowywany jest też indeks kanału (przekaźnika).
Do testów użyłem tego sterownika 4 przekaźników na WB3S:
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/4080361700_1642985485_thumb.jpg)
Teraz też zmienia się sposób definiowania przekaźników w Home Assistant w configuration.yaml. Od teraz wygląda to tak:
switch:
- platform: mqtt # Again, it's an MQTT device
name: "WB2S" # Choose an easy-to-recognize name
state_topic: "wb2s/1/get" # Topic to read the current state
command_topic: "wb2s/1/set" # Topic to publish commands
qos: 1
payload_on: 0 # or "on", depending on your MQTT device
payload_off: 1 # or "off", depending on your MQTT device
retain: true # or false if you want to wait for changes
- platform: mqtt # Again, it's an MQTT device
name: "WB2S2" # Choose an easy-to-recognize name
state_topic: "wb2s/2/get" # Topic to read the current state
command_topic: "wb2s/2/set" # Topic to publish commands
qos: 1
payload_on: 0 # or "on", depending on your MQTT device
payload_off: 1 # or "off", depending on your MQTT device
retain: true # or false if you want to wait for changes
- platform: mqtt # Again, it's an MQTT device
name: "WB2S3" # Choose an easy-to-recognize name
state_topic: "wb2s/3/get" # Topic to read the current state
command_topic: "wb2s/3/set" # Topic to publish commands
qos: 1
payload_on: 0 # or "on", depending on your MQTT device
payload_off: 1 # or "off", depending on your MQTT device
retain: true # or false if you want to wait for changes
- platform: mqtt # Again, it's an MQTT device
name: "WB2S4" # Choose an easy-to-recognize name
state_topic: "wb2s/4/get" # Topic to read the current state
command_topic: "wb2s/4/set" # Topic to publish commands
qos: 1
payload_on: 0 # or "on", depending on your MQTT device
payload_off: 1 # or "off", depending on your MQTT device
retain: true # or false if you want to wait for changes
Kilka wyjaśnień:
- "wb2s" tutaj to nazwa własna urządzenia (jak mamy dwa urządzenia, to trzeba będzie ją zmienić np. w kodzie...)
- w nazwie "wb2s/X/get" znak X oznacza numer kanału (numer przekaźnika)
- jak mamy pojedynczy przekaźnik, to używamy tylko "wb2s/1/get" oraz "wb2s/1/set"
- wartości payload mówią co wysyłamy (lub odbieramy) by określić stan urządzenia, tutaj po prostu 1 lub 0
Rezultat:
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/2274989500_1643063827_thumb.jpg)
Co wymaga moduł WB2S do uruchomienia?
Nie do końca byłem pewny gdzie powinienem umieścić ten akapit, więc po prostu go tutaj zostawię.
Otrzymałem od @pixelo kilka wylutowanych modułów WB2S, które są jak znalazł do testów.
Sprawdziłem, że taki moduł nie wymaga żadnych zewnętrznych podłaczeń oprócz zasilania 3.3V do działania. Można go też łatwo programować.
Poniżej fotorelacja z lutowania/uruchomienia:
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/9776726900_1643064620_thumb.jpg)
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/7778286000_1643064620_thumb.jpg)
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/5463156500_1643064697_thumb.jpg)
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/5928429900_1643064770_thumb.jpg)
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/4698239900_1643064961_thumb.jpg)
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/3508870800_1643064989_thumb.jpg)
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/2739077800_1643065019_thumb.jpg)
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/3681423200_1643065086_thumb.jpg)
![[BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant [BK7231T] Mój serwer HTTP, konfigurator, wsparcie MQTT z Home Assistant](https://obrazki.elektroda.pl/7057488800_1643065031_thumb.jpg)
Tak przygotowany moduł działa, jeden UART służy do programowania, drugi do odbierania logu.
Samych połączeń nie opisuję, gdyż je opisywałem w pierwszym temacie z serii.
Jak dodać wsparcie nowego urządzenia?
Poniżej przedstawiam pełną instrukcję dodawania wsparcia nowego urządzenia z BK7231T do bieżącej wersji mojego projektu.
1. Pobierz ostatnią wersję SDK z tego tematu (uzupełnij ją brakujący toolchain w \platforms\bk7231t\toolchain)
2. Uzupełnij dane w apps\my_alpha_demo\src\tuya_device.c
- SSID i hasło w connect_to_wifi
- MQTT nazwę użytkownika i hasło w mqtt_connect_client_info_t
- IP serwera MQTT w ipaddr_aton
- opcjonalnie zmień nazwę urządzenia w pakietach MQTT w tuya_device.c, tzn. w "wb2s/+/set" zmień "wb2s" na unikalną nazwę, inną dla każdego urządzenia w Twojej sieci (analogicznie dla get)
3. Uzupełnij konfigurację pinów w apps\my_alpha_demo\src\tuya_device.c w device_init:
Code: c
4. Skompiluj projekt "my_alpha_demo" wedle instrukcji stąd:
https://www.elektroda.pl/rtvforum/topic3850712.html
5. Wgraj "my_alpha_demo" wedle instrukcji stąd:
https://www.elektroda.pl/rtvforum/topic3850712.html
6. Uzupełnij configuration.yaml z Home Assistant wedle wzorca:
switch:
- platform: mqtt # Again, it's an MQTT device
name: "WB2S" # Choose an easy-to-recognize name
state_topic: "wb2s/1/get" # Topic to read the current state
command_topic: "wb2s/1/set" # Topic to publish commands
qos: 1
payload_on: 0 # or "on", depending on your MQTT device
payload_off: 1 # or "off", depending on your MQTT device
retain: true # or false if you want to wait for changes
- platform: mqtt # Again, it's an MQTT device
name: "WB2S2" # Choose an easy-to-recognize name
state_topic: "wb2s/2/get" # Topic to read the current state
command_topic: "wb2s/2/set" # Topic to publish commands
qos: 1
payload_on: 0 # or "on", depending on your MQTT device
payload_off: 1 # or "off", depending on your MQTT device
retain: true # or false if you want to wait for changes
- platform: mqtt # Again, it's an MQTT device
name: "WB2S3" # Choose an easy-to-recognize name
state_topic: "wb2s/3/get" # Topic to read the current state
command_topic: "wb2s/3/set" # Topic to publish commands
qos: 1
payload_on: 0 # or "on", depending on your MQTT device
payload_off: 1 # or "off", depending on your MQTT device
retain: true # or false if you want to wait for changes
- platform: mqtt # Again, it's an MQTT device
name: "WB2S4" # Choose an easy-to-recognize name
state_topic: "wb2s/4/get" # Topic to read the current state
command_topic: "wb2s/4/set" # Topic to publish commands
qos: 1
payload_on: 0 # or "on", depending on your MQTT device
payload_off: 1 # or "off", depending on your MQTT device
retain: true # or false if you want to wait for changes
Gotowe!
Konfigurator pinów jest dostępny, ale on nie zapisuje jeszcze ustawień w pamięci Flash, więc zasadniczo pozwala nam tylko się pobawić i poeksperymentować.
Wsparcie testowania na systemie Windows
Ostateczna wersja projektu (ta którą tu publikuję) wspiera wciąż kompilację serwera HTTP oraz systemu pinów/przekaźników/kanałów pod system Windows poprzez środowisko Visual Studio - po prostu otwieramy projekt myHTTP2022.vcproj z folderu my_alpha_demo i kompilujemy. Po uruchomieniu postawi on nam serwer na porcie 80 na naszej maszynie. Dzięki temu można znacznie łatwiej i szybciej rozwijać cały moduł HTTP z projektu, testować wyglądów różnych stron, zmieniać podział na podstrony, itp.
Dalszy rozwój projektu
Oczywiście to jest tylko wstępna wersja demonstracyjna i niedługo zamierzam dodać:
- łatwiejszą obsługę urządzeń MQTT (losowa nazwa bazująca na adresie MAC urządzenia, możliwość zmiany tej nazwy przez WWW)
- zapis ustawień do pamięci flash tak by nie trzeba było rekompilować projektu zawsze
- system resetowania urządzenia i konfigurowania go przez otwartą sieć WiFi
- wsparcie XR809 (ten sam kod HTTP będzie i organizacja dla dwóch różnych rodzin mikrokontrolerów)
- być może przeniosę częściowo się na Ajaxa z tym panelem HTTP strony, chociażby dla odświeżania stanów przycisków/przekaźników...
Wsparcie projektu
Jeśli podoba Ci się pomysł i inicjatywa, to możesz wesprzeć projekt poprzez
- testowanie (za jakiś czas założę repozytorium github pod oba projekty, bo robię też wsad open source dla XR809)
- gromadzenie informacji w jakich urządzeniach są jakie moduły (możecie wysyłać zdjęcia, itp, chociażby w tym temacie. Wiem o liście https://templates.blakadder.com/ )
- darowiznę paypal:
https://paypal.me/openshwprojects
(trochę tych różnych urządzeń jest a ja docelowo chcę przetestować ich jak najwięcej, zresztą dużo już teardown umieściłem w https://www.elektroda.pl/rtvforum/forum507.html , środki pójdą na zakup kolejnych urządzeń )
- jeśli macie jakieś np. moduły z BK7231T które wam zostały po zamianie na ESP12F lub uszkodzone urządzenia smart itp to mogę je przyjąć do testów (kontakt PW)
Jest znacznie więcej podobnych modułów do WB2S i WB3S, każdy z nich zamierzam docelowo wspierać.
Podsumowanie
W ten sposób projekt "uwolnienia" BK7231T zaczyna naprawdę nabierać kształtów. Z kilku prostych demek UDP, TCP, HTTP i MQTT prezentowanych w poprzedniej części udało mi się już zrobić pierwszy rzeczywiście praktyczny wsad, który w ostateczności można by już wykorzystywać w normalnym 'smart' domu do sterowania oświetleniem i nie tylko (jak nie dodamy Relay to możemy odczytywać stany "Buttonów" i na ich bazie działać w Home Assistant, więc czujnik otwarcia drzwi itp czy tam zalania też jest wspierany).
To oczywiście nie jest koniec, następna aktualizacja wkrótce. Jeśli podoba się Wam inicjatywa to dajcie znać, muszę wiedzieć ile mamy potencjalnych użytkowników i jakie jest zainteresowanie całą ideą.
Na koniec chciałbym podziękować @strigona i @PIXELLO za podarowanie mi modułów do testów.
Cool? Ranking DIY