Spisu treści:
- Krok 1: Konfiguracja - najpierw skonfiguruj sprzęt
- Krok 2: Konfiguracja - Ustaw swój ekran
- Krok 3: Skonfiguruj Master End, a następnie połącz ze sobą - część 1
- Krok 4: Skonfiguruj Master End, a następnie połącz ze sobą - część 2
- Krok 5: Przegląd szkiców / programów - struktura programu
- Krok 6: Projektowanie odgórne
- Krok 7: Koncepcja i projekt - część 1
- Krok 8: Koncepcja i projekt – część 2
- Krok 9: Zakończ główną pętlę: A) Odbieranie z USB, B) Odbieranie ze Slave Arduino
- Krok 10: Odbieranie i obsługa danych w podrzędnym Arduino
- Krok 11: Napisz funkcję odbioru
- Krok 12: Napisz podfunkcję odbierania - część 1
- Krok 13: Napisz podfunkcję odbierania - część 2
- Krok 14: Napisz funkcje przesyłania i analizowania
- Krok 15: Napisz funkcje przesyłania i analizowania
- Krok 16: Finał
Wideo: Wysyłanie danych liczbowych z jednego Arduino do drugiego: 16 kroków
2024 Autor: John Day | [email protected]. Ostatnio zmodyfikowany: 2024-01-30 11:28
Wstęp
David Palmer, CDIO Tech. na Uniwersytecie Aston.
Czy kiedykolwiek musiałeś przesyłać numery z jednego Arduino do drugiego? Ta instrukcja pokazuje, jak.
Możesz łatwo przetestować, jak działa, po prostu wpisując ciąg liczb do wysłania na terminalu monitora szeregowego, i zobacz, jak numery powracają na drugim monitorze szeregowym podłączonym do drugiego Arduino. Możesz nawet użyć łącza Bluetooth.
Co to robi
Dwa programy Arduino (szkice w języku Arduino) wymagają opracowania, jeden program Master do łączenia się z komputerem hosta z Arduino Serial Monitor, jeden do działania jako Slave do odbierania wiadomości szeregowej od Master, dekodowania jej i odsyłania. Slave może opcjonalnie wyświetlać liczby, z którymi ma do czynienia na monitorze szeregowym drugiego IDE - na wypadek, gdybyś chciał tego użyć. Może to przede wszystkim pomóc w sprawnym działaniu i pomóc, jeśli zdecydujesz się wprowadzić zmiany w programach, aby dopasować je do własnych wymagań.
Ekwipunek
- 2 Arduino
- 2 przewody USB
- przewody krosowe (w razie potrzeby)
- 1 komputer PC/laptop z załadowanym środowiskiem Arduino IDE (dostępny do bezpłatnego pobrania ze strony Arduino.cc)
Krok 1: Konfiguracja - najpierw skonfiguruj sprzęt
Podłącz 2 Arduino do 2 portów USB w komputerze.
Wskazówka, dobrym pomysłem jest oznaczenie ich jako M i S (master i slave), aby później nie wpaść w zamieszanie (jak pokazano na 2 zdjęciu tutaj).
Krok 2: Konfiguracja - Ustaw swój ekran
Najlepszą rzeczą jest skonfigurowanie ekranu tak, abyś miał
- IDE załadowane programem Master po lewej stronie i
- że z niewolnikiem po prawej.
Trzymaj monitory szeregowe dla Maser i Slave po lewej i prawej stronie, jak pokazano na zrzucie ekranu tutaj.
Krok 3: Skonfiguruj Master End, a następnie połącz ze sobą - część 1
Gdy konfigurujesz Master End Serial Monitor do wysyłania dwóch liczb, musisz zawsze używać początku i końca, znaków separatora oraz znaku separatora przecinka, jak widać tutaj.
Teraz musisz połączyć 2 Arduino razem przez port szeregowy. Odbywa się to za pomocą dwóch przewodów krosowych.
Użyłem zielonego i żółtego
- Najpierw weź żółty, musi być podłączony do D6 w jednym Arduino i D7 w drugim
- Następnie odwrotnie dla zielonego przewodu, D7 na pierwszym i D6 na drugim Arduino.
Alternatywnie, jeśli masz coś dostępnego, jak para modułów Bluetooth – takich jak HC-05 – będą one również działać, aby dać dokładnie taki sam efekt, jak powyższe przewody.
Krok 4: Skonfiguruj Master End, a następnie połącz ze sobą - część 2
Korzystamy z biblioteki Software Serial. Dalsze informacje są dostępne pod tym linkiem
Możesz zobaczyć to wywołane w wierszu 7 każdego z programów. Konfiguruje piny cyfrowe 7 i 6 jako TX i RX (nadawanie i odbieranie). W ten sposób dane będą przesyłane z Master Arduino przez zielony przewód do Slave, a gdy program Slave w drugim Arduino zakończy swoją pracę, z powrotem przez żółty przewód. Na dole tej samej ilustracji (w oknie Monitora szeregowego) widać, że przesyłane przez nas dane pomyślnie ominęły opisaną tutaj pętlę i wróciły do komputera jako para liczb całkowitych ładnie oddzielona.
Krok 5: Przegląd szkiców / programów - struktura programu
Układ Jak we wszystkich szkicach Arduino są 3 podstawowe części:
- Deklaracje
- Ustawić
- Główna pętla
Jak to często bywa, wykorzystaliśmy tutaj czwartą sekcję, która jest dodatkiem „Funkcje”. Jeśli nie jesteś zaznajomiony z używaniem funkcji, możesz wyszukać w Google „funkcje Arduino”, a witryny z objaśnieniami, takie jak przykład, znajdziesz w tym linku: www.tutorialspoint.com/arduino/arduino_functions…..
Wykorzystaliśmy również zakładki, aby podzielić program na łatwiejsze w zarządzaniu bloki.
Trzy bloki, których użyliśmy, można zobaczyć na górze każdej ilustracji okien IDE powyżej:
- prostyRxTx0330Master
- pospolity
- notatki
W rzeczywistości są to oddzielne pliki w folderze programu, jak widać w tym widoku Eksploratora Windows plików programu Slave.
Jest bardzo dobry powód, dla którego to zrobiliśmy.
- Kiedy budowaliśmy program, zdaliśmy sobie sprawę, że większość programu dla Mistrza była taka sama jak dla Niewolnika.
- Skończyło się na wyciągnięciu wszystkich wspólnych części do zakładki, którą nazwaliśmy „wspólne”, a następnie za każdym razem, gdy debugowaliśmy część (przetestowaliśmy ją i byliśmy zadowoleni, że działa dobrze), po prostu kopiowaliśmy i wklejaliśmy całą kartę od Master do Slave lub odwrotnie.
- Zdarza się, że zakładki notatek są identyczne, ponieważ projekt jest ogólny.
Żadna z funkcji nie jest wywoływana z setupu, wszystkie są wywoływane z loopa, więc utworzyliśmy je po setup, ale przed pętlą.
Krok 6: Projektowanie odgórne
Dobrym pomysłem jest zaprojektowanie szkicu, zaczynając od definicji tego, co chcesz zrobić.
Kiedy już to masz, możesz zacząć robić szkice, wykonując te rzeczy. Ogólnie rzecz biorąc, jeśli jest jakiś szczegół, którego jeszcze nie wiesz, jak zrobić, po prostu ustaw go jako funkcję i zostaw tworzenie funkcji na później.
Jest to zgodne z filozofią dobrego projektowania, nauczaną na wielu uniwersytetach, zwaną CDIO (jeśli jeszcze tego nie znasz, możesz go wyszukać w Google i znaleźć strony, w których można to wyjaśnić, takie jak: https://www.cdio.org/s.) To w zasadzie mówi: Nie zaczynaj Projektu, zanim nie będziesz miał jasnego Konceptu. Nie rozpoczynaj implementacji, dopóki nie uzyskasz jasnego projektu. Nie oczekuj, że będzie działać, zanim nie uzyskasz jasnej implementacji. Najpierw C, potem D, I i O. Na każdym kolejnym etapie wykonujesz iterację (cofasz się o pętle), więc gdy będziesz zadowolony z początkowej pętli Projektu, sprawdź, czy nadal odpowiada Koncepcji, i zaktualizuj C, jeśli trzeba. I tak dalej, więc nawet jeśli masz już do pracy, idź z powrotem na górę i ponownie zobacz, jak teraz wygląda C, potem D i I, i wykonaj i sprawdź wszystko zmiany w razie potrzeby. W przypadku szkiców programowania działa to tak samo, jeśli projektujesz od góry do dołu.
Krok 7: Koncepcja i projekt - część 1
Koncepcja tutaj wygląda jak zarys wymagań określonych w zakładce „uwagi”.
Projekt może wyglądać jak wczesna wersja pętli, która pasuje do zakładki notatek i może wyglądać tak, jak widać na tym rysunku
Zobacz, jak lubię zacząć od faktycznego skopiowania komentarzy CTRL-C do nagłówka pętli, a następnie zacznij wypełniać puste miejsca poleceniami, które zrobią te rzeczy.
To faktycznie kompiluje się OK, jak widać na dole ekranu na rysunku. To sięga od etapu D CDIO do I, a gdy rozwijamy kod, dobrym pomysłem jest kontynuowanie tej pętli D-I.
Teraz pora przejść do następnego etapu, jest tam komentarz, który mówi, że: //odbierzemy coś ze sprzętowego USB, a następnie prześlemy to na programowy kanał szeregowy. Piszemy ten kod, aby tak się stało - linie od 133 do 138 pokazane tutaj żółtym wyróżnieniem
Krok 8: Koncepcja i projekt – część 2
Dwie pierwsze funkcje, które tutaj wprowadzamy, to (recv() i tran() do odbierania z portu sprzętowego i przesyłania do portu oprogramowania - stąd wywoływanie ich z wyświetlonymi parametrami 'hw' lub 'sw').
Oprócz nich dodaliśmy test na zmiennej globalnej o nazwie newData. To jest flaga, którą ustawimy wewnątrz funkcji " void recv(); ". Gdy wiadomość zostanie odebrana, zmienna ta jest oznaczona flagą z false na true. Robimy to tak, że przesyłamy wiadomość tylko wtedy, gdy została odebrana (flaga ==true) w linii 134. A kiedy już prześlemy naszą wiadomość, która jest „wykonana”, usuwamy flagę z powrotem na fałsz w linii 137.
Ponownie możemy sprawdzić kompilację (od D do I) i tym razem mamy komunikat o błędzie „nie zadeklarowany” (pokazany). To mówi nam, że nie zadeklarowaliśmy funkcji recv(); funkcjonować. Planujemy to zrobić później, więc na razie, aby umożliwić nam czystą kompilację, musimy utworzyć funkcję fikcyjną lub zastępczą, jak pokazano poniżej.
Ponownie możemy sprawdzić kompilację (od D do I) i tym razem mamy kolejny komunikat o błędzie „nie zadeklarowany” dla tran(); funkcjonować. To wymaga podobnego tworzenia kodu pośredniczącego. Ponownie możemy sprawdzić kompilację (od D do I) i tym razem stwierdzimy, że działa to doskonale; jak na razie dobrze.
Krok 9: Zakończ główną pętlę: A) Odbieranie z USB, B) Odbieranie ze Slave Arduino
Jest jeszcze jeden ostatni element, który dodaliśmy, aby zakończyć tę część, a mianowicie dodać trochę kodu debugującego.
Jest jeszcze inna instrukcja dotycząca debugowania szkiców, do której można się odnieść, aby zrozumieć, co tutaj zrobiliśmy i dlaczego. Zapoznaj się z instrukcją „Jak budować i testować szkice Arduino, dopóki nie zadziałają”
Tak więc te wiersze debugowania [136-139 pokazano] są dodawane następnie w głównej pętli i, lo i oto, można je przetestować na końcu Master, ustawiając zmienną debugowania true, i kompilując (I), a następnie, jeśli podłączasz Arduino, które możesz przesłać, otwórz monitor szeregowy i sprawdź, czy to, co wraca do monitora szeregowego, jest takie, jak pokazano tutaj (czy widzisz dodany komunikat „TRYB DEBUGOWANIA”?)
Krok 10: Odbieranie i obsługa danych w podrzędnym Arduino
Odbieranie od Slave Arduino
Dodaj niezbędny kod dla drugiego kanału do głównej pętli, programowego odbiornika szeregowego, jak pokazano - linie 149 do 155.
Czy widzisz, że struktura jest luźno oparta na tym, co napisaliśmy powyżej dla przypadku Master?
Zobaczysz również, że otrzymujemy błąd kompilatora, kolejną niezadeklarowaną funkcję - tym razem parseData(); - więc musimy zrobić skrót dla tego, zanim będziemy mogli uruchomić bezbłędną kompilację testową.
Obsługa danych w Slave Arduino
Dodaj kod pętli głównej wymagany dla Arduino, jeśli jest skonfigurowany jako urządzenie Slave, jak pokazano - linie 163 do 174. Czy widzisz, że jego struktura jest bardzo podobna do struktury pierwszego kanału?
I powinieneś się przekonać, że tym razem kompiluje się absolutnie dobrze.
Krok 11: Napisz funkcję odbioru
Funkcja Receive - void recv(char from){} - ma dwa główne zadania.
1, aby otrzymać ciąg znaków z kanału USB, oraz
2, aby otrzymać jeden z kanału Arduino na Arduino.
Do pierwszego będziemy musieli użyć, ponieważ wykorzystuje on wbudowany sprzętowy UART Arduino, a do drugiego za pomocą standardowej Biblioteki Arduino: oprogramowania UART.
Kiedy zaczynamy dodawać kod do funkcji - aby stworzyć funkcję, która coś robi, a nie tylko kod pośredniczący - musimy pamiętać o usunięciu lub zakomentowaniu zastępowanego kodu pośredniczącego. W przeciwnym razie otrzymujemy błąd kompilacji: refinyfikacja 'void lrec(char)'.
Spróbuj uzyskać błąd, a następnie wypróbuj jeden ze sposobów sugerowanych powyżej, aby się go pozbyć.
Zacznij od funkcji, która wygląda tak, jak pokazaliśmy tutaj wiersze od 75 do 88 w kolorze żółtym.
Wiesz już, że mając kod będziesz musiał spróbować operacji kompilacji. Daje ci błąd, taki jak te, które mieliśmy wcześniej, typu: nazwa funkcji nie zadeklarowanej w tym zakresie. Na początku będziemy potrzebować innego kodu pośredniczącego, który pozwoli nam skompilować po tym błędzie, więc dodaj go tak jak poprzednio i upewnij się, że możesz teraz uzyskać kompilację bez błędów.
Teraz spójrzmy na kod, który napisaliśmy dla funkcji recv().
Jest całkiem czysty, możesz zobaczyć użycie warunku 'if' do wytworzenia dwóch części funkcji, o której mowa powyżej.
Kod wewnątrz części 'sw' i części 'hw' ma tę samą formę i opiszę to tutaj.
Pierwsza z par linii w każdym przypadku jest początkiem pętli while. Jeśli nie znasz while, możesz poszukać wyjaśnień i przykładów na stronie Arduino.cc/Reference. Tutaj czekamy 'gdy' wbudowana funkcja 'Serial' nie otrzymała żadnych znaków i ponieważ zmienna newData została wyłączona (tzn. warunek newData == false jest spełniony). Jak tylko znak - lub więcej niż jeden znak - zostanie odebrany, while "przeskoczy" do drugiej linii w tej parze. Spowoduje to wywołanie funkcji recAstringChar(char); funkcja do obsługi bieżącego znaku. Ta para linii będzie się następnie zmieniać, dopóki (lub tak długo, jak) będą jakieś znaki, które nadal wymagają odbioru. Gdy wszystkie są skończone, stan while kończy się, zezwalając na przejście do następnego poziomu if lub else, a następnie zezwalając na rec(char); funkcja do końca. W ten sposób otrzymano pełną wiadomość.
Krok 12: Napisz podfunkcję odbierania - część 1
Teraz musimy napisać funkcję o nazwie recAstringChar(char);. Jak widać z komentarza do wiersza 50 na górze, jego zadaniem jest aktualizacja dwóch buforów kopiami przychodzącej wiadomości szeregowej. [Okazało się, gdy próbowałem to wszystko uruchomić, że jedną rzeczą, której się nauczyłem, było to, że potrzebuję dwóch różnych buforów - a przynajmniej był to najłatwiejszy sposób na obejście niektórych problemów, stąd tak jakby wyewoluował w potrzebę 2 buforów, więc Właśnie je zrobiłem.] Wywołałem jeden bufor: receiveData, a drugi: receiveChars.
Bufory są zmiennymi globalnymi, więc są deklarowane na poziomie modułu, patrz wiersze 9 i 10 wspólnej zakładki. Istnieją inne zmienne zadeklarowane wewnątrz tej funkcji, które w związku z tym mają zasięg lokalny - pokazany w wierszach 51-54 tutaj. To nie jest miejsce na wyjaśnianie różnic między globalami a lokalnymi, ale więcej informacji na ten temat można znaleźć na https://www.arduino.cc/glossary/en/ w sekcji Lokalne i Globalne.
Możesz również dowiedzieć się wszystkiego o typach danych i modyfikatorach typu: static, boolean, byte, const, char na stronie https://www.arduino.cc/reference/en/#variables, pokazanej tutaj.
Główny przepływ programu w tej funkcji jest kontrolowany przez if w wierszu 56 tutaj i odpowiadający mu else w wierszu 74. Dotyczy to dwóch scenariuszy
a) [od linii 74.], gdy rozpoczyna się odebrana wiadomość. Dzieje się tak, gdy startMarker jest zauważony - został on zdefiniowany jako znak „ <”, dlatego za każdym razem, gdy testujemy szkic, zawsze zaczynamy nasz ciąg od tego znaku. Jeśli tego nie zrobimy, nic nie zostanie przetworzone jako odebrane, wszystko zostanie zignorowane, tak jakbyśmy wpisywali bzdury po znaku zachęty klawiatury „Serial Monitor”.
b) [linie od 56 do 73], które odbierają wszystkie inne znaki, niezależnie od tego, jakie one są, ale zajmują się tylko znakami po prawidłowym uruchomieniu (odebrano '>' jak w a) powyżej.)
W tych wierszach (od 74 do 78) odebrane < umieszczamy w jednym z buforów (receivedData[0]), ale nie w drugim. Dopasowujemy wskaźnik bufora (zmienna: char ndx), aby wskazywał na następną zapasową pozycję bufora (receivedData[1]) za pomocą polecenia post-inkrementacji (++) w linii ndx++; i ustawiamy flagę w toku na true.
Przebieg programu w tej części funkcji jest kontrolowany przez if w wierszu 57. i odpowiadające mu else w wierszu 65. Dotyczy to dwóch scenariuszy
a) [od linii 65. wł.] gdy odebrana wiadomość zostanie zakończona. Dzieje się tak, gdy endMarker jest zauważony - zdefiniowany jako >, dlatego za każdym razem, gdy testujemy nasz szkic, zawsze kończymy nasz ciąg tym znakiem. Jedną z rzeczy, które mają miejsce po odebraniu znaku końca jest to, że flaga globalna (zmienna techniczna) newData jest ustawiana na wartość true w momencie zakończenia funkcji, tak że funkcja, która wywołała naszą podfunkcję (funkcja wywołująca: recv(char);) może 'wiedzieć', że prawidłowe nowe dane zostały odebrane jako kompletne.
b) [linie od 57 do 64], które odbierają wszystkie inne znaki, bez względu na to, jakie one są. Po prostu starannie parkuje je w rzędach w obu buforach.
Krok 13: Napisz podfunkcję odbierania - część 2
Pomocne może być podanie przykładu tego, jak wyglądają 2 bufory po ich zapełnieniu. Gdybyśmy wprowadzili enter, bufory miałyby pokazane w nich znaki:
Więc teraz widać, że mamy jeden bufor, który zawiera dokładnie te same znaki, które wpisaliśmy po raz pierwszy, i jeden bufor, który ma tylko dwie wartości i przecinek oddzielający. Teraz mamy kod, który może odbierać znaki, które wpisujemy na klawiaturze monitora szeregowego, możemy przejść z fazy I CDIO do O, wpisać kilka ciągów i zobaczyć, co się stanie. Prześlij kod do Master Arduino, otwórz Serial Monitor i spróbuj wpisać coś ważnego, np. Enter. Czy na ekranie monitora szeregowego pojawia się echo, takie jak pokazano tutaj?
Krok 14: Napisz funkcje przesyłania i analizowania
Najpierw dla przekazu
Więc teraz otrzymaliśmy napis, możemy napisać funkcję transmisji: tran(char); zastąpić jego odcinek. Umożliwi nam to przesłanie stringa z Mastera do Slave Arduino, więc upewnij się, że oba urządzenia są podłączone i połączone ze sobą, aby przetestować tę nową funkcję.
Wprowadź tę funkcję, jak pokazano tutaj w wierszach od 117 do 133. Jak zauważysz, ma dwie części, jedną do transmisji do kanału USB (sprzętowy UART), a drugą do przesyłania do drugiego Arduino (programowy UART). Powinno to spowodować błąd kompilacji -za darmo i możesz od razu przesłać szkic i zobaczyć, co się stanie. Tym razem wyślę. Czy otrzymujesz pokazany wynik?
Zrzut ekranu jest interesujący, ponieważ ciąg Received… powinien wyglądać poprawnie jak poprzednio, a ciąg Transmitted… powinien teraz wyglądać poprawnie. Należy jednak pamiętać, że konwersja liczb całkowitych nie zadziałała. Aby to zadziałało, trzeba jeszcze dodać trochę kodu.
Krok 15: Napisz funkcje przesyłania i analizowania
Następnie do Parse
Jest to fragment kodu, który analizuje otrzymany ciąg w celu pobrania liczbowych ciągów częściowych i konwertuje je na wartości całkowite. Jest to void parseData(); funkcja pętli głównej
Zastąp parse stub kodem pokazanym w wierszach 98 - 113. Prześlij go i zobaczmy, czy problem, który mieliśmy z 2 wartościami całkowitymi, został naprawiony. Spróbujmy.
Tak, działa, jak pokazano, znalezione liczby całkowite to 49 i 98.
Krok 16: Finał
Dane te krążą po pętli od komputera PC przez urządzenie nadrzędne do urządzenia podrzędnego iz powrotem przez urządzenie nadrzędne do komputera. Po załadowaniu gotowej wersji common do końcówek Master i Slave oraz przy wyłączonym teraz trybie debugowania, możemy zobaczyć dane poprawnie odebrane na obu końcach, jak pokazano tutaj.
Zalecana:
Wysyłanie danych bezprzewodowego czujnika wibracji i temperatury do Excela za pomocą Node-RED: 25 kroków
Wysyłanie danych z bezprzewodowego czujnika wibracji i temperatury do Excela za pomocą Node-RED: Przedstawiamy przemysłowy bezprzewodowy czujnik wibracji i temperatury Long Range IoT firmy NCD, który może pochwalić się zasięgiem do 2 mil przy użyciu bezprzewodowej struktury sieci kratowej. Wyposażony w precyzyjny 16-bitowy czujnik wibracji i temperatury, to urządzenie przek
Wysyłanie danych o bezprzewodowych wibracjach i temperaturze do Arkuszy Google za pomocą Node-RED: 37 kroków
Wysyłanie danych o bezprzewodowych wibracjach i temperaturze do Arkuszy Google za pomocą Node-RED: Przedstawiamy przemysłowy bezprzewodowy czujnik wibracji i temperatury Long Range IoT firmy NCD, który może pochwalić się zasięgiem do 2 mil przy użyciu bezprzewodowej struktury sieci kratowej. Wyposażony w precyzyjny 16-bitowy czujnik wibracji i temperatury, to urządzenie przek
Połączenie czujnika Infineon DPS422 z Infineon XMC4700 i wysyłanie danych do NodeMCU: 13 kroków
Łączenie czujnika Infineon DPS422 z Infineon XMC4700 i wysyłanie danych do NodeMCU: W tym samouczku nauczymy się używać DPS422 do pomiaru temperatury i ciśnienia barometrycznego za pomocą XMC4700. konsumpcja
System obecności poprzez wysyłanie danych RFID do serwera MySQL za pomocą Pythona z Arduino: 6 kroków
System Attendance poprzez wysyłanie danych RFID do serwera MySQL przy użyciu Pythona z Arduino: W tym projekcie skomunikowałem RFID-RC522 z arduino, a następnie wysyłam dane RFID do bazy danych phpmyadmin. W przeciwieństwie do naszych poprzednich projektów, w tym przypadku nie używamy żadnej osłony Ethernet, tutaj tylko odczytujemy dane szeregowe pochodzące z ar
Wysyłanie danych z bezprzewodowego czujnika temperatury i wilgotności dalekiego zasięgu IoT do arkusza Google: 39 kroków
Wysyłanie danych z bezprzewodowego czujnika temperatury i wilgotności dalekiego zasięgu IoT do arkusza Google: używamy tutaj czujnika temperatury i wilgotności NCD, ale kroki pozostają takie same dla każdego produktu ncd, więc jeśli masz inne bezprzewodowe czujniki ncd, możesz swobodnie obserwować obok. Za pomocą stopu tego tekstu musisz