Skocz do zawartości

Tablica liderów


Popularna zawartość

Pokazuje zawartość z najwyższą reputacją od 07.03.2020 we wszystkich miejscach

  1. 15 punktów
    Skonstruowałem sobie kalkulator, a co z CE? Problem przedstawiony w tytule jest często podnoszony przez amatorów elektroniki, którzy chcą i przede wszystkim potrafią skonstruować samodzielnie sprzęt elektroniczny. W tym miejscu doganiają ich pytania, czy w zasadzie mogą używać takiego produktu, czy mogą go pożyczyć koledze, a może jeżeli to taki dobry produkt, czy można go sprzedać na allegro, przecież to tylko kilka sztuk, a nie masowa produkcja na linii. Nakleję sobie CE, to będę bezpieczny, tak? Tytułowy kalkulator jest tylko przykładem produktu elektronicznego jednak ten artykuł dotyczy każdego innego produktu jak latarka, mini suszarka, a także robot. Kiedy konstruktora amatora dopadają pytania jak powyżej, to zaczyna zastanawiać się co z tym CE. Przecież wszystkie takie produkty w sklepie mają CE ( a przynajmniej powinny), to co z tym jego produktem. Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Tutaj należy zastanowić się skąd w zasadzie bierze się to CE, bo przecież nie są to tylko takie dwie literki, które się nanosi na produkty,…bo każdy tak robi. Skądś to się musi brać. I dochodzimy do genezy CE – oznakowanie CE czyli w pełnym brzmieniu Conformité Européenne (z francuskiego) jest to swoista deklaracja producenta, że produkt, który jest nim oznakowany spełnia wymagania dyrektyw Nowego Podejścia Unii Europejskiej. Dyrektywy Nowego Podejścia zostały wdrożone do europejskiego prawodawstwa w celu zapewnienia bezpieczeństwa produktów, a przez to umożliwienia swobodnego ich przepływu w obrębie Unii Europejskiej i krajów współpracujących. Obecnie wydanych jest ponad 30 Dyrektyw Nowego Podejścia dotykających wymagań dla różnych grup produktowych. Należy jednak pamiętać, że większość produktów znajduje się w zakresie nie jednej dyrektywy, a kilku dyrektyw. Stworzyłem budzik, co teraz? W tym miejscu dochodzimy do clue sprawy, czyli jak to się ma do produkcji na użytek własny. Tutaj należy się zastanowić jakie dyrektywy mogą ,,dotknąć” nasz produkt, gdyż każda z nich ma swój własny zakres i swoje wyłączenia. Rozważmy prosty produkt elektroniczny, powiedzmy sterowany przez połączenie radiowe jak na przykład budzik podłączony do prądu przez bezpośrednią wtyczkę (tak, wbrew pozorom to jest ważna informacja, gdyż podłączenie przez adapter, jak na przykład podłączenie laptopa jest już traktowane inaczej), z możliwością wyłączenia telefonem. Taki produkt może znaleźć się w zakresie dyrektyw LVD 2014/35/UE (Low voltage directive), EMC 2014/30/EU (Electromagnetic compatibility directive), RED 2014/53/EU (Radio directive) i RoHS 2011/65/UE (Ograniczenie stosowania niektórych niebezpiecznych substancji w sprzęcie elektrycznym i elektronicznym). Każda z powyższych dyrektyw ma swój własny zakres zastosowania, jak i również wyłączenia z zakresu, które w tym przypadku interesują nas najbardziej. I tutaj w dyrektywie EMC i RED możemy znaleźć wyłączenie dla urządzeń radiowych stosowanych przez radioamatorów w rozumieniu przyjętym w regulacjach radiowych przyjętych w ramach Konstytucji Międzynarodowego Związku Teleko­munikacyjnego i Konwencji Międzynarodowego Związku Telekomunikacyjnego, jeżeli urządzenie nie jest udostępnione na rynku – do meritum udostępniania na rynku przejdziemy w kolejnym akapicie. Więc trzeba teraz sprawdzić co znaczy to enigmatyczne wyłączenie z zakresu. Zgodnie z regulaminem radiokomunikacyjnym z 2016 roku służba amatorska jest to służba radiokomunikacyjna mająca na celu samokształcenie, wzajemne komunikowanie się i badania techniczne prowadzone przez amatorów, tj. przez odpowiednio upoważnione osoby interesujące się techniką radiową wyłącznie z pobudek osobistych, a nie w celu zarobkowym.[1] Tą definicję potwierdza nam jeszcze załącznik I dyrektywy RED, który mówi, że nieobjęte niniejsza dyrektywą są (oczywiście jeżeli nie są udostępnione na rynku): zestawy radiowe do montażu i użytku przez radioamatorów; urządzenia radiowe zmodyfikowane przez radioamatorów na ich potrzeby; urządzenia zbudowane samodzielnie przez radioamatorów, które służą celom eksperymentalnym i naukowym w ramach służby radiokomunikacyjnej amatorskiej.[2] Ufff… z tego wynika, że o wymaganiach EMC i RED możemy już zapomnieć. A co z LVD i RoHS? Tutaj nie znajdujemy takich bezpośrednich wyłączeń z zakresu, więc trzeba trochę bardziej pokombinować. Jeżeli chodzi o RoHS, to dotyczy on ograniczenia substancji niebezpiecznych w sprzęcie elektrycznym i elektronicznym, więc to wymaganie musieli też spełnić sprzedawcy naszych komponentów, a co za tym idzie skoro wszystkie komponenty są zgodne z RoHS to i nasz produkt będzie zgodny. Zostało LVD, więc tutaj dochodzimy do interpretacji definicji. Celem dyrektywy LVD jest zapewnienie spełniania przez znajdujący się w obrocie sprzęt elektryczny […]. Zgodnie z definicją wprowadzenie do obrotu oznacza pierwsze udostępnienie sprzętu na rynku Unii Europejskiej. Tutaj upewnia nas jeszcze w tym przekonaniu blue guide czyli przewodnik po europejskich przepisach dotyczących produktów, który mówi, że nie doszło do wprowadzenia do obrotu jeżeli produkt jest produkowany na własny użytek, przy czym zaznacza też, że część unijnego prawodawstwa obejmuje swym zakresem również te produkty, które są wytwarzane na własny użytek. Na szczęście w przewodniku możemy znaleźć też dalsze wyjaśnienie, że w przypadku gdy unijne prawodawstwo harmonizacyjne reguluje użytek własny, nie odnosi się to do sporadycznej produkcji na własny użytek przez osobę prywatną w kontekście niekomercyjnym. [3] Czyli jeżeli skonstruowaliśmy sami i używamy sami, jesteśmy po bezpiecznej stronie i ocena zgodności takich produktów nie musi już spędzać nam snu z powiek. To jeżeli ja mogę używać, to mogę też podarować w prezencie koledze? I tutaj zaczynają się schody. Chociaż logicznym może się wydawać, że skoro nie bierzemy za produkt pieniędzy to nie wprowadzamy go do obrotu jest to błędne myślenie. Definicja udostępniania na rynku mówi, że jest to dostarczenie sprzętu do celów dystrybucji, konsumpcji lub używania na rynku Unii w ramach działalności handlowej, odpłatnie lub nieodpłatnie.[4] To oznacza, że zgodnie z przepisami Unii Europejskiej produktu, który nie przeszedł oceny zgodności i nie jest odpowiednio oznakowany m.in. oznakowaniem CE nie możemy udostępnić nawet nieodpłatnie. Ale to jest świetny budzik, sprzedam jeden na allegro, kto mnie złapie! W Polsce funkcjonuje 15 organów nadzoru rynku, są to m.in. Urząd Ochrony Konkurencji i Konsumentów, Państwowa Inspekcja Pracy, Główny Urząd Nadzoru Budowlanego, Główny Inspektorat Sanitarny itd. Każda z tych instytucji jest upoważniona do kontrolowania produktów znajdujących się w zakresie ich kompetencji. Jeżeli organ nadzoru rynku podczas kontroli znajdzie produkt niezgodny (niespełniający wymagań lub niebezpieczny) to nałoży na osobę odpowiedzialną karę, która nie jest taka mała. Jednak trzeba mieć z tyłu głowy gorszy scenariusz. Co jeżeli nasz produkt spowoduje wypadek i dojdzie do uszkodzenia mienia lub ciała użytkownika? W takim wypadku będzie to już groźba odpowiedzialności karnej i ograniczenia wolności. Warto więc jeszcze dobrze przemyśleć takie ryzykowne pomysły. To chyba już wszystko wiem! Podsumowując, każdy produkt jest inny i trzeba go rozważać indywidualnie, gdyż nawet mała zmiana może spowodować, że powinniśmy już brać pod uwagę inne wymagania. Jednak większość prostych elektronicznych produktów, które są wytwarzane przez konstruktorów amatorów wyłącznie na swój własny użytek, jest po bezpiecznej stronie i nie trzeba się zastanawiać nad ich certyfikacją. Aczkolwiek należy pamiętać, że nawet kiedy podarujemy taki produkt babci pod choinkę, to w rozumieniu przepisów wprowadzamy go do obrotu i stajemy się producentem, na którym ciąży już obowiązek spełnienia m.in. wszystkich omówionych wymagań. [1] Regulamin Radiokomunikacyjny wydanie 2016 tom 1 – tłumaczenie Instytutu Łączności we współpracy z Departamentem Telekomunikacji Ministerstwa Cyfryzacji [2] DYREKTYWA PARLAMENTU EUROPEJSKIEGO I RADY 2014/53/UE z dnia 16 kwietnia 2014 r. w sprawie harmonizacji ustawodawstw państw członkowskich dotyczących udostępniania na rynku urządzeń radiowych i uchylająca dyrektywę 1999/5/WE [3] Zawiadomienie Komisji – Niebieski przewodnik – wdrażanie unijnych przepisów dotyczących produktów 2016 - 2016/C 272/01 [4] DYREKTYWA PARLAMENTU EUROPEJSKIEGO I RADY 2014/35/UE z dnia 26 lutego 2014 r.w sprawie harmonizacji ustawodawstw państw członkowskich odnoszących się do udostępniania na rynku sprzętu elektrycznego przewidzianego do stosowania w określonych granicach napięcia
  2. 8 punktów
    Algorytmy związane z szeroko pojętą sztuczną inteligencją (AI, Artificial Intelligence) są ostatnio bardzo popularnym tematem. Na ogół AI kojarzy się z ogromnym zapotrzebowaniem na moc obliczeniową, wykorzystaniem GPU (Graphics Processing Unit) oraz obliczeniami w chmurze. Okazuje się jednak, że sztuczna inteligencja znajduje również zastosowanie w świecie mikrokontrolerów. Powstał nawet termin na określenie takich zastosowań - Edge AI. O tym jak popularny jest temat AI świadczy chociażby pojawienie się sprzętowych akceleratorów przeznaczone dla urządzeń wbudowanych, tzw. TPU (Tensor Processing Unit), przykładowo Google Coral, albo Intel Neural Compute Stick. Dostępne są również mikrokontrolery z wbudowanym TPU, np. Kendryte K210 . Spis treści serii artykułów: Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.1 Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.2 Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.3 Firma STMicroelectronics, czyli producent popularnych również na naszym forum układów STM32 postanowiła dostarczyć produkt związany z AI. Na razie nie oferuje sprzętowej akceleracji, ale możemy za darmo pobrać pakiet oprogramowania o nazwie X-CUBE-AI, który pozwala na łatwe wykorzystanie możliwości jakie daje sztuczna inteligencja. W niniejszym artykule postaram się opisać bardzo prosty przykład wykorzystania biblioteki X-CUBE-AI na płytce ewaluacyjnej z układem STM32L475. Planowałem napisać dłuższy artykuł na zakończony niedawno konkurs, ale w trakcie przygotowywania projektu odkryłem jak dużo muszę się jeszcze sam nauczyć, zanim będę w stanie dzielić się wiedzą. Jednak to co udało mi się poznać i zrobić postaram się opisać w tym temacie. Proszę jednak o wyrozumiałość, tematem sztucznej inteligencji zajmuję się wyłącznie hobbistycznie i cały czas się uczę, więc z góry przepraszam za niedoskonałości opisywanego rozwiązania. Wybór platformy sprzętowej Sztuczna inteligencja nie bez powodu kojarzy się z ogromnym zapotrzebowaniem na pamięć i moc obliczeniową, rozpoznawanie mowy lub obrazu jest chyba dobrym przykładem, gdzie moc chmury wydaje się niezbędna. Okazuje się jednak, że AI można również zastosować w znacznie mniej wymagających obszarach. Jakiś czas temu na forum pojawił się opis projektu czujnika HRV: Wykrywanie bicia serca na podstawie sygnału jest jednym z przykładów, gdzie algorytmy sztucznej inteligencji mogą znaleźć zastosowanie. Ja co prawda nie jestem entuzjastą projektowania urządzeń medycznych metodami mocno hobbistycznymi, ale wspomniany artykuł zachęcił mnie do wypróbowania X-CUBE-AI do wykrywania wzroca w pewnym sygnale. Jako platformę testową wybrałem płytkę B-L475E-IOT01A, wyposażoną w mikrokontroler STM32L475 oraz całkiem sporo interesujących modułów peryferyjnych, a co najważniejsze w akcelerometr LSM6DSL. Płytka posiada ogromny potencjał, ja wykorzystam tylko mały jej fragment: port UART do komunikacji z PC, przede wszystkim do wysyłania danych akcelerometr posłuży jako źródło sygnału przycisk wykorzystam do uczenia sieci dioda LED będzie sygnalizowała wykrycie gestu Zbieranie danych treningowych Danymi źródłowymi będą pomiary z akcelerometru. Podczas uczenia, po wykonaniu gestu, który chcę nauczyć sieć będę naciskał przycisk na płytce. Dane treningowe mają więc postać 4 liczb: odczytu z akcelerometru dla osi XYZ oraz stanu przycisku. Ponieważ obraz jest wart tysiąca słów poniżej filmik z uczenia sieci: Moim celem było nauczenie sieci wykrywania dwukrotnego machnięcia płytką. W sumie nie wyszło mi to trenowanie do końca, bo wcale nie łatwo jest taki gest wielokrotnie wykonywać i się nie pomylić. Więc ostatecznie sieć ma wykrywać dwu- lub więcej- krotne pobujanie płytką. Jednokrotne, albo dwukrotne wykonane z odstępem gesty mają być ignorowane. Oczywiście można sieć nauczyć właściwie dowolnego gestu, pod warunkiem oczywiście, że sami jesteśmy w stanie ten gest wielokrotnie wykonać (i nie stracimy cierpliwości). Dane z akcelerometru odczytuję co 20ms. Z moich obliczeń wyszło, że 128 wyników, czyli 2.56s w zupełności wystarcza na wykonanie interesującego mnie gestu. Wyniki pomiarów wysyłane są przez port szeregowy Uczenie polegało na włączeniu zapisu danych do pliku oraz cierpliwym machaniu płytką oraz naciskaniu przycisku po wykonaniu uczonego gestu. W wyniku powstał plik data1.csv data1.zip Program do zbierania danych został napisany w środowisku STM32CubeIDE, dzięki temu jego kod będzie można później wykorzystać od testowania wytrenowanej sieci. Sam program jest właściwie banalny, do jego napisania wystarczy w zupełności materiał kursu STM32F4. Skoro mamy już dane treningowe, czas przystąpić do najważniejszego, czyli uczenia sieci. W następnej części postaram się opisać jak możemy wykorzystać zebrane dane. Spis treści serii artykułów: Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.1 Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.2 Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.3
  3. 8 punktów
    Wstęp Jeśli sięgnąłeś po ten artykuł, to prawdopodobnie jesteś programistą, a co więcej, chcesz być lepszym programistą! To bardzo dobrze, ponieważ rynek IT potrzebuje lepszych programistów, a wytwarzanie czystego kodu według przyjętych standardów programowania jest zdecydowanym krokiem w tę stronę. Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Czym właściwie jest czysty i dobry kod? Na to pytanie nie ma jednoznacznej odpowiedzi. Istnieje jednak zbiór pewnych reguł i narzędzi, które skutecznie wspomogą pracę programisty, a stosowane w całym zespole, polepszą jego efektywność i skuteczność. Spis treści serii artykułów: Czysty kod w praktyce - część 1 Czysty kod w praktyce - część 2 Jak więc tworzyć oprogramowanie pozbawione błędów? Podstawową regułą jest stosowanie właściwie dobranych nazw opisujących zmienne, funkcje czy struktury – jeśli kwestia ta nie jest spełniona, to żadna konwencja nie zda egzaminu. Nazwa taka powinna być odpowiedzią na wszystkie ważne pytania – w jakim celu istnieje, co robi, jakiego jest typu i jak jest używana. Jeżeli nazwa zmiennej wymaga komentarza, to znaczy, że nie ilustruje swojej intencji (dobry kod jest samokomentujący!). Przedrostki określające typy zmiennych Przykładowe przedrostki określające typy zmiennych: diGripperOpen – cyfrowy sygnał wejściowy mówiący, że chwytak jest otwarty; doGripperOpen – cyfrowy sygnał wyjściowy mówiący, aby chwytak został otwarty; aiTemperatureSensor – analogowy sygnał wejściowy czujnika temperatury; giTemperatureSensor – grupowy sygnał wejściowy czujnika temperatury (cyfrowy sygnał zakodowany na przykład na 16 bitach); nIndex – zmienna typu int zawierająca dane licznikowe; rRadius – zmienna typu real zawierająca promień; bMP3Module_On – zmienna typu bool informująca o załączeniu modułu mp3; Nie bądźmy dowcipni (ale tylko w kodzie ;d) Jeżeli nazwy są dowcipnymi określeniami, mogą być zapamiętane wyłącznie przez osoby z identycznym co autor poczuciem humoru i tylko dopóki dane określenie nie wyjdzie z mody. Nazwa zmiennych i funkcji powinny odzwierciedlać ich znaczenie. Dowcipne hasła z najnowszego standupu Rafała Paczesia zostawmy na integrację po pracy. Jedno słowo na pojęcie Należy stosować zasadę jedno słowo na jedno abstrakcyjne pojęcie i trzymać się jej w całym swoim programie. Na przykład diHumiditySensor. Nie twórzmy zgadywanek! Należy unikać używania różnych określeń do jednego celu. Powoduje to brak jednoznaczności. Czujnik zmierzchu oprogramowany w kodzie musi mieć jedną nazwę – używanie zamiennej terminologii jak czujnik optyczny, czujnik zmierzchu czy czujnik zmierzchowy powodują tylko bałagan w kodzie. Jeśli w systemie, który programujemy mamy kilka czujników, dla ich rozróżnienia nazwijmy je zgodnie z ich rolą i lokalizacją – nie stosujmy na przykład: aiHumiditySensor, aiMoistureSensor, aiWetnessSensor a na przykład aiHumiditySensor_Room, aiHumiditySensor_Kitchen, aiHumiditySensor_Bathroom. Dodajmy znaczący kontekst Jeśli mamy grupę danych, możemy posłużyć się różnymi zabiegami (w zależności od możliwości języka w jakim programujemy), aby panował w nich ład i porządek, grupując np. dane adresowe jako addrFirstName, addrLastName, addrState i tak dalej. Dzięki temu inni programiści będą wiedzieli, że zmienne te są częścią większej grupy danych. Możemy również użyć struktury, klasy – wszystko w zależności od języka programowania. Stosujmy terminologię klienta Jeśli tylko to możliwe, to do nazewnictwa zmiennych stosujemy terminologie klienta – jeśli istnieją dwa lub więcej terminów klienta na jedno pojęcie, wybieramy jedno i konsekwentnie się do tego stosujemy. Na przykład jeśli programujemy system do zarządzania produkcją u klienta, który wytwarza pakiety baterii z ogniw, używajmy zmiennych nBatteryIndex, bBatteryPresent itd. Podsumujmy dobre nazwy w kodzie przedstawiają intencje; bez mylących i fałszywych wskazówek; bez zbyt subtelnych różnic; są spójne; można je wymówić; łatwo je wyszukać; nazwy zmiennych mają rzeczowniki; nazwy funkcji mają czasowniki; dowcipne nazwy mogą nie być zrozumiane; dodajemy znaczący kontekst; nazwy zmiennych, funkcji, podprogramów w języku angielskim; zmiana nazwy na czytelniejszą to nic złego. Stosujmy dobry, konsekwentny styl Jak zapewnić tę czytelność? Po pierwsze: nazwy - zmiennych, stałych, funkcji, klas, metod, struktur itd. Każda nazwa powinna być znacząca, chyba że ma marginalne znaczenie. Jednoliterowe nazwy mogą się nadawać do zmiennych tymczasowych, takich jak np. zmienne pętli, ale tylko i wyłącznie wtedy. Nazwy powinny odzwierciedlać zastosowanie – zmienna przechowująca indeks tablicy może nazywać się „index”, a funkcja dodająca do siebie dwie liczby – „add”. Trudno się nie zgodzić, że są to o wiele czytelniejsze nazwy niż np. „dupadupacycki” czy „qwert”. Jeśli chodzi o dokumentację i komentarze, to lepiej jest używać długich nazw zmiennych (o ile dany język programowania na to pozwala), procedur, programów i funkcji, które dobrze określają ich znaczenie, zamiast używać krótkich nazw i zaopatrywać ich długim komentarzem. Poza tym długie nazwy pomagają w przypomnieniu sobie działania programu jeśli zaglądamy do niego po długiej przerwie. Jaki sens mają szczegółowe komentarze? Nie powinno się pisać kodu wyłącznie czytelnego, bądź wyłącznie optymalnego. Kod powinien być zrównoważony i wypośrodkowany – na tyle zoptymalizowany na ile nie straci na czytelności. Argument czytelności można w ogóle pominąć dopiero wtedy gdy wydajność kodu jest niewystarczająca, jednak wtedy należy taki kod opatrzyć stosownym komentarzem. Komentarze – kiedy nie mają sensu? Niewiele jest rzeczy tak pomocnych, jak dobrze umieszczony komentarz. Jednocześnie nic tak nie zaciemnia modułu, jak kilka zbyt dogmatycznych komentarzy. Nic nie jest tak szkodliwe, jak stary komentarz szerzący kłamstwa i dezinformację. Komentarze nie są szminką dla złego kodu! Jednym z często spotykanych powodów pisania komentarzy jest nieudany kod. Napisaliśmy funkcję i zauważamy, że jest źle zorganizowana. Wiemy, że jest chaotyczny. Mówimy wówczas: „Hm, będzie lepiej, jak go skomentuję”. Nie! Lepiej go poprawić! Precyzyjny i czytelny kod z małą liczbą komentarzy jest o wiele lepszy niż zabałaganiony i złożony kod z mnóstwem komentarzy. Zamiast spędzać czas na pisaniu komentarza wyjaśniającego bałagan, jaki zrobiliśmy, warto poświęcić czas na posprzątanie tego bałaganu i poprawić nasz kod - czytelny kod nie wymaga komentarza. Komentarze – kiedy mają sens? W wielu przypadkach stosowanie komentarza jest jak najbardziej słuszne. Często musimy zastosować komentarz prawny z powodu korporacyjnych standardów klienta. Czasami przydatne jest umieszczenie w komentarzu podstawowych informacji – na przykład typu czy informacji o wartości zwracanej zmiennej i wtedy warto zawrzeć taką informację w komentarzu informacyjnym. Na etapie projektowania często praktyką są komentarze TODO jako notatki zagadnień, które pozostały jeszcze „do zrobienia”. Formatowanie kodu Pamiętajmy, że kod powinien być ładnie sformatowany. Należy wybrać zbiór prostych zasad, które rządzą formatowaniem kodu, a następnie w konsekwentny sposób je stosować. Jeżeli pracujemy w zespole kilku programistów, to wszyscy jego członkowie powinni przyjąć zbiór zasad formatowania i stosować się do nich. Bardzo dobrą praktyką jest stosowanie pionowych odstępów pomiędzy segmentami kodu oraz wcięć – do poszczególnych programów w pliku, do funkcji i bloków funkcji. Z naszego programu należy również usunąć cały martwy kod – to znaczy taki, który nigdy nie jest wykorzystany (na przykład kontrolują go nigdy niespełniane warunki) lub taki, który mógł służyć jedynie do testów. Zamiast stosować magiczne liczby, użyjmy stałych nazywanych – na przykład liczba 86 400 powinna być ukryta w stałej SECONDS_PER_DAY. Jeżeli wyświetlamy 70 wierszy na stronę, to liczba 70 powinna być ukryta w stałej LINES_PER_PAGE. Spis treści serii artykułów: Czysty kod w praktyce - część 1 Czysty kod w praktyce - część 2
  4. 7 punktów
    Ki-Cad to program, a właściwie zbiór programów służących do tworzenia schematów elektrycznych i obwodów drukowanych. Cechuje się dużą możliwością personalizacji interfejsu i posiada kompleksowy zestaw narzędzi pozwalający na przeniesienie pomysłu na gotowy projekt układu. Pełny program można pobrać ze strony producenta: https://kicad-pcb.org/. Pobieramy program, instalujemy go i otwieramy. Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Tworzenie projektu Po otwarciu programu ukaże się nam takie oto okno: Tworzymy nowy projekt klikając ikonę z niebieskim folderem i nadajemy mu nazwę. W naszym przypadku będzie to “poradnik-forbot”. Gdy stworzymy nasz projekt razem z nim utworzą się dwa pliki. Pierwszy z rozszerzeniem “.kicad_pcb” służy do projektowania fizycznej wersji układu(tj. rysowania ścieżek na obwodzie drukowanym, stawiania footprintów itp.). Drugi plik z rozszerzeniem “.sch” służy do tworzenia schematu ideowego naszego układu(tj. tworzenie logicznych połączeń, dodawanie logicznym reprezentacji układów itp.). Najprostszym sposobem na stworzenie projektu jest rozpoczęcie go od schematu ideowego, gdyż gdy stworzymy schemat ideowy na schemacie fizycznym automatycznie pojawią się połączenia między footprintami, które ułatwią nam rysowanie ścieżek. Schemat ideowy i dodawanie elementów Schemat ideowy stworzymy za pomocą edytora schematów “Eeschema”. Po otwarciu ukaże się nam taki widok: Po zapoznaniu się z interfejsem możemy wejść w “Ustawienia strony”, aby zmienić jej wymiary, autora, datę itp. W tym projekcie spróbujemy zrobić prostego, okrojonego klona Arduino Pro Mini. Projektowanie warto rozpocząć od postawienia portów zasilania , wykonujemy to poprzez otwarcie interfejsu oznaczonego jako “D”. W “Dodawanie portów zasilania” znajdziemy możliwe do dodania porty zasilania. W moim naszym przypadku użyjemy portu 5V i GND. (widok po otwarciu okna “Dodawanie portów zasilania”) Dodanie portów zasilania w taki sposób jest istotne, aby program mógł poprawnie zinterpretować nasz układ. Gdy już mamy porty zasilania czas dodać elementy elektryczne, klikamy na przycisk “Dodaj Symbol”, ukaże się nam takie oto okno: Analogiczny do interfejsu “Dodawanie portów zasilania”, jednak z większą ilością zakładek. Najpierw dodamy ATmega 328p do naszego schematu. Jest to mikrokontroler używany w Arduino Pro Mini. Szukamy zakładki MCU microchip ATmega, rozwijamy ją i szukamy “ATmega328p-AU”. Skrót AU oznacza, że ten mikrokontroler jest w obudowie TQFP-32, taka sama jak w Arduino Pro Mini. Zaznaczamy i klikamy “ok”, aby dodać element do schematu. Taki element możemy podczas przesuwania obracać klawiszem R ,a także najeżdżając kursorem na element bądź napis kliknąć M ,aby poruszać elementem. Jest to wygodniejsze niż zaznaczanie gdyż zaznaczając możemy przez przypadek oznaczyć także inny element lub połączenie. Następnie, tak samo jak mikrokontroler dodajemy potrzebne rezystory , kondensatory oscylatory itd. Łączymy za pomocą narzędzia “Dodawanie połączeń”. Dodawanie Etykiet Podczas procesu łączenia elementów warto pomyśleć o dodaniu Etykiet. Etykiety pozwalają na połączenie wszystkich pinów do których mają być przyłączone. W ten sposób tworzy się sieć połączeń. Ma to dwie główne zalety w porównaniu do tradycyjnych połączeń. Po pierwsze schemat staje się bardziej czytelny, gdyż przy dużej ilości połączeń może wyjść nieczytelne spaghetti połączeń. Po drugie nadaje to nazwę naszej sieci połączeń, przyda się nam to szczególnie w trakcie projektowania samej PCB. Aby utworzyć Etykietę naciskamy przycisk “dodaj Etykietę globalną”. Nazywamy ją w polu “Etykieta” i umieszczamy na schemacie. Utworzyliśmy tym samym typ etykiety np.”VCC”. Aby dodać kolejną etykietę robimy to samo, tylko nie wpisujemy nazwy w pole etykieta, ale je rozwijamy. Wewnątrz powinna się znajdować nasza etykieta “VCC” wybieramy ją i klikamy “ok”. Tym sposobem dodaliśmy naszą etykietę do schematu. Podobnie jak elementy, etykiety można obracać klawiszem “R”. Teraz, gdy przyłączymy te etykiety do określonych pinów, program połączy je w sieć o nazwie “VCC”. Właściwości elementów i znacznik "niepołączone" Zaznaczając element i klikając przycisk “E” możemy wejść w właściwości danego elementu. Możemy tutaj edytować różne parametry, takie jak ułożenie, wyśrodkowanie napisów itp. A także wpisać wartość naszego elementu( tak jak na rysunku poniżej, oscylator o wartości 16 MHz). Zwróćcie uwagę na znak zapytania obok oznaczenia, oznacza on jeszcze nie nadaną numerację tego elementu. Ważne jest by każdy element był oznaczony, możemy to zrobić za pomocą pewnego narzędzia, które przedstawię wam później. Może się tak zdarzyć że niektóre piny np. w mikrokontrolerze nie będą przez nas używane i do niczego ich nie podłączymy. Należy wtedy użyć narzędzia "znacznik niepołączone" i oznaczyć nim nasze nieużywane piny. (Wtedy program nie będzie wskazywał błędów) (Wygląd gotowego schematu z ATmega328p) Gdy nasz układ jest już połączony zostały nam tylko trzy kroki. Są nimi: numeracja elementów, przypisywanie footprintów do elementów i generowanie listy sieci. Numeracja elementów: Klikamy przycisk “Numerowanie symboli na schemacie”, ukaże nam się taki widok: Klikamy “Numeruj”, narzędzie automatycznie ponumeruje nam elementy na schemacie. Przypisywanie footprintów do elementów: Elementy na schemacie są tylko ideowe, więc trzeba przypisać do nich footprinty, czyli fizyczne miejsca dla elementów na obwodzie drukowanym. Robimy to za pomocą przycisku “Przypisywanie footprintów do symboli”. Następnie ukazuje się nam takie oto okno: W pierwszej kolumnie mamy listę folderów zawierających footprinty, w drugiej symbole z naszego schematu, a w trzeciej rozwinięty, aktualnie otwarty, folder z lewej kolumny. ATmega328p-AU ma już przypisany swój footprint, z uwagi na dostępny tylko ten konkretny model obudowy. Możemy to zmienić, lecz ta obudowa jest w pełni kompatybilna. Do reszty elementów musimy konkretne footprinty przypisać sami. Wyszukujemy w pierwszej kolumnie odpowiedni folder, zaznaczamy go i w trzeciej kolumnie wyszukujemy footprint, który jest nam potrzebny. Klikamy dwa razy lewy przycisk myszy, aby przypisać footprint do naszego symbolu. Jeśli potrzebujemy mieć wgląd w dokładny wygląd footprintu, możemy go zaznaczyć prawym przyciskiem myszy i wyświetlić jego fizyczne przedstawienie na płytce. (Wygląd okna z przypisanymi footprintami) Generowanie listy sieci Gdy już to skończymy musimy jeszcze wyeksportować listę sieci z naszego schematu do pliku , które program Pcbnew (Ten w którym robimy już fizyczny obwód) mógł odczytać nasz schemat. Klikamy więc po zamknięciu przypisywanie footprintów klikamy przycisk “Generowanie listy sieci”. Zaznaczmy zakładkę Pcbnew i klikamy “generowanie listy sieci” . Plik ten zostanie utworzony w folderze naszego projektu. Pcbnew i właściwe tworzenie układu drukowanego Następnie otwieramy program Pcbnew , aby rozpocząć właściwe tworzenie obwodu drukowanego. Po otwarciu programu ukaże się nam taki oto widok: Interfejs jest bardzo podobny do programu Eeshema. Domyślnie program obsługuje dwie warstwy miedzi (warstwa F i warstwa B). Możemy to zmienić wchodząc w “Ustawienia projektowe płytki”. Kiedy zapoznamy się z interfejsem “Pcbnew” możemy przystąpić do załadowania listy sieci, którą zrobiliśmy przed chwilą. Klikamy “Załaduj listę sieci”, ukaże się nam taki widok: Klikamy na ikonkę folderu w prawym górnym rogu i wybieramy naszą wcześniej utworzoną listę sieci. Następnie klikamy przycisk “Uaktualnij PCB”. Dzięki temu wszystkie footprinty, które dodaliśmy do schematu zostaną dodane do naszego obwodu drukowanego. Powinno to wyglądać mniej więcej tak: Jak widać wszystkie footprinty zostały dodane. Dodatkowo zostały utworzone linie pomocnicze, które mówią nam co powinno być sobą połączone na podstawie schematu. Jest to bardzo pomocne, jeśli chcielibyśmy coś dodać do naszego układu, ale nie usuwać istniejących footprintów i połączeń. Możemy jeszcze raz załadować listę sieci. Wtedy zostaną zaaplikowane poprawki, które wykonaliśmy na schemacie. Pamiętajmy także o opcji “Odbuduj połączenia wspomagające”, aby zostały one uaktualnione. Ustawienia obwodu drukowanego Zanim zaczniemy rysować ścieżki warto jeszcze dostosować ich parametry . Robimy to wchodząc w “Ustawienia obwodu drukowanego”. Możemy tam dostosować różne parametry naszego obwodu drukowanego, np.szerokość ścieżek, odległość między ścieżkami itp. oraz przypisać te właściwości do konkretnych sieci w naszym układzie. W naszym przypadku dodamy tylko dwie klasy sieci domyślną oraz zasilania(trochę grubszą z uwagi na większe możliwe prądy przepływające przez te ścieżki). Przypiszemy też klasę zasilania do sieci GND i 5V. Klikamy “ok” i od teraz program automatycznie przełączy nam właściwą szerokość ścieżki w zależności od sieci którą będziemy rysować. Skróty klawiszowe Pcbnew Przejdźmy do właściwego układania footprintów i rysowania ścieżek. Skróty klawiszowe są podobne jak w programie “Eeschema”, też klawisz “M” używamy aby przemieścić obiekt, “R” aby obracać obiekt. Ważny skrót klawiszowy to “V”, gdyż nim przełączmy między warstwami miedzi. Można też, gdy trzymamy obiekt tym klawiszem, przełożyć go na drugą stronę płytki oraz podczas rysowania ścieżki możemy utworzyć przelotkę. Jest to mała metalizowana dziura która pozwala na przejście wertykalne przez płytkę i np. połączyć elementy na dwóch różnych warstwach, stworzyć przejście pod ścieżką która nas blokuje na innej warstwie itp. Istnieje możliwość przełączania się tym klawiszem między warstwami, gdy nie jest zaznaczony żaden element. Strefy Warto pomyśleć o dodaniu warstw miedzi jako masę na całej dolnej warstwie naszej płytki. Nie tylko ułatwi to nam dołączanie elementów do uziemienia ale także zapewni proste ekranowanie układu. Robimy to za pomocą narzędzia “Dodaj strefy”. Naszym oczom ukaże się takie oto okno: Zaznaczamy warstwę “B.Cu” oraz wybieramy sieć “GND”, resztę ustawień pozostawiamy domyślne. Następnie klikamy “OK”. Potem fizycznie musimy narysować obrys strefy na naszym układzie. Strefa następnie zostanie dodana na całym obrysowanym przez nas obszarze. Musimy też pamiętać że podczas rysowania w obszarze strefy nie aktualizuje się on w czasie rzeczywistym i musimy po dokonaniu jakiś zmian nacisnąć klawisz “B”, aby na nowo wypełnić strefę (np. strefa usuwa się z utworzonych przez nas ścieżek, elementów oraz wypełniła puste miejsca na płytce) Po tym możemy już spokojnie połączyć wszystkie elementy układu. Oto efekt: (Gotowy układ z uruchomioną kontrolą DRC. Wszystko jest dobrze , nie ma błedów) Warto po zakończonej pracy sprawdzić układ kontrolą DRC, aby upewnić się że wszystko jest odpowiednio połączone, itp. Należy też pamiętać że : Ścieżki powinny zginać się pod kątem 45 stopni nie 90 stopni Napisy warstwy opisowej (np. C1 , R2 itp. zaznaczone kolorem zielonym) nie powinny się znajdować na padach lutowniczych naszych footprintów) (Przegląd 3D naszego układu) Po kliknięciu przycisku ALT+3 możemy włączyć widok 3D naszej płytki , aby zobaczyć jak wygląda i czy napisy są umieszczone czytelnie. Eksportowanie projektu Na końcu, aby wyeksportować naszą płytkę tak, aby fabryka PCB mogła ją zrobić należy wejść w “Rysuj”. Ukaże się nam takie okno. Zależnie od fabryki, w której będziemy chcieli wyprodukować płytkę, możemy użyć różnych formatów projektu. Zazwyczaj potrzeba plików w formacie GERBER. Zaznaczamy, więc warstwy jakie chcemy wyeksportować, wybieramy format wyjściowy i folder wyjściowy(jeśli tutaj nic nie wybierzemy program utworzy nam pliki w folderze projektu). Następnie klikamy “Rysuj”, a potem “Generuj pliki wierceń”, aby stworzyć pliki wierceń (wyświetli nam się podobne okienko w którym możemy wybrać format wyjściowy itp.). I takim sposobem stworzyliśmy swoją pierwszą płytę w KiCadzie. Dziękuję za uwagę! Aleksander Flont
  5. 6 punktów
    StealthMamut to nasz drugi robot minisumo. Robot startował na zawodach ze zmiennym szczęściem. Ale miał swoje chwile chwały Największe sukcesy to 2 miejsce w Rzeszowie oraz 1 miejsce na RA w 2014 roku. Od konceptu do realizacji U początku całej koncepcji znalazł się pomysł, żeby zbudować robota, który będzie słabo wykrywalny przez czujniki przeciwnika, czyli technologia znana jako Stealth Pierwsze testy polegały na sprawdzeniu sposobu reakcji czujników Sharp (najpopularniejsze) gdy skierujemy je pod kątem ok. 45 stopni na lustro. Zgodnie z przewidywaniem promienie były odbijane przez lustro i czujnik lustra nie wykrywał. Zrozumieliśmy, więc że robot z obudową w kształcie piramidy z luster ustawionych pod kątem będzie odbijał promienie czujników i będzie teoretycznie w 100% niewidoczny. Pierwszy projekt powstał w sketchupie na wiosnę 2013 i był to 4 kołowy robot, z 4 silnikami, 3 czujnikami Sharp GP2Y0D340K. Bryła robota byłą w kształcie ściętego ostrosłupa o podstawie ośmiokąta. Projekt ten był ciekawy, natomiast bardzo trudny w realizacji. Dlatego zawiesiliśmy prace i pomysł musiał dojrzeć. Pod koniec 2013 roku koncepcja zaczęła się krystalizować. Pierwszym krokiem było uproszczenie projektu o następujące założenia: Robot 2 kołowy z dwoma silnikami – dzięki temu było dużo więcej miejsca oraz odpadał problem docisku noża pojawiający się przy 2 osiach Tylko 1 czujnik przeciwnika Powstał kolejny projekt w Sketchupie, oraz film animowany prezentujący koncepcje: Aby projekt można było urzeczywistnić, kluczową sprawą było znalezienie dostawcy odpowiednich luster. Stanęło na lustrach z plexi z firmy Audioplex. Były one w miarę łatwe w obróbce i wytrzymalsze niż lustra szklane. Zakupiliśmy arkusz luster i okazał się on odpowiedni do naszych potrzeb. Gdy mieliśmy już materiał wykonany prototyp obudowy z zakupionych „luster”, tak aby sprawdzić skuteczność koncepcji Stealth. Okazało się, że koncepcja stealtch działa Oczywiście w praktyce prototyp nie był w 100% niewidzialny – szczególnie na krawędziach. Natomiast był na tyle niewidzialny że technologię uznaliśmy za wartą przeniesienia na prawdziwego robota. Robot został stworzony w rekordowo krótkim czasie kilku tygodni. Bardzo pomogły doświadczenia z poprzednimi robotami. W pierwszych kilku zawodach robot borykał się z chorobami wieku dziecięcego (zarówno mechanicznie jak i program), ale po kolei wszystko zostało dopracowane i mógł stanąć w szranki z najlepszymi. Projekt 3D Robot był od początku do końca szczegółowo zaprojektowany w 3D w programie SketchUp.. Względem projektu z końca 2013 dodaliśmy dodatkowy czujnik (w sumie 2 czujniki) oraz został wybrany akumulator (Dualsky). Zostało też zaprojektowane całe wnętrze tak aby sprawdzić czy wszystkie części na pewno się mieszczą. Po dopracowaniu projektu ogólnego, dolna stalowa podstawa została zaprojektowana w programie Inventor. Elektronika Podstawowe elementy wykorzystane w układzie to: Procesor Atmega 16 - sterowanie całym robotem 2 czujniki 40 cm Sharp GP2Y0D340K - czujniki przeciwnika ustawione na wprost. 2 czujniki linii – CNY70 - do wykrywania linii oczywiście. TB6612– dwa mostki dla silników głównych, sterowany sygnałami PWM z procesora Stabilizator napięcia LM1117 dla zapewnienia 5V zasilania dla układów. Ze względu na kształt robota, podzieliliśmy elektronikę na 3 płytki. Dla oszczędzenie miejsca zastosowaliśmy w większości elementy typu SMD. Płytka główna wypełnia przestrzeń w obudowie i jest bazą dla procesora oraz zapewnia złącza dla czujników linii. Diody na tej płytce wykorzystywane były tylko do celów testowych. W pierwotnej koncepcji zasilanie czujników miało być wyłączane w pewnych fazach walki tak aby czujniki nie emitowały wiązki i tym samym zdradzały pozycji. Okazało się jednak, że przy obecnym tempie walk dużo ważniejsza jest świadomość sytuacyjna niż jakikolwiek niewielki efekt wyłączenia czujników. Schemat głównej płytki poniżej: Środkowa płytka jest bazą dla mostków dla silników oraz stabilizatora. Schemat poniżej: W końcu górna płytka jest tylko bazą dla złącza programatora, przycisków, diod oraz głośnika. Ciekawa jest geneza wykorzystania głośnika. Ze względu na górną obudowę, która jest także z lustra nie widać na zewnątrz żadnych diod. Tak wiec aby wybrać program startowy używamy przycisku po naciśnięciu którego liczba piknięć głośnika mówi jaki program został wybrany. Oczywiście obecnie większość robotów ma tak nisko czujniki że nie omiata wiązką górnej powierzchni, ale 1 lub dwa takie się zdarzały. Dodatkowo użycie u góry lustra poprawia estetykę całego robota. Schemat górnej płytki poniżej: Generalnie elektronika spełnia dobrze swoje zadania, chodź można było na pewno jeszcze bardziej upchać elementy oraz zastosować lepsze taśmy to łączenia sygnałów pomiędzy poszczególnymi płytkami. Płytki PCB Płytki zostały wykonane w Eaglu. Ich wykonanie zostało zlecone do firmy, która wykonuje PCB, a lutowanie było już po naszej stronie. Najtrudniejszym elementem było przylutowanie procesora oraz mostków, ale udało się to zrobić. Łączenie pomiędzy płytkami zostało wykonane na taśmach z goldpinami i jest to chyba najgorzej wykonany element robota. Płytki zostały osadzone na 2 długich śrubach przytwierdzonych do podłoża. Zasilanie Zdecydowaliśmy się na zasilanie 11,1 V z 3 celowego pakietu Dualsky 400 mAh. Odpowiednie napięcie dla układów elektronicznych (5V) zapewnia stabilizator liniowy. Jeśli chodzi o silniki to PWM tak dobiera napięcie średnie aby ich nie spalić (max 9V). Mechanika Podstawa robota wykonana jest z stalowej blachy i stanowi oparcie dla ścian obudowy z luster. Niestety ze względu na problemy technologiczne elementy podpierające lustra prościej nam było zrobić z balsy, przez co podstawa jest trochę za lekka i trzeba było próbować dociążyć robota dodatkowymi odważnikami z ołowiu. Niestety udało się to tylko połowicznie ponieważ nie było tyle miejsca. Cięższy materiał do dociążenia (np. iryd czy osm, czyli metale o większej gęstości) jest niestety cenowo poza zasięgiem. Silniki główne to Pololu 30:1. Są one bardzo dobre dla tego robota, ponieważ jest on przez to szybki i może wykonywać zwody i manewry okalające szczególnie w pierwszej części walki. Niestety w zwarciu przegrywają z silnikami 50:1. Koła sami odlewamy koła z silikonu co bardzo dobrze wpływa na przyczepność robota. Niestety sama wymiana kół jest trudna dlatego nie zawsze są czas i chęci na zrobienie tego przed zawodami co skutkuje gorszymi parametrami podczas walk. Obudowa typu Stealth W praktyce obudowa typu stealth nie jest tak idealna, jak w założeniach. Można jednak powiedzieć, żę w miarę dobrze spełnia swoją rolę. Niestety są elementy, które ułatwiają lokalizacje robota: Otwory na czujniki Metalowa podstawa (kilka mm od podłogi) 2 śruby po bokach przytrzymujące obudowę Dodatkowo w trakcie walk w kilku miejscach lustra zostały porysowane przez noże innych robotów. Naprawienie tego wymagało by wymiany obudowy, ale jest to bardzo czasochłonne wiec nie zostało to nigdy zrealizowane. Program Program bazuje na programie z moich poprzednich robotów – FlyingMamut oraz MicroMamut. Został stworzony jest w oparciu o konkretne reguły odpowiadające staną czujników oraz stanowi logiki. Na podstawie stanu czujników i stanu obecnego ustalany jest stan następny. Program został napisany w języku C. Używałem WinAVR. Programuję przez USBAsp. Do debugowania programu używam złącza RS-232, którego moduł z układem MAX232 podpisany jest dodatkowo do robota. W toku prac i testów program został rozwinięty o specjalne manewry używane przy starcie robota do walki, tak aby korzystając z „niewidzialności” mógł zajść przeciwnika od tyłu. Niestety część robotów używa najprawdopodobniej algorytmów „na ślepo” atakujących w najbardziej prawdopodobne miejsce gdzie będzie nasz robot, a na małej planszy 77cm nie ma dużego pola manewru. Z powyższego powodu dobrze szło naszemu robotowi na planszy 144 cm na zawodach Robotic Arena we Wrocławiu. Dodatkowym mankamentem jest to, że gdy nasz robot ustawia się przodem to jest bardziej widoczny dla przeciwnika. Podsumowanie Jesteśmy bardzo zadowoleni z tego robota szczególnie, dlatego że udało się wprowadzić do zawodów kolejnego po FlyingMamucie robota, który nie bazuje tylko i wyłącznie na czystej sile i mocnym, ostrym klinie. Oprócz dobrej zdolności do walki, nasz robot jest też jednym z robotów o najczystszej formie i najlepszym designie (zewnętrznym). Dużym komplementem dla nas było to, gdy jeden z sędziów na pierwszych zawodach tego robota w Wałbrzychu powiedział „Nie myślałem jeszcze jakiś robot zaskoczy mnie w zawodach Minisumo”. Sukcesy tego robot sprawiły, że cały czas myślimy nad kolejnym nietypowym robotem, który mógłby walczyć o zwycięstwo
  6. 6 punktów
    Pracuję sam, po co mi kontrola wersji? Jeżeli słyszałeś już o Gicie, prawdopodobnie wiesz że jest doskonałym narzędziem w projektach nad którymi pracuje wiele osób. A co jeśli pracujesz sam? Czy w takim razie Git ma coś do zaoferowania? Tak. Nie musisz nawet łączyć się z internetem żeby korzystać z jego dobrodziejstw. Pomyśl o następujących scenariuszach: Chcesz wrócić do wersji kodu sprzed 2 tygodni, czwartek o 17:12. Albo do wczorajszej, bo wczoraj jeszcze działało a jesteś absolutnie pewien że nic nie ruszałeś Nie pamiętasz po co modyfikowałeś dany plik, albo nawet konkretną linię Fragment programu nie działa, nie wiesz od kiedy. Git pozwoli Ci znaleźć konkretną zmianę, która spowodowała błąd Chcesz wykonać mały eksperyment na boku, bez obaw że zepsujesz "właściwą" wersję programu Chcesz móc sprawdzić jak wyglądały Twoje postępy w projekcie Chcesz łatwo opublikować swój kod w serwisie GitHub aby się nim pochwalić lub pozwolić skorzystać komuś innemu. We wszystkich tych miejscach Git oferuje swoje wsparcie. Dlatego powstał ten artykuł - aby pomóc osobom które piszą programy jako dodatek do swojej pracy w poznaniu podstaw gita i skorzystania z jego dobrodziejstw. Nie ma żadnych specjalnych wymagań - każdy powinien być w stanie skorzystać Jeżeli jesteś zaawansowanym użytkownikiem gita to nic ciekawego tutaj nie znajdziesz, uprzedzam Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Model pracy Praca z gitem opiera się na użyciu repozytoriów. Repozytorium to "baza danych" przechowująca informacje o aktualnym oraz historycznym stanie projektu. Na potrzeby początkujących można założyć że jeden projekt = jedno repozytorium. Repozytorium może być lokalne lub zdalne. Lokalne to takie które posiadasz na swoim komputerze. Zdalne, to repozytorium znajdujące się w innej lokalizacji (np. Na githubie) do którego istnieje odwołanie w repo lokalnym. Dzięki temu możliwa jest współpraca nad jednym projektem przez wiele osób. Podstawową jednostką pracy jest commit. Jest to zapisany stan repozytorium w danym momencie. Trochę jak spakowany katalog projektu tylko bardziej inteligentny. Zdecydowana większość codziennej pracy z gitem opiera się na tworzeniu/przeglądaniu i modyfikowaniu commitów. Commit składa się z kilku elementów, m.in.: Zmian które wprowadza Autora Daty utworzenia Komentarza Wartości hash SHA-1 Listy rodziców Commity są unikalne rozróżnialne na podstawie hasha SHA-1. Jest on dosyć długi - w praktyce używa się jego skróconej wersji (pierwsze 6/8 znaków). Commity mogą też dostać dodatkowe identyfikatory poza hashem - będę je nazywał etykietami. Jest kilka rodzajów etykiet. Jedna z nich to tagi. Wyobraź sobie że twój projekt osiągnął wersję 1.0 i chcesz łatwo dotrzeć do konkretnego commita zawierającego tą wersję - możesz stworzyć tag v1.0, v1.0.1 etc aby łatwo to śledzić. Przykładowa lista commitów oraz tagów z projektu PyTorch - widać hashe, autorów, daty oraz komentarze. Innym rodzajem tagów są tzw. gałęzie o których w tym tutorialu będzie po macoszemu (gałęzie są szerokim tematem i na początek do samodzielnej pracy nie są niezbędne). Drzewo Każdy commit (poza pierwszym) ma przynajmniej jednego rodzica - tzn. commit na bazie którego powstał. Tworzy to strukturę podobną do drzewa. Poniższy rysunek przedstawia dwa przykładowe drzewa. Górne jest liniowe, dolne posiada rozgałęzienie. W obu przypadkach commit A jest pierwszym (jako jedyny nie posiada rodzica). Strzałka od X do Y oznacza "X jest potomkiem Y". Dziś skupimy się na przypadku z liniową historią, nie wszystko na raz To co muszę powiedzieć o gałęziach to, że zawsze istnieje domyślna gałąź o nazwie master. W przypadku całkowicie liniowego projektu będzie to jedyna istniejąca. Co to dla nas oznacza póki co? Ostatnio utworzony commit zawsze będzie miał dodatkową etykietę "master" co ilustruje poniższy schemat: Nowo utworzony commit przejmuje etykietę z gałęzią rodzica (ale tylko gałęzią). Dość teorii (na razie) - przejdźmy do praktyki. Instalacja i konfiguracja Będziemy potrzebowali dwóch rzeczy Konto w serwisie GitHub - https://github.com/ - poradzisz sobie sam Git dla Twojego systemu operacyjnego. Pokażę Ci jak go zainstalować dla systemu Windows - https://git-scm.com/downloads Instalacja standardowo polega na klikaniu next. Większość opcji wystarczy pozostawić domyślnymi, proponuję zmienić tylko domyślny edytor tekstowy (git czasami będzie go otwierał, sugeruję wybrać notepad++ zamiast vim). Po instalacji git będzie dostępny z każdego okna konsoli. Osobiście polecam korzystać z Git bash, który instaluje się automatycznie. Aby to zrobić kliknij PPM w dowolnym folderze i wybierz "Git Bash here". Nie ma potrzeby nic konfigurować, ja jedynie zmieniam czcionkę na większą (ppm na pasku tytułu Options/Text). Git bash emuluje podstawowe zachowanie linuksa, dlatego folder w którym jesteś zobaczysz jako np. /d/code zamiast D:\code. (Wszystkie komendy będziemy wykonywać w oknie git bash) Potrzebujemy ustawić dosłownie dwie opcje konfiguracyjne - nazwę użytkownika oraz email. Aby to zrobić wywołaj dwa polecenia (bez znaków ">" oznaczają one które polecenie powinieneś wpisać, linie bez tego znaku są wyjściem zwracamym przez gita): > git config --global user.name "NAZWA UŻYTKOWNIKA" > git config --global user.email "adres@email" Nazwa użytkownika pojawi się przy każdym commicie razem z adresem email. Adres ustaw ten sam na który zarejestrowałeś konto na GitHub. Polecenie git config służy do ustawiania/podglądania opcji konfiguracyjnych. Przełącznik --global oznacza, że chcemy ustawić opcję dla wszystkich repozytoriów - możliwe są różne ustawienia dla każdego repozytorium z osobna. Na końcu znajduje się opcja do ustawienia oraz jej nowa wartość. Projekt Github Aby uprościć nieco sprawę repozytorium załóżmy od razu na githubie. Klikamy na ikonę plusa w prawym górnym rogu i wybieramy new repository. Wpisujemy jego nazwę i zaznaczamy Initialize this repository with a README - github utworzy automatycznie pierwszy commit z readme co nam nieco ułatwi start. Po kliknięciu na Create repository repozytorium zostanie utworzone. To jest nasze zdalne repo. Musimy teraz utworzyć lokalne na jego bazie. Klikamy Clone or download, wybieramy Use HTTPS i kopiujemy URL który się pojawi. Klonowanie repozytorium W oknie git bash przechodzimy do folderu w którym trzymamy projekty (u mnie jest to D:/code) i wpisujemy > git clone SKOPIOWIANY_URL Cloning into 'git-forbot-tutorial'... remote: Enumerating objects: 3, done. remote: Counting objects: 100% (3/3), done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 Unpacking objects: 100% (3/3), 607 bytes | 3.00 KiB/s, done. Git utworzył nowy katalog z projektem (sklonował ze zdalnego repozytorium), wejdźmy do niego (cd nazwakatalogu) u wywołajmy polecenie git status. > git status On branch master Your branch is up to date with 'origin/master'. nothing to commit, working tree clean Czytając po kolei: Jesteśmy w gałęzi master (domyślnej) Nasza lokalna gałąź jest aktualna względem zdalnej (origin/master) Nie mamy żadnych zmian widocznych dla gita Od razu warto wyjaśnić czym jest origin - jest to nazwa zdalnego repozytorium. W naszych przykładach origin będzie odnosić się do naszego zdalnego repo na githubie. Odniesienia do zdalnych repo mogą się nazywać dowolnie (i może być ich więcej) origin jest standardową konwencją. Zobaczmy jeszcze wynik polecenia git log > git log commit e95e01842542a0a3a0380a34d863b906838ba1d4 (HEAD -> master, origin/master, origin/HEAD) Author: Mateusz Bednarski <mateusz.bednarski@windowslive.com> Date: Sat Mar 14 23:58:16 2020 +0100 Initial commit Wyświetla ono listę commitów. Widzimy wymienione wcześniej elementy. Warto jeszcze wspomnieć czym jest HEAD - jest to etykieta oznaczająca "aktualny" commit - czyli ten który będzie rodzicem dla nowo utworzonego (po jego utworzeniu HEAD przesunie się automatycznie). Jest jeszcze origin/HEAD ale tym nie musisz się na razie przejmować. Pierwszy commit Najwyższa pora utworzyć pierwszy commit. Przykład będzie w zwykłym C - dla gita nie ma to absolutnie żadnego znaczenia. Tworzymy plik main.c o treści: #include <stdio.h> int main(){ puts("Hello world"); return 0; } Zapytajmy teraz gita o status repozytorium: > git status On branch master Your branch is up to date with 'origin/master'. Untracked files: (use "git add <file>..." to include in what will be committed) main.c nothing added to commit but untracked files present (use "git add" to track) Pojawiły się nowe informacje. Git rozpoznał że w twoim working copy (czyli de facto tym co jest aktualnie w katalogu) pojawił się nowy plik - w stanie untracked. Stan ten oznacza, że git nie będzie się tym plikiem interesował. Aby wskazać, że nowy plik jest częścią projektu wydajemy polecenie: > git add main.c I jeszcze raz git status (na początku zachęcam to wydawanie tej komendy po każdej akcji - w ten sposób szybciej nauczysz się co widzi git). > git status On branch master Your branch is up to date with 'origin/master'. Changes to be committed: (use "git restore --staged <file>..." to unstage) new file: main.c Status zmienił się na staged. Co to oznacza? Plk został dodany do tak zwanego staging area albo index - jest to miejsce gdzie przygotowywany jest następny commit. W gicie tworzenie commita jest dwuetapowe - napierw dodajemy zmiany do indeksu, usuwamy je z niego, modikujemy etc - dowolnie długo do czasu kiedy nasz commit jest gotowy. Wtedy za jednym razem zapisujemy wszystkie zmiany z indeksu jako nowy commit a indeks jest czyszczony i gotowy do przygotowania następnego. Chcemy aby nasz pierwszy commit zawierał tylko tą zmianę więc jesteśmy gotowi na: > git commit -m "Hello world" 1 file changed, 6 insertions(+) create mode 100644 main.c Utworzyliśmy pierwszy commit! Jak widać służy do tego polecenie git commit. Przełącznik -m ustawia komentarz. Jeżeli go pominiesz git uruchomi edytor tekstowy gdzie będziesz mógł go wpisać. Możemy to sprawdzić wydając polecenie git log. > git log Kolejne commity Idziemy za ciosem i dodajmy kolejny. Wyedytujmy plik README: # git-forbot-tutorial A small repository for git tutorial. Oraz main.c #include <stdio.h> int main(){ puts("Hello from git-managed world!"); return 0; } Dodatkowo skompilujmy nasz program gcc main.c Jak zwykle - najpierw git status Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: README.md modified: main.c Untracked files: (use "git add <file>..." to include in what will be committed) a.exe Widzimy, że git rozpoznał zmiany w śledzonych plikach jak również pojawienie się nowego. Dodajmy pliki źródłowe do indeksu git add README.md main.c (sprawdź wynik polecenia git status) Robimy commit git commit -m "Updated readme and hello world message" (sprawdź git status oraz git log) Poznaliśmy najprostszy sposób tworzenia commitów. Ignorowanie plików Pliku a.exe/a.out nie chcemy dodawać ponieważ jest plikiem wynikowym. W repozytorium powinny znajdować się tylko pliki źródłowe. Podobnie nie chcemy aby znalazły się tam pliki tymczasowe lub z logami. Niestety polecenie status będzie je pokazywać za każdym razem zaśmiecając nam obraz sytuacji. Oczywiście istnieje na to sposób - możemy stworzyć plik .gitignore w którym zawrzemy listę wzorców do ignorowania. Zawartość naszego pliku .gitignore *.exe *.out Istnieją generatory takich plików, doskonale nadają się na początkową wersję - np. https://www.gitignore.io/ Zobaczmy status > git status Untracked files: (use "git add <file>..." to include in what will be committed) .gitignore Zgodnie z tym czego oczekiwaliśmy binarka jest zignorowana. Za to plik .gitignore powinniśmy zcommitować. Zrób to sam Wycofywanie zmian Ok, potrafimy już dodawać zmiany. A co z ich usuwaniem? Rozpatrzymy kilka przypadków Sceniariusz 1 Przypadkiem zmodyfikowałem plik i chcę wrócić do wersji z ostatniego commita. Kot przebiegł mi po klawiaturze i mój README wygląda teraz tak: # git-forbot-tutorial A small repository for git tutorial. asdhawgdagwdkaw > git status Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: README.md Aby wrócić do wersji z ostatniego commita wykonaj > git restore README.md To wszystko UWAGA: git restore jest dosyć nowym poleceniem (przez co jeszcze mało popularnym). Inną metodą jest > git checkout -- README.md Secnariusz 2 Dodałem zmiany do indeksu ale jednak nie chcę ich commitować, co robić? Wprowadźmy zmiany do pliku main.c #include <stdio.h> int main(int argc, char** argv){ puts("Hello from git-managed world!"); puts("I can handle args"); return 0; } I dodajemy je do staging area > git add main.c (o git status już nie będę wspominał) Niestety zaszła pomyłka i nowy commit miał wyglądać tak: #include <stdio.h> int main(int argc, char **argv){ puts("Hello from git-managed world!"); puts("I can handle args"); return 0; } Nie ma problemu! Popraw plik i zobaczmy co pokaże status Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: main.c Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: main.c Widać, że plik main.c pojawił się zarówno w staging area jak i jako not staged - interpretacja jest prosta - część zmian została dodana do indeksu a część nie. Jeżeli teraz wykonamy commit to znajdą się tam tylko poprzednio dodane zmiany. My jednak teraz chcemy dodać poprawki, wykonaj git add main.c i sprawdź status a następnie wykonaj commit. Scenariusz 3 A co jeśli nie chcemy poprawiać zmian tylko po prostu je wycofać ze staging area? Wystarczy wykonać > git restore --staged zmiany zostaną wycofane z indeksu ale pozostaną w kopii roboczej. Na przykład: # Modyfikujuemy plik README > git add README.md > git status (przeanalizuj) > git restore --staged > git status (przeanalizuj i sprawdź zawartosc pliku README) IMG Teraz jest dobry moment na pauzę i przećwiczenie samodzielnie powyższych poleceń Usuwanie plików No dobrze, a co jeżeli chcę usunąć plik z repozytorium? Na początek dodajmy nowy plik (może być pusty) > git add info.txt > git commit info.txt -m "Added info" Wydaj polecenie rm info.txt (lub usuń plik z poziomu IDE, wychodzi na to samo) i sprawdź status Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) deleted: info.txt Git zarejestrował usunięcie pliku, teraz wystarczy git add info.txt i git commit Uwaga: istnieje również polecenie git rm które pozwala na usuwanie plików z repozytorium i pozostawienie ich w kopii roboczej. Jest to nieco bardziej zaawansowany temat i teraz go pominę. Tworzenie tagów Wspominałem, że git pozwala dopasowywać dodatkowe etykiety do commitów. Zróbmy to teraz - przyda się za chwilę. Wydaj polecenie git log, wybierz dowolny commit i skopiuj pierwsze 6 znaków jego SHA-1. Następnie wywołaj > git tag -a -m "Version 1.0" v1.0 WYBRANE_SHA Przełącznik -a tworzy tzw "annotated tag" - który zawiera autora, komentarz (dodany przełącznikiem -m). v1.0 to nazwa tagu, na końcu znajduje się hash commita do otagowania. Jeżeli pominiesz ten ostatni, git wybierze aktualny (ten gdzie wskazuje HEAD, ale o tym jeszcze za chwilę). Wykonaj git log aby zobaczyć swój tag [...] commit 6146854e0e8545bbbd5968f29a805d1c728e73a0 (tag: v1.0) [...] Istnieją również "lightweight tag" które zawierają tylko nazwę taga (bez przełączników -a i -m). Ja generalnie rekomenduję użycie tagów zanotowanych - ze względu na większą ilość informacji, lekkich używam tylko do tagów tymczasowych. Przeglądanie historii Na początku mówiłem, że git pozwoli Ci cofać się w czasie - zobaczmy jak to działa. Załóżmy, że chcesz wrócić do konkretnego stanu repozytorium. Zacznijmy od tego otagowanego. Na początek upewnij się że zarówno working copy jak i indeks są czyste - nie jest to wymagane ale będzie ułatwieniem. Aby przejść do historycznej wersji kodu wpisz > git checkout v1.0 Gdzie zamiast v1.0 może być dowolny istniejący tag lub ID commita. Git wypluwa dość długi komunikat - w skrócie repozytorium jest w stanie detached HEAD. Oznacza to, że nie jesteś w żadnej gałęzi. Na tym etapie rekomenduję uznać że taki stan oznacza "nie rób żadnych zmian w kodzie ktore mozesz chcieć zachować" - o tym jak to dokładnie obsłużyć będzie w drugiej części poświęconej gałęziom. To co nas w tej chwili interesuje to fakt "HEAD is now at 6146854 Added .gitignore" - w kopii roboczej znajduje się wersja kodu z tego konkretnego commita i HEAD (czyli wskaźnik "aktualnego" commita) jest tutaj ustawiony. Możesz np. sprawdzić jakie są różnice w pliku main.c względem najnowszego > git diff HEAD master main.c Czyli "pokaż mi różnicę między commitami HEAD oraz master w pliku main.c". Wiesz co oznacza HEAD, przypomnę że master jest etykietą najnowszego commita w gałęzi master. Git diff ma dużo więcej możliwości, zachęcam do zapoznania się z dokumentacją. Ok, zrobiliśmy inspekcję i chcemy wrócić do mastera. Co zrobić? Wystarczy wywołać > git checkout master Wrócimy do naszej gałęzi, HEAD zostanie również odpowiednio ustawiony. Przywracanie plików w wersji historycznej Załóżmy, że znalazłeś w którym miejscu historii wprowadziłeś błąd i chcesz przywrócić historyczną wersję pliku. Wystarczy wykonać > git checkout ID_COMMITA nazwa_pliku Czyli "przywróć plik nazwa_pliku z commita o id ID_COMMITA" gdzie id może być skrócony hash lub tag. Zmiana od razu wyląduje w staging area. (o wykonywaniu git status nie muszę przypominać ) Podsumowanie (niektórych) operacji Synchronizacja ze zdalnym repozytorium Kiedy wejdziesz na swoje repozytorium na githubie zobaczysz że nic się nie zmieniło - taka jest idea gita - system jest rozproszony i wszystkie zmiany dzieją się lokalnie. Aby przekazać swoje zmiany do zdalnego repozytorium wydaj polecenie: > git push Git poprosi Cię o zalogowanie się. Po pomyślnym wykonaniu polecenia odśwież stronę i zobaczysz swoje zmiany. Istnieje również możliwość pracy bez podawania hasła, przy użyciu klucza SSH. Dobre praktyki Git jak każde narzędzie zaawansowane narzędzie pokazuje pełnię mocy jeśli jest używany poprawnie. Wymaga to nieco doświadczenia ale jest kilka dobrych praktyk, które mozesz wdrożyć od razu: Wiadomości w commitach powinne być dokładne Jeden commit = jedna zmiana (w sensie logicznym, jak najbardziej może to być wiele plików) Staraj trzymać się working copy czyste, sytuacja kiedy przed tygodnie masz untracked files i nie dodałeś ich do gitignore "bo później się przydadzą" to proszenie się o kłopoty Commituj często - ja w pracy zwykle robię ok. 10 commitów dziennie Często używaj poleceń log oraz status Zanim zaczniesz używać gita w swojej pracy magisterskiej nabierz nieco doświadczenia na mniejszym projekcie Wypróbuj narzędzie gitk do graficznego przeglądania drzewa Nie rekomenduję używania narzędzi do obsługi gita wbudowanych w IDE - często robią one więcej niż sugerują nazwy poleceń Git miejscami jest mało intuicyjny, nie zrażaj się że czegoś od razu nie rozumiesz. Interfejs programu gitk Zakończenie części pierwszej Ufff, spro informacji jak na jeden raz. Jeżeli mój styl pisania się Wam spodoba to przygotuję drugą część gdzie opiszę dokładniej jak pracować ze zdalnym repozytorium oraz gałęziami. Niemniej posiadasz już podstawową wiedzę, która pozwoli Ci korzystać z dobrodziejstw Gita. Powodzenia
  7. 6 punktów
    W poprzedniej części, nauczyliśmy się kodować rozbudowane i responsywne strony. Mam nadzieję, że przeczytałeś co nieco z linków na dole, ponieważ teraz będziemy mocniej kodować, aby stworzyć jeszcze ładniejszą i jeszcze bardziej funkcjonalną stronę. Zrobimy nawet mały smartdom! Do dzieła! Spis treści serii artykułów: 1. Omówienie, i przygotowanie środowiska 2. Zapoznanie z nowym środowiskiem, praca jako Arduino, prosty serwer WWW 3. Przyspieszony kurs na webmastera 4. Wykresy, zapis do SPIFFS, mini smart-dom 5. Odbiór danych z przeglądarki, stałe IP, łączenie modułów ESP Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Wykresy Wcześniej mieliśmy specjalną podstronę, gdzie zapisywaliśmy temperaturę co jedną sekundę. Jeżeli chcielibyśmy dodać drugi termometr musielibyśmy dodać drugą podstronę, itd... Ale dzięki jQuery, możemy odczytać z jednej podstrony więcej informacji, niż tylko jedna! Zacznijmy od zmodyfikowania poprzedniego kodu. Na rozgrzewkę, zmieńmy nazwę /temp na /data. server.on("/data", handleData); //setup() void handleData() { ds.requestTemperatures(); cls(); lcd.print("Temperatura: "); lcd.println(ds.getTempCByIndex(0)); server.send (200, "text/html", (String)ds.getTempCByIndex(0)); } Następnie przejdziemy od razu do wyświetlenia wykresu. Do tego będziemy potrzebować biblioteki ChartJS. Dopisujemy zatem kilka linijek kodu: <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js" integrity="sha256-R4pqcOYV8lt7snxMQO/HSbVCFRPMdrhAFMH+vr9giYI=" crossorigin="anonymous"></script> <div class="col-sm"> <canvas id="wykres"></canvas> </div> Pierwsza linijka odpowiada za dołączenie biblioteki. W drugiej zaś, tworzymy nową małą kolumnę, w środku niej canvas. Canvas to znacznik wykorzystywany jako swego rodzaju płótno - będziemy tam rysować nasz wykres. Jak na razie w środku nie ma nic - ale zaraz coś namalujemy, dzięki nowej bibliotece. Pamiętajmy, że nową kolumnę umieszczamy obok pierwszej, w tym samym rzędzie! Jak się domyślasz, trzeba będzie dodać trochę kodu. Proponuję już nie używać znacznika <script>, tylko wszystkie nasze skrypty przenieść do nowego pliku. Dlatego, w folderze testowym, robimy nowy plik "skrypty.js". W środku przenosimy stary kawałek kodu: Cały kod JS w dokumencie HTML usuwamy. Znaczniki możemy zostawić, dopiszemy jedynie atrybut, gdzie jest nasz nowy plik. Przechodzimy do naszego nowo utworzonego pliku - zrobimy na razie wykres z stałych danych. Stwórzmy zatem tablice tych danych: var temperatura = [25, 20, 21, 22, 27]; var lata = ["1 rok", "2 rok", "3 rok", "4 rok", "5 rok"]; Przykładem tutaj będzie średnia temperatura pokoju na przestrzeni lat. Lata będą osią x (poziomą), temperatura y (pionową). Tablica labels przechowuje etykiety poszczególnych danych, var ctx = document.getElementById('wykres'); var wykres = new Chart(ctx, { type: 'line', data: { labels: lata, datasets: [ { label: 'Temperatura', data: temperatura, borderColor: 'rgba(255, 130, 150, 1', backgroundColor: 'rgba(255, 130, 150, 0.2)' } ] } }); Następnie przypisujemy nasze płótno do zmiennej. Kolejna zmienna to już stricte nasz wykres. Pierwszy atrybut mówi, że będzie to wykres liniowy (możemy zrobić kołowy, czy słupkowy). Potem zapisujemy dane wykresu: na początku definiujemy "labels" (etykiety) - to one będą naszą osią czasu. Datasets to paczki danych umieszczanych na tamtej osi - my mamy tylko jedną paczkę, temperatura. Przed każdą temperaturą wyświetlamy zatem tekst "Temperatura: ", informujemy jakie dane chcemy wyświetlić (po kolei!), następnie definiujemy jaki chcemy kolor kreski i kolor tła, jakie kreska wykreśli. Wrzucamy to wszystko do naszego dokumentu: $( document ).ready(function() { var temperatura = [25, 20, 21, 22, 27]; var lata = ["1 rok", "2 rok", "3 rok", "4 rok", "5 rok"]; var ctx = document.getElementById('wykres'); var wykres = new Chart(ctx, { type: 'line', data: { labels: lata, datasets: [ { label: 'Temperatura', data: temperatura, borderColor: 'rgba(255, 130, 150, 1', backgroundColor: 'rgba(255, 130, 150, 0.2)' } ] } }); setInterval(function() { var strona = new XMLHttpRequest(); strona.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { document.getElementById("data").innerHTML = this.responseText; } }; strona.open("GET", "/data", true); strona.send(); }, 1000); }); Dlaczego wszystko jest w "ready"? Dzięki temu, nasz kod wykona się tylko wtedy, kiedy wszystko się załaduje. Bo jak kod w jQuery miałby się wykonać bez jQuery? Jeżeli dokument już zapisałeś, spróbuj uruchomić stronę: A teraz spróbuj zmienić rozmiar okna przeglądarki, czy uruchomić wszystko na telefonie. Jak to wygląda? Właśnie dzięki responsywności, dostajemy możliwość "łatwego" zmieniania rozmiarów strony. Dlatego będzie ona dobrze czytelna i na komputerze, i na telefonie. Wrzućmy zatem wszystko co mamy w folderze testowym, do folderu /data, i wgrajmy na ESP. Dokonujemy szybkich zmian w kodzie: String getScript() { String strona; File file = SPIFFS.open("/skrypty.js", "r"); while(file.available()) strona+=(char)file.read(); file.close(); return strona; } //skrypty server.on("skrypty.js", handleSkrypty); void handleSkrypty() { server.send(200, "text/javascript", getScript()); } I wgrywamy całość na ESP. Jak widzimy, wszystko pięknie działa: Wykresy w czasie rzeczywistym Spróbujmy teraz wyświetlić "rzeczywiste" dane. Niestety, to już jest trochę trudne - lecz na pewno sobie poradzimy. Na początku, dopóki nie dostaniemy odczytów z termometru, będziemy mieli 0 stopni: var temperatura = [0, 0, 0, 0, 0]; To, ile damy tutaj zmiennych, będzie decydować o "szerokości" naszego wykresu. var ctx = document.getElementById('wykres'); var wykres = new Chart(ctx, { type: 'line', data: { labels: ['', '', '', '', ''], datasets: [ { data: temperatura, label: 'Temperatura', borderColor: 'rgba(255, 130, 150, 1', backgroundColor: 'rgba(255, 130, 150, 0.2)' } ] } }); Pierwszych 5 temperatur tak naprawdę nie ma, więc damy im puste etykiety. Reszta zostaje tak samo. Teraz dodamy dwie funkcje: dodającą wartość, i usuwającą ostatnią wartość. function addData(chart, label, data) { chart.data.labels.push(label); chart.data.datasets[0].data.push(data); chart.update(); } Pierwszy argument, to wykres - bo w końcu możemy ich mieć kilka na stronie. Drugi argument to etykieta, trzeci to wartość. Na początku "popychamy" stos etykiet, dodając kolejną. Podobnie robimy z danymi, a na końcu aktualizujemy wykres. function popData(chart, index) { chart.data.labels.splice(index, 1); chart.data.datasets[0].data.splice(index, 1); chart.update(); } Kolejna funkcja usuwa nam etykietę na podanym indeksie, a potem dane. var sekundy = 0; setInterval(function() { var strona = new XMLHttpRequest(); sekundy++; var etykieta = "" + sekundy + " sekunda"; strona.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var zawartosc = this.responseText; var dane = zawartosc.split("\n"); document.getElementById('data').innerHTML = dane[0]; addData(wykres, etykieta, dane[0]); popData(wykres, 0); } }; strona.open("GET", "/data", true); strona.send(); }, 1000); }); Tak wygląda cała nowa funkcja, która nam aktualizowała temperaturę. Z racji tego, że wykonuje się co sekundę, zrobimy od razu licznik tych sekund - stąd zmienna na początku. W funkcji zwiększamy zmienną o 1, a potem tworzymy etykietę. W środku funkcji pobierającej temperaturę dokonało się jednak najwięcej zmian. Zawartość całej strony /dane przechowujemy w zmiennej, którą potem rozdzielamy na linie co znak nowej linii. Dlaczego tak, a nie po prostu odczytać? Dzięki temu, na jednej podstronie możemy odczytywać bardzo wiele informacji, zamiast tworzyć nowe podstrony. Temperatura będzie zapisana na pierwszej linijce strony - wypisujemy ją na stronie głównej, potem dodajemy ją do wykresu (z etykietą), a starą temperaturę usuwamy. Jeżeli nie będziemy usuwać starych danych, po pewnym czasie całość zrobi się mocno nieczytelna. Przechodzimy do kodu; modyfikujemy funkcję handleData: void handleData() { ds.requestTemperatures(); cls(); lcd.print("Temperatura: "); lcd.println(ds.getTempCByIndex(0)); String strona = (String)ds.getTempCByIndex(0); strona += "\n"; //dodamy zaraz więcej danych! server.send (200, "text/html", strona); } Praktycznie nic się nie zmieniło, jedyne co to dodaliśmy zmienną strona przechowującą podstronę /data. Jest ona pisana w locie, co umożliwia wypisanie tam danych z czujników. Po każdym dodaniu linii, dodajemy znak nowej linii dla rozdzielenia danych. Wszystko wgrywamy do ESP, i sprawdzamy czy działa: Zapisywanie plików To teraz spróbujmy zapisać temperaturę do pliku, żeby potem "na żądanie" robić wykres. Np. co godzinę zapisywać temperaturę, robić średnią, i pokazywać na wykresie. Akurat to jest dość proste, wystarczy nowa funkcja: void writeToFile(String plik, String tekst) { File file = SPIFFS.open(plik, "w"); if (file.print(tekst)) { cls(); lcd.println("Zapisano pomyslnie!"); } else { cls(); lcd.println("Blad zapisu!"); } file.close(); } Pierwszym argumentem jest plik. Musimy sami utworzyć taki pusty plik, aby do niego zapisać różne dane. Drugi argument to oczywiście zawartość, którą chcemy zapisać. Jako zadanie domowe (no dobra, zadanie dodatkowe), spróbuj zrobić program który zapisze temperaturę, a następnie odczyta i zapisze na wykresie. Praca na wielu danych No, to jak mamy już wszystko do pracy na wielu danych, dodanie czujników powinno być czystą przyjemnością. Dlatego dodamy: HC-SR04 przycisk (kontakron, co chcesz) barometr BMP180 fotorezystor a nawet WS2812. A co. Scenariusz jest prosty: mierzę temperaturę, odległość, stan przycisku, ilość światła, wysyłam to do WWW, a z WWW dostaję informacje o kolorze WS2812. W tym momencie instalujemy wszystkie potrzebne biblioteki, dodajemy je na początku kodu, i dodajemy je do naszej funkcji obsługującej /data: void handleData() { ds.requestTemperatures(); String strona = (String)ds.getTempCByIndex(0); strona += "\n"; strona += analogRead(PIN_FRES); strona += "\n"; strona += bmp.readTemperature(); strona += "\n"; strona += bmp.readPressure(); strona += "\n"; strona += zmierzOdleglosc(); strona += "\n"; strona += digitalRead(PIN_BTN); //temperatura ds //fotorezystor //temperatura bmp //cisnienie //odleglosc //przycisk server.send (200, "text/html", strona); } Jedyne co się zmieniło, to dodanie kilku odczytów oddzielonymi znakami nowej linii. Po wgraniu tego do kodu, powinieneś zobaczyć takie coś na stronie /data: Pierwsza wartość to zatem temperatura, druga ilość światła, trzecia to też temperatura (uznajmy że z innego pokoju), czwarta to ciśnienie w paskalach, piąta to odległość zmierzona przez HC-SR04, ostatnia to odczyt z kontaktronu. Wygląda na to, że muszę popracować z HC... Ale dlaczego wszystko jest w jednej linii, tylko porozdzielane spacjami? Otóż to są nowe linie, co możemy zobaczyć w narzędziach deweloperskch: Aby przejść do nowej linii, musielibyśmy dać znacznik <br>. Jak widać, przeglądarka także "sama" dodała <html> i <body>. No, jeżeli już masz wszystkie czujniki gotowe, przejdziemy do strony. Odpalamy plik skrypty.js. Ale chwila! Zanim zaczniemy kodować, musimy sobie odpowiedzieć na pytanie "co kodować". Zawsze przed kodowaniem zadaj sobie to pytanie! Mamy 6 odczytów. Dwa z nich wyświetlimy na wspólnym wykresie, światło możemy wyrazić kolorem barwy, ciśnienie pokażemy w hPa obok "surowych" danych. Będziemy wyświetlali napis, czy drzwi zostały otwarte (kontaktron). No, to teraz możemy kodować. Zacznijmy od zapisu tych wszystkich danych: if (this.readyState == 4 && this.status == 200) { var zawartosc = this.responseText; var dane = zawartosc.split("\n"); var temp1 = dane[0]; var fres = dane[1]; var temp2 = dane[2]; var cis = dane[3] / 100; var odl = dane[4]; var btn = dane[5]; document.getElementById('data').innerHTML = dane[0]; addData(wykres, etykieta, dane[0]); popData(wykres, 0); } To jest if z funkcji "setInterval". Tak jak wcześniej tylko dodaliśmy zapis danych, tak i teraz tylko dodaliśmy odczyty. Aby wyświetlić temperatury na jednym wykresie, musimy najpierw zmodyfikować funkcje pop i push: function addData(chart, data, dset) { chart.data.datasets[dset].data.push(data); chart.update(); } function addLabel(chart, label) { chart.data.labels.push(label); chart.update(); } function popData(chart, index) { chart.data.labels.splice(index, 1); chart.data.datasets.forEach((dataset) => { dataset.data.splice(index, 1); }); chart.update(); } Komenda "znikająca" została teraz obramowana pętlą forEach. Wykonuje się ona tyle razy, ile razy występuje coś (dosłownie - dla każdego). Dla każdego zatem zestawu danych, usuwamy zmienną. Aby dodać daną, od teraz musimy powiedzieć do którego zestawu danych ją dodać. Dodawanie etykiety dałem do osobnej funkcji, aby nie robić duplikatów etykiet. A tak wygląda nasz nowy wykres: var temperatura = [0, 0, 0, 0, 0]; var temperatura2 = [0, 0, 0, 0, 0]; var ctx = document.getElementById('wykres'); var wykres = new Chart(ctx, { type: 'line', data: { labels: ['', '', '', '', ''], datasets: [ { data: temperatura, label: 'Temperatura w pokoju', borderColor: 'rgba(255, 130, 150, 1)', backgroundColor: 'rgba(255, 130, 150, 0.2)' },{ data: temperatura2, label:'Temperatura w drugim pokoju', borderColor: 'rgba(23, 126, 214, 1)', backgroundColor: 'rgba(23, 126, 214, 0.2)', } ] } }); Dodaliśmy drugi zestaw danych. Ma on niebieską kreskę, i pusty zestaw początkowych danych. Etykiety teraz będą różne - dla dwóch różnych pokoi. Zostało nam dodać dodawanie i odejmowanie danych z wykresu: document.getElementById('temp1').innerHTML = temp1; document.getElementById('temp2').innerHTML = temp2; addLabel(wykres, etykieta); addData(wykres, temp1, 0); addData(wykres, temp2, 1); popData(wykres, 0); I szybkie zmiany w HTMLu: <p>Temperatura w pokoju: <span id="temp1">N/A</span></p> <p>Temperatura w drugim pokoju: <span id="temp2">N/A</span></p> Spróbujmy teraz to wgrać i zobaczyć efekt. Piękny efekt. No, to teraz może pobawimy się FontAwesome? Wchodzimy tutaj, i szukamy ikonki termometru: Niestety, dla nas dostępne są tylko te widoczne ikony - te szare są dla wersji płatnych. Wybierzmy ikonkę która nam najbardziej przypadła do gustu: Klikamy "Start using this icon", i kod kopiujemy tam, gdzie ma być ikonka. Ja sobie dam ją przed temperatury: <p>Temperatura w pokoju: <i class="fas fa-thermometer-three-quarters"></i> <span id="temp1">N/A</span></p> <p>Temperatura w drugim pokoju: <i class="fas fa-thermometer-three-quarters"></i> <span id="temp2">N/A</span></p> I po wgraniu dostajemy takie ładne ikonki: To teraz możemy dodać pozostałe dane. Spróbujemy zmienić kolor za pomocą jQuery - lecz musimy jakoś przekonwertować wartość z fotorezystora do koloru. Kolor możemy zapisać heksadecymalnie (#ffffff to biały), w formacie rgba (255, 255, 255, 255 to biały nieprzezroczysty), czy podać stałą (red - czerwony). Tutaj proponuję wyświetlić kolor w skali szarości. Dlatego odczyt od razu podzielimy przez 4, by mieć wartości 0-255: var fres = dane[1] / 4; 'Następnie stworzymy zmienną, która będzie przechowywać tekst atrybutu color. Dlaczego odejmowanie jest w nawiasach? Otóż bez nich, odjęlibyśmy tekst "20". Dlatego zapis 2+2 da nam 22, a zapis (2+2) da nam 4. Ale... dlaczego w ogóle odejmujemy? Jeżeli światło będzie białe, nie będziemy go widzieli na stronie z białym tłem. var color = 'rgb('+fres +','+(fres - 20)+','+(fres - 20)+')'; Oraz zmieniamy co sekundę kolor: document.getElementById('light').style.color = color; No dobra, ale czego zmieniamy? Ja sobie dodałem tekst "Światło" oraz ikonkę kwadratu z przypisanym id "light". To on będzie zmieniał kolor. <p>Światło: <i class="fas fa-square" id="light"></i> Teraz dodamy ciśnienie, oraz jednostki dla temperatur. W googlu szukamy "degree symbol" lub kopiujemy stąd: ° Cała kolumna będzie wyglądać tak: <div class="col-sm"> <h1>Centralna Baza Dowodzenia</h1> <p>Dumnie wspierana przez ESP8266</p> <a type="button" class="btn btn-success" href="/on">ON</a> <a type="button" class="btn btn-danger" href="/off">OFF</a> <p>Temperatura w pokoju: <i class="fas fa-thermometer-three-quarters"></i> <span id="temp1">N/A</span>°C</p> <p>Temperatura w drugim pokoju: <i class="fas fa-thermometer-three-quarters"></i> <span id="temp2">N/A</span>°C</p> <p>Światło: <i class="fas fa-square" id="light"></i></p> <p>Ciśnienie: <span id="cis"></span>hPa</p> <p>Odległość: <span id="odl"></span>cm</p> <p>Drzwi są <span id="drzwi"></span></p> </div> Pomiędzy znacznik </span> a </p> dodaliśmy tekst, z jednostką. Podobnie z ciśnieniem. Przechodzimy zatem do kodowania: document.getElementById('cis').innerHTML = cis; document.getElementById('odl').innerHTML = odl; if(!btn) document.getElementById('drzwi').innerHTML = 'otwarte!'; else document.getElementById('drzwi').innerHTML = 'zamknięte.'; Robimy prostego ifa, który nam wyświetli tekst "zamknięte", jeżeli odczyta HIGH. W przeciwnym razie wyświetli tekst "otwarte". Sprawdźmy, czy kod działa: Można powiedzieć, że zrobiliśmy już mini smartdom. Mamy sterowanie z poziomu aplikacji oraz wiele odczytów. W następnym odcinku spróbujemy dodać małego ESP, aby zbierał odczyty z innego pokoju, użyjemy WS2812, oraz zrobimy małe podsumowanie. Bądźcie kreatywni! Spróbujcie urozmaicić swój smartdom, stronę, i pokażcie w komentarzach wasze konstrukcje! Spis treści serii artykułów: 1. Omówienie, i przygotowanie środowiska 2. Zapoznanie z nowym środowiskiem, praca jako Arduino, prosty serwer WWW 3. Przyspieszony kurs na webmastera 4. Wykresy, zapis do SPIFFS, mini smart-dom 5. Odbiór danych z przeglądarki, stałe IP, łączenie modułów ESP
  8. 6 punktów
    Z poprzedniej części mogliśmy się dowiedzieć, co to takiego ten cały HTTP -jak się okazało, nic strasznie skomplikowanego. Dzisiaj zajmiemy się praktyczną stroną - czyli napisaniem najprostszego serwera. Oczywiście - jako że temat traktuje o IoT - serwer będzie udostępniał dane odczytane z jakichś czujników. Już słyszę: a dlaczego serwer a nie klient, przecież klient powinien być łatwiejszy? Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. A dlatego, że aby sprawdzić działanie klienta, należy mieć serwer a takiego na razie nie mamy. Natomiast klienta HTTP ma tu raczej każdy - począwszy od przeglądarek, poprzez przeróżne wgety i curle kończąc na PuTTY i netcacie. Przyjrzyjmy się więc dokładniej, jak działa. Spis treści serii artykułów: Protokół HTTP w zastosowaniach IoT - część 1: trochę teorii Protokół HTTP w zastosowaniach IoT - część 2: budujemy serwer Protokół HTTP w zastosowaniach IoT - część 3: tworzymy klienta Serwer HTTP W najprostszym przypadku serwer po podłączeniu do sieci po prostu czeka na zgłoszenie klienta, wykonuje jakąś tam funkcję obsługi, rozłącza się z klientem i powraca do czekania. Spróbujmy napisać taki program. Ponieważ dla różnych płytek i modułów łączenia z siecią program może być różny, spróbujmy stworzyć przynajmniej dwa (czyli Arduino z Ethernet Shield i dla ESP8266/ESP32). Zacznijmy od jakichś założeń. A więc: Do płytki podłączony jest potencjometr 10 kΩ i przycisk; Serwer w odpowiedzi na zapytanie podaje wartości odczytu potencjometru i przycisku; Serwer nie sprawdza poprawności zapytania, zakłada że jeśli ktoś coś od niego chce to tylko tego co wypisuje; Serwer wypisuje wartości czystym tekstem, bez zabawy w jakieś HTML-e i podobne wymysły. Najpierw więc przygotujmy wszystko, co nam będzie potrzebne. Przede wszystkim musimy sobie przygotować płytkę i podłączyć do niej potencjometr i przycisk. W przypadku Arduino będzie to wyglądać tak: Podobnie dla ESP8266 i ESP32: Zanim zabierzemy się do pisania programu - kilka słów wstępu. Otóż różne biblioteki (WiFi, Ethernet) operujące na połączeniach sieciowych TCP/IP zawsze mają swoje klasy serwera i klienta, będące pochodnymi ogólnych klas Server i Client. Funkcje obsługi połączeń (spoza tych bibliotek) zawsze wymagają właśnie obiektów tych klas, a nie konkretnych (np. EthernetServer). A co robią takie obiekty? To są po prostu elementy najprostszego połączenia TCP/IP. Obiekt klasy Server nasłuchuje na określonym porcie i w przypadku przychodzącego połączenia tworzy nowy obiekt typu Client, już połączony ze stroną nawiązującą połączenie: EthernetClient client=server.available(); if (client) { // obsługa połączenia } Obiekt klasy Client można również utworzyć ręcznie - i wtedy trzeba mu podać adres i port serwera, z którym ma się połączyć: if (client.connect(ADRES_IP_SERWERA, PORT_SERWERA)) { // rozmowa z serwerem } else { Serial.println("Nie mogę nawiązać połączenia"); } Ponieważ Client dziedziczy po klasie Stream (a Stream po Print) - możemy tu używać wszystkich metod pochodzących z tej klasy (podobnie jak w obiektach klasy Serial) Dobra, wystarczy tego wstępu. Zabierzmy się wreszcie za napisanie prostego serwera. Zacznijmy od tego, co będzie się powtarzać we wszystkich następnych programach, czyli od tych wszystkich beginów, initów i innych powtarzalnych rzeczy. Spróbujmy od razu zrobić to tak, aby jak najmniej ingerować w późniejsze programy czy to przy zmianie płytki (Arduino - ESP), czy przy dalszych eksperymentach z programami (serwer i klient). Taki typowy kod (dla Arduino) wygląda tak: #include <SPI.h> #include <Ethernet.h> #define POT_PIN A1 #define KEY_PIN A0 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; EthernetServer server(80); void setup() { Serial.begin(9600); while (!Serial); // dla Leonardo Ethernet.begin(mac); while (Ethernet.linkStatus() == LinkOFF) { Serial.println(F("Ethernet cable is not connected.")); delay(500); } // dajmy mu trochę czasu na połączenie delay(1000); Serial.print(F("Adres serwera: ")); Serial.println(Ethernet.localIP()); pinMode(KEY_PIN, INPUT_PULLUP); server.begin(); } Oczywiście jest to najprostszy z możliwych sposobów połączenia z siecią, więcej informacji można znaleźć w dokumentacji i przykładach biblioteki Ethernet. Porównajmy to z kodem dla ESP8266 i ESP32: #ifdef ESP32 #include <WiFi.h> #define POT_PIN 32 #define KEY_PIN 16 #else #include <ESP8266WiFi.h> #define POT_PIN A0 #define KEY_PIN 4 #endif const char* ssid = "My_SSID"; const char* password = "My_PASSWORD"; WiFiServer server(80); void setup(void) { Serial.begin(115200); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(); Serial.print("Połączono z WiFi, adres serwera: "); Serial.println(WiFi.localIP()); pinMode(KEY_PIN, INPUT_PULLUP); server.begin(); } I znów procedura łąćzenia jest maksymalnie uproszczona - ale nie czas to i nie miejsce na omawianie niuansów różnych rodzajów połączeń:) Jak widać kody dla ESP8266 i ESP32 są na tyle podobne, że mogliśmy użyć wspólnego kodu zmieniając tylko bibliotekę i definicje pinów. Spróbujmy jednak pójść dalej i stworzyć wspólny kod dla Arduino/Ethernet i ESP. #ifdef AVR // część dla Arduino i Ethernet Shield #include <SPI.h> #include <Ethernet.h> #define POT_PIN A1 #define KEY_PIN A0 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; #define MyServer EthernetServer #define MyClient EthernetClient #define SERIAL_SPEED 9600 void init_network(void) { Ethernet.begin(mac); while (Ethernet.linkStatus() == LinkOFF) { Serial.println(F("Ethernet cable is not connected.")); delay(500); } // dajmy mu trochę czasu na połączenie delay(1000); Serial.print(F("Adres serwera: ")); Serial.println(Ethernet.localIP()); } #else // część dla ESP #ifdef ESP32 #include <WiFi.h> #define POT_PIN 32 #define KEY_PIN 16 #else #include <ESP8266WiFi.h> #define POT_PIN A0 #define KEY_PIN 4 #endif const char* ssid = "My_SSID"; const char* password = "My_PASSWORD"; #define MyServer WiFiServer #define MyClient WiFiClient #define SERIAL_SPEED 115200 void init_network(void) { WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(); Serial.print("Połączono z WiFi, adres serwera: "); Serial.println(WiFi.localIP()); } #endif MyServer server(80); void setup(void) { Serial.begin(SERIAL_SPEED); #ifdef __AVR_ATmega32U4__ while (!Serial); // potrzebne tylko dla Leonardo/Micro #endif init_network(); pinMode(KEY_PIN, INPUT_PULLUP); server.begin(); } Możemy teraz zobaczyć, że: wszystkie zależne od płytki wywołania funkcji inicjalizujących połączenie zostały przeniesione do funkcji init_network(); nazwy klas zależnych od płytki i typu połączenia z siecią zostały "zamaskowane" definicjami MyClient i MyServer W ten sposób pisząc dalej program nie będziemy musieli zawracać sobie głowy różnicami między płytkami, bibliotekami i sposobami połączeń z siecią. I teraz mając wszystko przygotowane, możemy na spokojnie zająć się... pierwszą wersją serwera Kod jest tu banalnie prosty i chyba nie wymaga komentarza. Po połączeniu klienta pobieramy od niego wszystko jak leci nigdzie tego nie zapamiętując, aż do napotkania pustej linii. W tym momencie zakładamy, że klient nic więcej wysyłać nie będzie. W odpowiedzi wysyłamy dwa nagłówki: HTTP/1.1 200 OK - potwierdzenie, że wszystko się udało Content-Type: text/plain - czyli, że odpowiadamy czystym tekstem Po nagłówkach wysyłamy linię zawierającą obie odczytane z wejść wartości i zamykamy połączenie. void loop() { // Sprawdzamy, czy klient się połączył MyClient client = server.available(); if (!client) { // nie połączył się return; } Serial.println(F("Nowe połączenie")); // Czytamy znak po znaku aż do napotkania pustej linii: bool linia_pusta = true; bool naglowki_wczytane = false; while (client.connected()) { if (!client.available()) continue; char c = client.read(); Serial.print(c); if (c == '\n') { // koniec linii if (linia_pusta) { naglowki_wczytane = true; // wczytaliśmy wszystko break; } linia_pusta = true; } else if (c != '\r') { // znak '\r' po prostu pomijamy linia_pusta = false; } } // czy na pewno wszystko wczytaliśmy? if (!naglowki_wczytane) { // klient zniknał Serial.println(); Serial.println(F("Klient rozwiał się we mgle")); } else { // Wypisujemy ważne nagłówki i pustą linię client.print(F("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n")); int pot = analogRead(POT_PIN); int key = digitalRead(KEY_PIN); client.print(pot); client.print(' '); client.println(key); } client.stop(); } Możemy teraz w praktyce przetestować nasz serwer. Możemy do tego użyć przeglądarki, wpisując następujący URL: http://adres_ip_serwera/ My jednak chcielibyśmy obejrzeć całą transmisję, więc znów użyjemy PuTTY lub nc: Jak się przekonaliśmy, napisanie prostego serwera nie jest wcale takie trudne, a nawet taki banalny program może byc użyteczny (np. w czujniku temperatury, odpytywanym periodycznie przez główny komputer w domu). Tym niemniej program może się niespecjalnie podobać. Przede wszystkim - czego byśmy nie wpisali, dostaniemy w odpowiedzi wartości z czujników. Podglądając komunikację widzimy, że przeglądarka wysłała dodatkowy request prosząc o ikonkę serwisu - a takiej przecież nie mamy. Spróbujmy więc ulepszyć nieco nasz serwer, tworząc... ulepszoną wersję serwera Poczyńmy znów pewne założenia: Oprócz potencjometru i przycisku serwer podaje wartość jakiejś zmiennej (nazwijmy ją po prostu nasza_zmienna); Serwer oprócz metody GET obsługuje również HEAD; W przypadku otrzymania nieprawidłowej linii serwer odpowiada komunikatem błędu; Z poziomu przeglądarki możemy ustawić wartość "nasza_zmienna" wpisując URL typu http://adres_ip_serwera/set/wartość; Jeśli serwer otrzyma żądanie /favicon.ico, odpowiada błędem 404 (Not Found). #ifdef AVR #define BUFFER_LENGTH 128 #else #define BUFFER_LENGTH 1024 #endif int nasza_zmienna=0; void error400(MyClient& client) { client.println(F("HTTP/1.1 400 Bad Request\r\n\ Content-type: text/plain\r\n\r\n\ Bad request")); client.stop(); } void error404(MyClient& client) { client.println(F("HTTP/1.1 404 Not Found\r\n\ Content-type: text/plain\r\n\r\n\ Not found")); client.stop(); } void error405(MyClient& client) { client.println(F("HTTP/1.1 405 Method Not Allowed\r\n\ Allow: GET,HEAD\r\n\ Content-type: text/plain\r\n\r\n\ Method not allowed")); client.stop(); } void loop(void) { char bufor[BUFFER_LENGTH]; bool isHead; // Sprawdzamy, czy klient się połączył MyClient client = server.available(); if (!client) { // nie połączył się return; } Serial.println(F("Nowe połączenie")); // dajmy sobie czas na wpisanie czegoś do PuTTY client.setTimeout(20000); // Wczytujemy pierwszą linię int n = client.readBytesUntil('\n',bufor, BUFFER_LENGTH-1); if (!n) { // nic nie wczytano? Serial.println(F("Klient się rozmyślił")); client.stop(); return; } bufor[n] = 0; // dopisujemy '\0' na końcu stringu Serial.println(bufor); // teraz pomijamy resztę nagłówków bool linia_pusta = true; bool naglowki_wczytane = false; while (client.connected()) { if (!client.available()) continue; char c = client.read(); Serial.print(c); if (c == '\n') { // koniec linii if (linia_pusta) { naglowki_wczytane = true; // wczytaliśmy wszystko break; } linia_pusta = true; } else if (c != '\r') { // znak '\r' po prostu pomijamy linia_pusta = false; } } // czy na pewno wszystko wczytaliśmy? if (!naglowki_wczytane) { // klient zniknał Serial.println(); Serial.println(F("Klient rozwiał się we mgle")); client.stop(); return; } char *path=strchr(bufor,' '); if (!path) { error400(client); return; } *path++=0; // wstawiamy zero w miejsce spacji // oddzielającej metodę od ścieżki if (!strcmp(bufor, "GET")) { isHead = false; } else if (!strcmp(bufor, "HEAD")) { isHead = true; } else { error405(client); return; } char *proto = strchr(path, ' '); if (!proto) { error400(client); return; } *proto++=0; // nie przesadzajmy, uwierzmy na słowo że to HTTP :) // nie będziemy sprawdzać co siedzi w proto if (!strcmp(path,"/favicon.ico")) { error404(client); return; } if (!strncmp(path, "/set/", 5)) { nasza_zmienna = atoi(path+5); Serial.print(F("Ustawiamy nową wartość zmiennej: ")); Serial.println(nasza_zmienna); } // wypisujemy nagłówki client.print(F("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n")); // teraz możemy odpowiedzieć trzema wartościami: // potencjometr, przycisk i nasza zmienna if (!isHead) { int pot = analogRead(POT_PIN); int key = digitalRead(KEY_PIN); client.print(pot); client.print(' '); client.print(key); client.print(' '); client.println(nasza_zmienna); } client.stop(); } Kod powinien być zrozumiały bez szczegółowego omawiania każdej linii, być może z dwiema uwagami: dotychczas nie mówiliśmy o metodzie HEAD. Działa ona podobnie jak GET, ale serwer kończy transmisję po wysłaniu nagłówków. zamiast błędu 405 (Method Not Allowed) należałoby użyć 501 (Not Implemented). Sugerowałoby to jednak błąd oprogramowania serwera tymczasem to klient przeznaczony do współpracy z urządzeniami IoT powinien wiedzieć jakie pytanie można zadać serwerowi. Dodatkowo obsługa błędu 405 wymaga wysłania dodatkowego nagłówka informującego klienta, jakich metod może używać. A oto wyniki działania naszego serwera: Widzimy więc, że zgodnie z naszymi założeniami: Metoda HEAD powoduje wysłanie tylko nagłówków; Nieznana metoda powoduje wystąpienie błędu 405 Prośba o /favicon.ico powoduje wystąpienie błędu 404 URI o specialnej postaci: /set/<liczba> powoduje zapisanie liczby w pamięci serwera. Możemy również zauważyć, ze odpowiedź informująca o błędzie ma taką samą postać jak prawidłowa, czyli składa się z głównego nagłówka odpowiedzi (tzw. "status line"), nagłówka typu, ewentualnie dodatkowych nagłówków i odpowiedzi w postaci zrozumiałej dla człowieka. Dodatkowym efektem wysłania 404 w odpowiedzi na favicon.ico jest zapamiętanie tej informacji przez przeglądarkę i nie proszenie o nią za każdym razem. Taki serwer może już służyć bardziej praktycznym celom - np. sterowaniem różnych domowych urządzeń za pomocą przeglądarki. I tyle na dziś. Kody obu programów znajdziemy w załączniku: servers.zip - a w następnej części spróbujemy stworzyć klienta, czyli skomunikować się z naszym serwerem. Spis treści serii artykułów: Protokół HTTP w zastosowaniach IoT - część 1: trochę teorii Protokół HTTP w zastosowaniach IoT - część 2: budujemy serwer Protokół HTTP w zastosowaniach IoT - część 3: tworzymy klienta
  9. 5 punktów
    Regulator PID (proporcjonalno-całkująco-różniczkujący) jest jednym z podstawowych typów regulatorów. Znalazł swoje miejsce w niezliczonej liczbie układów regulacji. Implementacja i testowanie algorytmu sterowania na rzeczywistym obiekcie pozwoli na łatwiejsze zapoznamie się z zasadami nim rządzącymi. W artykule zostanie zwrócona uwaga na wpływ poszczególnych członów regulatora na zachowanie obiektu. Co więcej zwrócimy uwagę na problem nasycania się całki (ang. integral windup) i zrealizujemy przykładowy układ do jego przeciwdziałania. Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Zarys struktury Obiektem sterowania jest śmigło (opis projektu wkrótce) z 8-bitowym mikrokontrolerem AVR w którym zaimplementujemy nasz regulator. Urządzeniem wykonawczym jest silnik prądu stałego. Jego sterowanie odbywa się za pomocą 10-bitowej modulacji szerokości impulsu. Sprzężenie zwrotne pozyskiwane jest z czujnika odległości zamontowanego na płytce. Jednostką odczytywanych wartości jest milimetr. Celem regulacji jest utrzymanie śmigła na zadanej wysokości - w naszym przypadku 1m. Jak wygląda schemat blokowy zaimplementowanego układu regulacji? Wykorzystane rozwiązanie przedstawione jest na poniższym obrazku. Kod algorytmu Nazwy zmiennych w kodzie programu są nazwane analogiczne do nazw sygnałów na schemacie blokowym. Kod pętli głównej programu został przedstawiony poniżej. while(1) { // czas próbkowania Ts=50ms // kod odpowiedzialny za pobranie i zapisanie w zmiennej height odległości od powierzchni w mm while(dataReady == 0) VL53L1X_CheckForDataReady(dev, &dataReady); dataReady = 0; VL53L1X_GetDistance(dev, &height); VL53L1X_ClearInterrupt(dev); error = height_setpoint - height; // obliczanie wartości uchybu sterowania // sterowanie niebieską diodą led sygnalizującą zawieranie się wartości uchybu w przedziale +-5cm if(abs(error) < 50) PORTD |= (1<<PD3); else PORTD &= ~(1<<PD3); // obliczanie całki metodą prostokątów z uwzględznieniem sprzężenia od mechanizmu anti-windup integral = integral + error*Ts + Kaw*integral_correction; // obliczanie pochodnej derivative = (error-last_error)/Ts; // równanie obliczające wartość wielkości sterującej przed ograniczeniem cv_before_sat = Kp*error + Ki*integral + Kd*derivative; if(cv_before_sat > 1023) control_value = 1023; // ograniczenie wartości sterującej else if(cv_before_sat < 0) control_value = 0; else control_value = cv_before_sat; // warunek zatrzymujący śmigło na czas 3s w przypadku wysokości mniejszej niż 10cm if(height < 100) { control_value = 0; OCR1A = (uint16_t)control_value; for(int i = 0; i < 6; i++) { // mruganie diodą PORTD ^= (1<<PD3); _delay_ms(500); } integral = 0; // zerowanie całki } else OCR1A = (uint16_t)control_value; // przypisanie wartości cv jako wypełnienie PWM integral_correction = control_value - cv_before_sat // różnica wartości sterujących last_error = error; // zapamiętanie wartości uchybu jako ostatni uchyb } Badanie działania poszczególnych członów Wstępny dobór nastaw regulatora zrealizowano metodą testów symulacyjnych: Kp=1.6, Ki=0.75, Kd=0.3, Kaw=0.1. W celu przebadania sposobu działania regulatora sprawdzimy zachowanie obiektu w kilku przypadkach, włączając kolejne człony regulatora. Śmigło po włączeniu zapamiętuje 400 ostatnich próbek aktualnej wysokości, lub wielkości sterującej. Z mikrokontrolera wyciągniemy je dzięki komunikacji UART, a następnie naszkicujemy odpowiednie wykresy. Przejdźmy do analizy działania członu proporcjonalnego. Jaka jest jego idea? W skrócie generuje tym silniejszy sygnał im większa jest różnica między wartością zadaną a wartością sterującą. Niżej przedstawione zostało zachowanie naszego obiektu z regulatorem tylko wykorzystującym człon proporcjonalny. Odpowiedź obiektu sterowanego jest daleka od ideału. Korzystanie tylko z członu P w obiektach często prowadzi do utraty stabilności, tak jak w naszym przypadku. Zajmijmy się członem różniczkującym. Człon różniczkujący jak nazwa wskazuje liczy pochodną. Im większy przyrost uchybu tym większa wartość sygnału sterującego. Jak sprawił się w naszym przypadku razem z członem P? Zastosowanie członu D pozwoliło na stabilizację obiektu, jednak uchyb w stanie stanie ustalonym nie jest do przyjęcia. Rozwiązaniem problemu jest dodanie członu I. Działanie członu całkującego polega na obliczaniu całki, czyli w najprostszym wypadku w systemach dyskretnych na ciągłym sumowaniu kolejnych uchybów z uwzględnieniem czasu próbkowania. Regulator PI w naszym obiekcie zareagował dosyć drastycznie. Człon całkujący korzystnie wpływa na powstawanie oscylacji, oraz na destabilizowanie układu regulacji. Sprawdźmy zachowanie obiektu regulacji z wykorzystaniem pełnego regulatora PID. Szczęśliwie dla nas, po zastosowaniu różniczki obiekt został doprowadzony do stabilności, a dzięki całkowaniu uchyb w stanie ustalonym został wyeliminowany. Jednak przy członie całkującym należy zachować ostrożność i powrócić do wspomnianego wcześniej nasycania się całki. Brak mechanizmu anti-windup, może doprowadzić do zdominowania przez człon całkujący sygnału sterującego! W naszym przypadku taką sytuację możemy wygenerować przytrzymując przez chwilę śmigło w miejscu lekko oddalonym od wartości ustalonej. Lepszą analizę tego problemu można przeprowadzić obserwując wartość sterującą w czasie. Zaimplementowanie mechanizmu anti-windup do naszego regulatora spowodowało odpowiedzi jak niżej. Użyty przez nas mechanizm skutecznie osłabił człon I w chwilach kiedy całka została nasycona, a dodatkowo przyrost wartości sterującej został nieznacznie ograniczony. Moje zmagania z regulatorem PID prezentuje poniższy filmik. Podsumowanie Mam nadzieję, że temat regulatora PID został nieco bardziej przybliżony. Zrozumienie jego działania jest podstawą do poznania bardziej zaawansowanych algorytmów, które kiedyś będziesz mógł wykorzystać w swoich projektach! Cały kod programu dostępny jest na moim GitHubie. Pozdrawiam, Karol
  10. 5 punktów
    Reguła KISS, DRY oraz inne zasady dobrego programowania Reguła KISS (ang. Keep It Simple, Stupid), dosłownie zrób to prosto, idioto – Prostota (i unikanie złożoności) powinna być priorytetem podczas programowania. Kod powinien być łatwy do odczytania i zrozumienia wymagając do tego jak najmniej wysiłku. Większość systemów działa najlepiej, gdy polegają na prostocie, a nie na złożoności. Należy się więc starać, aby nasz kod podczas analizy nie zmuszał do zbytniego myślenia. Gdy po jakimś czasie do niego wrócimy i nie wiemy co tam się dzieje, to znak, że musimy nad tym popracować. Dobrą metodą jest również pisanie naszych programów raz jeszcze gdy już poznaliśmy dokładnie proces, wprowadziliśmy wszystkie zmiany jakie zgłosił nam klient w trakcie odbioru oraz po wyprowadzeniu wszystkich początkowych błędnych założeń w trakcie programowanie offline. Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Spis treści serii artykułów: Czysty kod w praktyce - część 1 Czysty kod w praktyce - część 2 DRY (ang. Don't Repeat Yourself, pol. Nie powtarzaj się) – reguła stosowana podczas wytwarzania oprogramowania, zalecająca unikanie różnego rodzaju powtórzeń wykonywanych przez programistów - na przykład unikanie tych samych czynności podczas kompilowania, unikanie wklejania (lub pisania) tych samych (lub bardzo podobnych) fragmentów kodu w wielu miejscach. Wielokrotne użycie tego samego kodu to podstawa programowania (Zmiana implementacji tylko w jednym miejscu oraz lepsza czytelność kodu oraz łatwość w utrzymaniu). Code for the Maintainer – czyli programuj tak jakbyś to robił dla osoby, która będzie później utrzymywać ten kod. Praca przy cudzym kawałku kodu to (przeważnie) najbardziej wymagająca i największa część pracy każdego programisty. Nie powinniśmy utrudniać sobie tego zadania. Zadbajmy więc o to, aby nie trzeba było się zbytnio głowić nad kawałkiem Twojego kodu. Pisz kod w taki sposób w jakim chciałbyś go otrzymać od kolego po fachu. Boy scout rule – zawsze pozostaw po sobie kod czystszy niż go zastałeś. REP - Reuse-release equivalence principle – ponowne użycie kodu to nie kopiowanie funkcji czy podprogramu z jednego projektu do drugiego. Jeżeli chcemy użyć ponownie kod, to należy go wydzielić do biblioteki, którą później możemy użyć ponownie. Klasy oraz funkcje, które są przeznaczone do ponownego wykorzystania, powinny być w jednej paczce, aby mogła stać się reużywalną biblioteką. Nie powinny się tam znaleźć te, które nie są przeznaczone do ponownego wykorzystania. W skrócie - albo wszystkie są reużywalne, albo żadne. Zdecydowanie ułatwi to edycje biblioteki oraz rozbudowę. Nie zawsze zasada KISS lub DRY jest słuszna! Weźmy np. zasadę DRY – Don’t Repeat Yourself – piszemy kod, który jest reużywalny, nie powtarzamy logiki zawartej w jednym miejscu w aplikacji, w innym. Tylko, że z tego rodzą się czasem klasy czy funkcje potwory. Okazuje się, że mają wiele rozglłęzień, skomplikowaną strukturę – tylko po to, żeby nie powtórzyć się w innym miejscu. I tu mamy problem – jest to świadome naginanie reguły KISS – Keep It Simple Stupid – która mówi o tym, żeby kod był tak prosty, jak to tylko możliwe. Wniosek nasuwa się jeden – nie istnieje srebrna kula. Nie istnieje też żaden złoty środek. Nie ma idealnego scenariusza, w którym weźmiemy garść złotych myśli (nasze reguły) i mamy gotowy przepis na sukces. Trzeba pamiętać o tym, żeby nie mieć klapek na oczach. Należy umiejętnie żonglować poszczególnymi zasadami, żeby osiągnąć satysfakcjonujący, końcowy rezultat. Zasady tylko wspomagają nas jako programistów, są narzędziem, które musimy umiejętnie wykorzystać. Zmiana osoby w projekcie a jakość kodu – jaki ma to wpływ na projekt i dochód? Wraz ze wzrostem bałaganu w oprogramowaniu niewątpliwie spada efektywność zespołu, co w znacznym stopniu paraliżuje projekt. Wobec spadku efektywności osoba odpowiedzialna za dany projekt zwykle dodaje do projektu więcej osób w nadziei, że podniesie to wydajność zespołu i uda się wyprowadzić problem tak, aby klient odebrał oprogramowanie. Jednak nowe osoby nie są zaznajomione z projektem oraz procesem jaki się tam odbywa, zasadą działania współpracujących ze sobą klas, czujników itd. Nie wiedzą, na jakie zmiany projektu mogą sobie pozwolić, a jakie spowodują jego naruszenie czy też zaprzestanie działania innych części kodu projektu. Dodatkowo, tak jak wszyscy pozostali w zespole, są pod ogromną presją zwiększenia wydajności swojej pracy, często w nadgodzinach czy dniach dla nich domyślnie wolnych. Należy tutaj pamiętać, że praca programisty to nie to samo, co człowieka na produkcji. Efektywność programisty to ok 6 godzin na dobę, ponieważ jest to praca umysłowa, wymagająca skupienia oraz kreatywnego myślenia. Należy mieć również na uwadze fakt, że to nie kierownictwo, ale programista jest fachowcem w programowaniu i zarządzaniu kodem i to programista musi umieć wywrzeć na swoich przełożonych świadomość jak ważny jest czysty kod. Nic nie ma tak głębokiego i w długim czasie degradującego wpływu na prowadzenie projektu, jak zły kod. Po pierwszym nieudanym projekcie spowodowanym złym kodem, lider projektu powinien wyciągnąć wnioski, aby zapobiec podobnym sytuacjom w przyszłości. Jednym z działań mającym zapobiegać bałaganowi w kodzie jest refaktoryzacja. Spowoduje to wprawdzie dodatkowe obłożenie czasowe (refaktoryzacja jest kosztowna czasowo, ale jest istotnym elementem zarządzania projektem informatycznym), które należy wziąć pod uwagę w trakcie ustalania harmonogramu projektu, ale zmniejsza prawdopodobieństwo pisania całego kodu od nowa. Podsumowanie Podsumowując, nie istnieje coś takiego, jak szablon dobrych zasad projektowania. Styl pisania kodu należy dostosować do projektu jaki jest tworzony, jego zagadnień czy problematyki – należy jednak być w tym bezwzględnie konsekwentnym. Jeśli będziemy w taki sposób pracować, efektywność naszej pracy będzie stale rosnąć. Jeśli czytelnik artykułu jest początkującym programistą, to stosując powyższe metody zoptymalizuje i uporządkuje swój kod, a co za tym idzie, jego projekty będą bardziej satysfakcjonujące i być może dzięki temu stanie się zawodowym programistą czego z całego serca życzę. Spis treści serii artykułów: Czysty kod w praktyce - część 1 Czysty kod w praktyce - część 2 Bibliografia: Martin R. - Czysty kod. Podręcznik dobrego programisty https://pl.wikipedia.org/ https://i1.wp.com/blog.knoldus.com/wp-content/uploads/2018/06/cleancode.png?fit=743%2C656&ssl=1 https://www.wykop.pl/cdn/c3201142/comment_kIUd4zw6h9I4Wt4s2yRz977DpSEBAUOv.jpg https://i.ytimg.com/vi/Fevz-Kb4bxc/maxresdefault.jpg https://cdn5.vectorstock.com/i/1000x1000/77/09/clean-code-vector-4247709.jpg https://www.perforce.com/sites/default/files/image/2019-09/image-blog-social-writing-clean-code_0.png https://3.bp.blogspot.com/-lcAjDyJMDzw/UpOnmQVcPyI/AAAAAAAALYQ/Zqf4yEU-z-s/s640/good_code.png https://1024kb.pl/wp-content/uploads/2018/08/var-1024x516.png https://res.cloudinary.com/teepublic/image/private/s--yz3dO1aO--/t_Resized Artwork/c_fit,g_north_west,h_954,w_954/co_ffffff,e_outline:48/co_ffffff,e_outline:inner_fill:48/co_ffffff,e_outline:48/co_ffffff,e_outline:inner_fill:48/co_bbbbbb,e_outline:3:1000/c_mpad,g_center,h_1260,w_1260/b_rgb:eeeeee/c_limit,f_jpg,h_630,q_90,w_630/v1536857572/production/designs/3150589_0.jpg https://ronjeffries.com/xprog/wp-content/uploads/Photo-Dec-07-1-45-14-PM.jpg https://i0.wp.com/image.slidesharecdn.com/cleancode-110520031623-phpapp02/95/clean-code-5-728.jpg?w=584&ssl=1 https://cloudogu.com/images/blog/2018/CleanCode_TheartofProgramming.jpg https://slideplayer.com/slide/12686823/76/images/6/What+is+clean+code+Clean+code+can+be+read%2C+and+enhanced+by+other+developers.+It+has+meaningful+names..jpg https://octoperf.com/img/blog/are-you-buying-quality-software/clean-code.png https://media.makeameme.org/created/clean-code-clean.jpg https://image.slidesharecdn.com/cleancode-vortrag-03-2009-pdf-121006112415-phpapp02/95/clean-code-pdf-version-5-728.jpg?cb=1349523162 https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/cde14740-68df-45f4-ab1f-e36ee4f16a47/cleancode-7.png
  11. 5 punktów
    Próbowaliście kiedyś sprawić by wasz projekt DIY wysyłał wam e-mail lub powiadomienie w określonej sytuacji. A może zastanawialiście się jak podłączyć asystenta głosowego do waszego projektu i sterować nim poleceniami głosowymi? Wydaje się to trudnym zadaniem, w końcu nie każdy majsterkowicz musi być mistrzem programowania. Nic bardziej mylnego! Z pomocą przychodzi IFTTT. IFTTT ("if this, then that") to darmowe narzędzie służące do komunikowania ze sobą dwóch serwisów/urządzeń. Daje możliwość połączenia waszego projektu z serwisami takimi jak Google Assistant, Amazon Alexa, Facebook, Twitter, Dropbox i wiele innych. Używanie IFTTT jest bardzo proste i (prawie) nie wymaga umiejętności programowania. Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Pierwsze połączenie Aby używać IFTTT musimy założyć konto (możemy użyć także konta Google lub Facebook). Aby utworzyć połączenie klikamy ikonkę naszego avatara i wybieramy “Create” (lub przechodzimy do ifttt.com/create). Tworzenie połączeń (applets) w IFTTT opiera się na zasadzie "if this, then that". Na początku decydujemy kiedy nasze połączenie na zadziałać (this) - wybieramy wyzwalacz (trigger) dla naszej akcji i ustalamy warunki jakie muszą zachodzić. Klikamy “This” i wybieramy interesujący nas serwis. (Ja wybrałem Button Widget - przycisk w aplikacji IFTTT [Android, iOS]). Teraz wybieramy kiedy wybrany serwis ma dać znać, że warunek został spełniony (dla Button Widget jest dostępne tylko Button Press - zawsze kiedy przycisk wciśnięty). Gdy ustawiliśmy już wyzwalacz czas na ustawienie akcji (co ma się zadziać po zadziałaniu wyzwalacza). Klikamy “That”, wybieramy interesujący nas serwis (ja wybrałem Email) i wybieramy rodzaj akcji (dla Email dostępne tylko Send me an email). Teraz musimy ustalić co dokładnie ma się zadziać (dla Email ustalamy tytuł i treść wiadomości w HTML). Klikając przycisk Add ingredient możemy dodać do akcji dane zwrócone przez wyzwalacz (dla Button Widget lokalizacja i czas). Klikamy “Create action”. Teraz widzimy podsumowanie naszego połączenia i możemy go nazwać. Klikamy “Finish”. Nasze połączenie jest gotowe. Pora je przetestować! Żeby uruchomić Button Widget musimy pobrać aplikację IFTTT [Android, iOS] i utworzyć Widget IFTTT na ekranie telefonu. Kliknięcie w przycisk (widget) powoduje wyzwolenie akcji. Po chwili (u mnie 5-10 sekund) powinien do nas przyjść e-mail. Połączenie z mikrokontrolerem. Sposób I - Adafruit IO Teraz spróbujemy połączyć nasz mikrokontroler (u mnie ESP8266 NodeMCU) z IFTTT przy pomocy serwisu Adafruit IO. Jest to darmowy (w wersji podstawowej) serwis do obsługi danych w chmurze stworzony z myślą o urządzeniach IoT. Pozwala na łatwą integrację z Arduino, ESP, RaspberryPi itp. Aby korzystać z Adafruit IO trzeba założyć konto. Po zalogowaniu klikamy w zakładkę “Feeds”. Klikamy przycisk “Action” i wybieramy “Create a new feed”. Wpisujemy wybraną nazwę (u mnie “Forbot”) i opcjonalny opis. Klikamy “Create”. Po wybraniu go z listy feed’ów widzimy na wykresie dane, które zostały do niego dostarczone. Na razie nic tu nie ma. Aby zobaczyć efekty musimy sięgnąć po mikrokontroler. Tak jak pisałem wyżej pokażę przykład na ESP8266. Do połączenia go z Adafruit IO skorzystamy z przykładowego kodu udostępnionego przez Adafruit. Podłączenie: przycisk - jedna nóżka do GND, druga do GPIO4 dioda - jedna nóżka do GND, druga przez rezystor (u mnie 510𝛀) do GPIO13 *Numery GPIO nie są zgodne z oznaczeniami na płytce. Sprawdź w internecie gdzie na twojej płytce znajdują się piny GPIO4 i GPIO13. // Instructables Internet of Things Class sample code // Circuit Triggers Internet Action // A button press is detected and stored in a feed // An LED is used as confirmation feedback // // Modified by Becky Stern 2017 // based on the Adafruit IO Digital Input Example // Tutorial Link: https://learn.adafruit.com/adafruit-io-basics-digital-input // // Adafruit invests time and resources providing this open source code. // Please support Adafruit and open source hardware by purchasing // products from Adafruit! // // Written by Todd Treece for Adafruit Industries // Copyright (c) 2016 Adafruit Industries // Licensed under the MIT license. // // All text above must be included in any redistribution. /************************ Adafruit IO Configuration *******************************/ // visit io.adafruit.com if you need to create an account, // or if you need your Adafruit IO key. #define IO_USERNAME "YOUR_USERNAME" // <-----ZMIENIĆ #define IO_KEY "YOUR_KEY" // <-----ZMIENIĆ /******************************* WIFI Configuration **************************************/ #define WIFI_SSID "YOUR_SSID" // <-----ZMIENIĆ #define WIFI_PASS "YOUR_PASSWORD" // <-----ZMIENIĆ #include "AdafruitIO_WiFi.h" AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS); /************************ Main Program Starts Here *******************************/ #include <ESP8266WiFi.h> #include <AdafruitIO.h> #include <Adafruit_MQTT.h> #include <ArduinoHttpClient.h> #define BUTTON_PIN 4 #define LED_PIN 13 // button state int current = 0; int last = 0; // set up the 'command' feed AdafruitIO_Feed *command = io.feed("YOUR_FEED"); // <-----ZMIENIĆ void setup() { // set button pin as an input pinMode(BUTTON_PIN, INPUT_PULLUP); pinMode(LED_PIN, OUTPUT); // start the serial connection Serial.begin(115200); // connect to io.adafruit.com Serial.print("Connecting to Adafruit IO"); io.connect(); // set up a message handler for the 'command' feed. // the handleMessage function (defined below) // will be called whenever a message is // received from adafruit io. command->onMessage(handleMessage); // wait for a connection while(io.status() < AIO_CONNECTED) { Serial.print("."); delay(500); } // we are connected Serial.println(); Serial.println(io.statusText()); } void loop() { // io.run(); is required for all sketches. // it should always be present at the top of your loop // function. it keeps the client connected to // io.adafruit.com, and processes any incoming data. io.run(); // grab the current state of the button. // we have to flip the logic because we are // using INPUT_PULLUP. if(digitalRead(BUTTON_PIN) == LOW) current = 1; else current = 0; // return if the value hasn't changed if(current == last) return; // save the current state to the 'command' feed on adafruit io Serial.print("sending button -> "); Serial.println(current); command->save(current); // store last button state last = current; } // this function is called whenever a 'command' message // is received from Adafruit IO. it was attached to // the command feed in the setup() function above. void handleMessage(AdafruitIO_Data *data) { int command = data->toInt(); if (command == 1){ //light up the LED Serial.print("received <- "); Serial.println(command); digitalWrite(LED_PIN, HIGH); delay(500); digitalWrite(LED_PIN, LOW); } else { Serial.print("received <- "); Serial.println(command); } } Zastępujemy: YOUR_USERNAME nazwą użytkownika z Adafruit IO YOUR_KEY kluczem Adafruit IO * YOUR_SSID nazwą twojej sieci wifi YOUR_PASSWORD hasłem do twojej sieci wifi YOUR_FEED nazwą twojego feed’a (u mnie “Forbot”) *klucz Adafruit IO można sprawdzić klikając “AIO Key” w prawym górnym rogu po zalogowaniu w Adafruit IO Po wgraniu programu, gdy wciśniemy przycisk to do naszego feed’a zostanie wysłana wartość 1. Dioda odczyta tą wartość z feed’a i zapali się na chwilę. Po puszczeniu przycisku zostanie wysłana wartość 0. Możemy zobaczyć to na wykresie w podglądzie feed’a. Wartości możemy dodawać też “ręcznie” klikając “Add Data” w widoku feed’a. Po dodaniu wartości “1” dioda powinna na chwilę się zaświecić. Adafruit IO mamy już skonfigurowane, teraz pora połączyć je z IFTTT. Nasze urządzenie możemy używać jako wyzwalacz (po kliknięciu w przycisk zrób ...coś) lub akcję (dzieje się coś, do feed’a dodawana jest wartość “1”, dioda świeci). Urządzenie jako wyzwalacz Sprawimy, że po wciśnięciu przycisku zostanie wysłany do nas e-mail zawierający datę i godzinę wciśnięcia przycisku. W IFTTT klikamy “Create” i jako “This” wybieramy Adafruit (użycie za pierwszym razem wymaga podłączenia konta Adafruit). Wybieramy opcję “Monitor a feed on Adafruit IO” (chcemy aby zadziałało dla konkretnej wartości, a nie dla jakiejkolwiek). Ustawiamy za feed nazwę naszego feed’a, za relationship “equal to” (chcemy aby wartość była równa), za value “1” (po wciśnięciu przycisku wysyłamy wartość “1”). Klikamy “Create trigger”. Za “That” wybieramy Email i konfigurujemy tytuł i treść wiadomości. Klikamy “Create action”. Klikamy “Finish” i gotowe! Od teraz za każdym razem gdy klikniemy przycisk dostaniemy email. Niestety w tej metodzie może występować opóźnienie ~1min. Urządzenie jako akcja Teraz trochę bardziej ambitne zadanie. Będziemy kazać Asystentowi Google mignąć naszą diodą. Klikamy Create i jako This wybieramy Google Assistant. Do wyboru mamy 4 możliwości: wypowiedzenie prostej frazy, wypowiedzenie frazy z liczbą, wypowiedzenie frazy z tekstem, wypowiedzenie frazy z liczbą i tekstem. 2, 3 i 4 opcja pozwalają nam wysyłać dane do Adafruit IO. Może to przydatne np. gdy chcemy sterować poziomem oświetlenia lub czasem świecenia. W naszym prostym przykładzie wybierzemy prostą frazę bez danych. Wpisujemy komendę po której wypowiedzeniu ma zamigać dioda (możemy wpisać na max 3 sposoby), odpowiedź którą ma udzielić asystent i wybieramy język (niestety w IFTTT nie obsługuje języka polskiego). Klikamy “Create trigger”. Za That wybieramy Adafruit i “Send data to Adafruit IO”. Wybieramy nazwę feed’a i wpisujemy wartość, którą chcemy do niego wysłać. Klikamy “Create action” , “Finish” i gotowe. Od teraz gdy powiemy asystentowi komendę to on wyśle “1” do Adafruit IO, a dioda zamiga. Połączenie z mikrokontrolerem. Sposób II - Webhooks Drugi sposób polega na pomijaniu pośrednika (Adafruit IO) i bezpośrednim kontakcie z IFTTT. Jest on trochę bardziej skomplikowany, ale za to działa szybciej (u mnie opóźnienia max 5s). Stwórzmy urządzenie, które po wciśnięciu przycisku wyświetli powiadomienie na telefonie. Potrzebna będzie aplikacja IFTTT (jeśli nie chcesz jej pobierać możesz ustawić inną akcję). Na początku tworzymy nowy applet w IFTTT. Za This wybieramy Webhooks, dalej “Receive a web request”. Wybieramy nazwę zdarzenia (event name) - ja ustawiłem “button_pressed”. Za That wybieramy Notifications, dalej mamy do wyboru dwie opcje: powiadomienie podstawowe i rozbudowane. Na początek wybieramy podstawowe (Send a notification from the IFTTT app). Teraz ustalamy jaka ma być treść powiadomienia. Oprócz zwykłego tekstu klikając “Add ingredient” możemy dodać nazwę zdarzenia oraz wartości do niego dołączone. Jednorazowo możemy wysyłać od zera do trzech wartości. Przechodzimy dalej i klikamy “Finish”. Teraz klikamy naszego awatara i przechodzimy do My services. Z listy serwisów wybieramy Webhooks. Klikamy na przycisk “Documentation” w prawym górnym rogu. Na górze wyświetla się nasz indywidualny klucz. Gdy w miejsce {event} wstawimy nazwę naszego zdarzenia to na dole pojawi się adres URL, pod który trzeba wejść aby wywołać zdarzenie (na razie bez dodatkowych wartości). Wygląda on tak: https://maker.ifttt.com/trigger/nazwa_zdarzenia/with/key/klucz Wpiszmy teraz w przeglądarce ten adres. Powinna pojawić się nam informacja, że udało się uruchomić zdarzenie, a na nasz telefon powinno przyjść powiadomienie, którego treść wcześniej ustawiliśmy. Teraz spróbujmy przesłać dodatkowe wartości. Aby to zrobić za pomocą adresu URL musimy na końcu dodać: ?value1=wartość_1&value2=wartość_2&value3=wartość_3 (oczywiście możemy przesłać także mniej niż 3 wartości). Nasz URL będzie wyglądał tak: https://maker.ifttt.com/trigger/nazwa_zdarzenia/with/key/klucz?value1=wartość_1&value2=wartość_2&value3=wartość_3 Za wartości wstawiamy przykładowe wartości i wklejamy w przeglądarkę. W przeglądarce wyświetli nam się ten sam komunikat co wcześniej, ale powiadomienie na telefonie będzie już zawierać wpisane wartości (w przykładzie: 123, abc, Ala_ma_kota). Jeżeli już wiemy jak działa ta metoda to spróbujmy ją wykorzystać w naszym mikrokontrolerze. Od poprzedniego połączenia odłączamy diodę (zostaje przycisk - jedna nóżka do GND, druga do GPIO4). Teraz czas na program. W internecie możemy znaleźć kilka bibliotek ułatwiających pracę z IFTTT: pierwsza nie jest już wspierana przez autora i nie działa (przynajmniej u mnie), używając drugiej można wysłać tylko 0 lub 3 wartości, co nie zawsze jest wygodne (np. jak chcemy wysłać 1 wartość to za pozostałe dwie musimy wstawić ręcznie ”” ), trzecia działa, ale wymaga biblioteki ArduinoJson w wersji 5.x.x (z najnowszą 6.x.x nie działa). Bawiąc się z IFTTT i ESP8266 stworzyłem własną bibliotekę. Stworzyłem ją co prawda głównie do celów edukacyjnych (to moja pierwsza biblioteka, więc krytyka mile widziana), ale możemy ją teraz wykorzystać. Przed użyciem musimy pobrać i zaimportować bibliotekę. Teraz otwieramy przykładowy kod z tej biblioteki i zmieniamy dane logowania do wifi, nazwę zdarzenia i klucz IFTTT. #include <ESP8266IFTTTWebhook.h> #include <ESP8266WiFi.h> ///////////////////////////////////////////////// const char* ssid = "YOUR_SSID"; const char* password = "YOUR_PASSWORD"; const char* API_KEY = "YOUR_KEY"; const char* WEBHOOK_NAME = "YOUR_EVENT_NAME"; //////////////////////////////////////////////// //button -> GPIO4 #define BUTTON_PIN 4 int current = 0; int last = 0; //Create ifttt object ESP8266IFTTTWebhook ifttt (WEBHOOK_NAME, API_KEY); void setup() { Serial.begin(115200); pinMode(BUTTON_PIN, INPUT_PULLUP); wifiConnect(); } void loop() { if(digitalRead(BUTTON_PIN) == LOW) current = 1; else current = 0; if(current != last){ last = current; if (current == 1) { ifttt.trigger("1","a","Something"); //3 values delay(5000); ifttt.trigger("1"); //1 value } } } void wifiConnect(){ Serial.print("connecting to "); Serial.println(ssid); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); } Jak korzystać z biblioteki? Na początku tworzymy obiekt klasy ESP8266IFTTTWebhook instrukcją ESP8266IFTTTWebhook ifttt (WEBHOOK_NAME, API_KEY); Od teraz możemy wywoływać zdarzenie za pomocą metody trigger() podając od zera do trzech argumentów: ifttt.trigger(opcjonalny_argument1, opcjonalny_argument2, opcjonalny_argument3,); . Po uruchomieniu programu ESP łączy się z wifi i po wciśnięciu przycisku wywołuje zdarzenie z argumentami "1","a","Something", a po 5 sekundach z jednym argumentem “1”. Działanie jest pokazane na filmie: Możliwości IFTTT Powyższe przykłady nie były zdumiewające, ale służyły tylko do celów edukacyjnych. W IFTTT możemy połączyć wiele różnych serwisów od automatyki domowej poprzez NASA i Wikipedię po Youtube. Pozwala to uzyskać naprawdę niesamowite, użyteczne i ciekawe kombinacje. IFTTT_przyklad_IO.zip
  12. 4 punkty
    Jak chodzi o CE mam głównie doświadczenie z dyrektywą medyczną, czyli 93/42/EWG i moje podejście może się wydawać niektórym zbyt rygorystyczne, ale pozwólcie że włączę się do dyskusji. Zacznijmy od samego początku, czyli dyrektyw oraz norm. Dyrektywy są prawem europejskim i nie tylko obowiązują jak każde inne prawo, ale są również nadrzędne nad prawem państw członkowskich. Same dyrektywy jako akty prawne są dostępne publicznie i są darmowe. Natomiast ich zapisy są bardzo ogólnikowe i to sprawia, że dla osób technicznych mogą wydawać się dziwne, albo nielogiczne - takie "lanie wody". Natomiast prawnicy rozumieją te zapisy aż za dobrze. W dyrektywach znajdziemy właśnie cytowane wcześniej fragmenty, czyli co ma zapewnić producent, albo dokładniej - podmiot wprowadzający na rynek. Te zapisy są mocno życzeniowe, urządzenia mają być bezpieczne, poprawnie skonstruowane, udokumentowane itd. Natomiast dyrektywy zawierają niewiele informacji, albo wcale o tym jak takie piękne idee wcielić w życie. Po prostu producent, albo podmiot który wprowadza na rynek UE wyroby innego producenta (np. spoza unii), ma wykazać, że wykonał niezbędne kroki, aby wymogi dyrektywy spełnić. I tutaj pojawia się problem. Bo jak zapewnić np. bezpieczeństwo urządzenia? Można latami prowadzić badania, wykonać ogromną pracę naukową aby udowodnić, że nasz produkt nie stwarza żadnych zagrożeń - i jest tutaj o wiele trudniej niż nam się wydaje. Bo np. co będzie jeśli dziecko weźmie nasz amatorski kalkulator, który ma przyciski drukowane na drukarce 3D, połknie przycisk i się zadławi? Kto będzie odpowiadał za takie zdarzenie? Czy umieściliśmy w instrukcji informację że kalkulator nie może być używany przez dzieci do lat 3? Jeszcze do niedawna taki rozważania pewnie uznalibyśmy za idiotyczne. Niestety moda na procesy o odszkodowanie zapoczątkowana w USA dotarła już do starej europy, więc moglibyśmy być bardzo zaskoczeni o jakie kwoty przyjdzie nam się sądzić... Wracając do CE. Ktoś wpadł na genialny w prostocie pomysł, jak zarabiać na darmowym teoretycznie prawie. Mamy dyrektywę, każdym może ją pobrać z sieci i za darmo czytać. Ale jak wiemy, ciężko jest z nią cokolwiek praktycznego zrobić. I tutaj pojawiają się tzw. normy zharmonizowane, na które potocznie mówimy po prostu normy. W prawie unii jest przyjęte, że jeśli wyrób spełnia wymagania norm zharmonizowanych, to spełnia też wymagania dyrektywy. Jest w tym tylko jeden kruczek - normy są już płatne, a np. wykonywanie badań na zgodność z normami jest płatne nawet bardziej. Mamy więc taką sytuację. Dyrektywy unijne są prawem i działają jak każde inne prawo - można je łamać i narażać się na konsekwencje. Ale aby go przestrzegać właściwie musimy spełniać wymogi norm zharmonizowanych, inaczej zawsze ryzykujemy, że ktoś oskarży nas o niedopełnienie obowiązków wynikających z dyrektywy i zażąda odpowiedniego odszkodowania. No i tutaj dochodzimy do sedna sprawy. Spełniając normy, posiadając odpowiednie wyniki badań - niezależnie czy wykonanych samemu, czy w zewnętrznej firmie, możemy nadać oznaczenie CE oraz podpisać się pod deklaracją zgodności, ale co najważniejsze spać spokojnie. Natomiast jeśli nie wykonamy tych kroków, to albo bezpośrednio łamiemy prawo, albo narażamy się na pozew o odszkodowanie, za to że nie spełniliśmy odpowiednich wymagań. To wszystko brzmi może nieco abstrakcyjnie, bo przecież "nikomu nic się nie stanie", ale jak wspominałem już o sądach i prawnikach - nawet pozornie prosty kalkulator może narazić nas na ogromne problemy. Jeśli nie wierzycie to poczytajcie np. o pozwach składanych przez rodziców za to że dziecko w supermarkecie otworzyło butelkę żrącego płynu i wypiło. Mamy teraz takie czasy, że nie jest to wina rodziców, ani dziecka, ale producenta że nie zrobił lepszego zamknięcia no i sklepu... W każdym razie oznaczenie CE to poważniejsza sprawa niż się wydaje.
  13. 4 punkty
    @Smitty, @Karor111, @satanistik, @hotdeejay - gratuluję wyróżnień, świetnie czytało się Wasze artykuły! @SquareShox - gratuluję nagrody publiczności! @Xevaquor, @Sandra, @ethanak, @Karrol, @Leoneq, @Elvis - gratuluję wygranej, świetna robota! Będę się z Wami kontaktował w celu przekazania upominków oraz nagród
  14. 4 punkty
    Proponuję nie zakładać nic ponad to, że ktoś szuka darmowej informacji kto i za ile taki projekt chciałby wykonać. Jak dla mnie tekst: "Budżet 10000zł. Jeśli nierealny proszę składać także wyższe oferty." oznacza, że to nie jest poważna oferta. Przecież nikt podając budżet nie zakłada, że szuka kogoś za więcej... cytując jak sądzę autora tej oferty "Gdzie sens? Gdzie logika?".
  15. 4 punkty
    Cześć, nie chciałem się wtrącać bo to nie moja sprawa, ale dyskusja poszła w takim kierunku, że się odezwę.. @atMegatona, czy myślisz, że za pomocą Arduino spełnisz wymagania standardu "MISRA" (właściwie to jest kilka wersji tego standardu z róźnych lat). Wiesz ile kosztują narzędzia software'owe pozwalające stwierdzić zgodność kodu źródłowego z jedną z wersji tego standardu (dla konkretnego standardu języka C++ i przeważnie jedne ściśle określonej wersji kompilatora).? Myślę tu głównie o statycznych analizatorach kodu w języku C++. - jeśli nie wiesz to tym lepiej , bo często na nie nie stać firm. Po drugie zgodzę się tu niestety z kolegą @Elvis - nie podpisanie się z imienia i nazwiska jest podejrzane. Po trzecie, jak ktoś podaje tyle szczegółów i ma takie wymagania odnośnie jakości oprogramowania to niech się wysili na opis software'u w jakimś używanym technicznym standardzie np. w języku UML (no bo jak później rozstrzygać czy software jest zgodny ze specyfikacją). Tu link do jednego z narzędzi pozwalających stwierdzić zgodność ze standardem MISRA: https://www.perforce.com/solutions/audit-and-compliance?utm_leadsource=cpc-googleadwords&utm_campaign=QAC-CODINGSTANDARDS-RoW&utm_source=googleadwords&utm_medium=cpc&utm_adgroup=MISRA Pozdrawiam
  16. 4 punkty
    W poprzednich częściach udało się zebrać dane treningowe oraz przygotować sieć neuronową. Zostało ostatnie i najprzyjemniejsze, czyli napisanie końcowego programu. Jako punkt wyjścia używam program w wersji użytej do zbierania danych. Jest w nim już praktycznie wszystko co potrzebne, czyli konfiguracja modułów peryferyjnych oraz odczyt danych z akcelerometru. Spis treści serii artykułów: Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.1 Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.2 Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.3 Przechodzimy do widoku CubeMX i odnajdujemy wyjątkowo kiepsko widoczny przycisk "Additional Software": Po jego naciśnięciu zobaczymy okno pozwalające na dodawanie modułów do naszego projektu. Nas interesuje moduł "STMicroelectronics.X-CUBE-AI". Jeśli wcześniej tego nie zrobiliśmy, musimy najpierw ten moduł pobrać i zainstalować. Niezbędne opcje zobaczymy w prawej części okna: Po instalacji modułu X-CUBE-AI wystarczy zaznaczyć checkbox i użyć podstawową funkcjonalność, czyli Core. Gdybyśmy planowali wykorzystanie sieci neuronowej w poważniejszym programie, takie ustawienie byłoby chyba najlepszą opcją. Jednak na potrzeby nauki możemy wykorzystać gotowiec i dodać jeszcze opcję "Application Template". Jakość kodu, który zostanie dodany jako "template" jest delikatnie mówiąc niezbyt imponująca. Dlatego napisałem, że lepiej chyba nie używać go w poważniejszych zastosowaniach - ale do nauki to bardzo wygodne rozwiązanie. Teraz możemy wrócić do głównego widoku CubeMX i odszukać moduł "STMicroelectronics.X-CUBE-AI" w kategorii "Additional Software": W środkowej części okna widzimy konfigurację modułu naciskamy przycisk-zakładkę z symbolem "+" i dodajemy naszą sieć. Domyślna nazwa to "network", ale warto ją zmienić na coś bardziej opisowego, ja wybrałem "forbot_ai". Następnie wybieramy typ modelu "Keras" oraz sam model, czyli nasz plik data2.h5 utworzony w poprzedniej części. Gdy wszystko jest już gotowe możemy nacisnąć przycisk "Analyze" i poczekać aż X-CUBE-AI sprawdzi, czy nasza sieć pasuje do wybranego mikrokontrolera. Wynik powinien wyglądać następująco: Jak widzimy cała skomplikowana sieć wykorzystuje 8.39KB pamięci flash oraz 72 bajty RAM. To bardzo mało jak dla STM32L475, ale nawet znacznie słabiej wyposażone mikrokontrolery mogą skorzystać z możliwości jakie dają sieci neuronowe. Natomiast w przypadku większych sieci możemy wykorzystać opcję kompresji. Po zapisaniu projektu i wygenerowaniu kodu zobaczymy dwie ważne zmiany w pliku main.c. Przed pętlą główną pojawi się wywołanie MX_X_CUBE_AI_Init(), a w samej pętli znajdziemy MX_X_CUBE_AI_Process(). Inicjalizacja nie będzie nas interesować i możemy zostawić wygenerowany kod bez zmian. Warto natomiast zapoznać się z treścią funkcji MX_X_CUBE_AI_Process(). Kod, który w niej znajdziemy może przyprawić o palpitacje serca, ale na szczęście to tylko przykład, chociaż chyba niezbyt udany. Możemy więc spróbować zrozumieć o co w tym kodzie chodzi - i szybko odkryjemy, że jedyne co istotne to wywołanie funkcji aiRun(), która przyjmuje dwa parametry: pierwszy to tablica z danymi wejściowymi dla sieci, a w drugiej pojawią się wyniki. Warto jeszcze odszukać jakie są wymiary tych tablic: AI_FORBOT_AI_IN_1_SIZE ma wartość 128, i jest to wielkość zdefiniowana podczas trenowania sieci, właśnie tyle danych musimy podać na wejście. Wielkość danych wynikowych znajdziemy w stałej AI_FORBOT_AI_OUT_1_SIZE, która ma wartość 2. Przykładowy kod wykonuje mnóstwo dziwnych operacji na typach danych, ale w rzeczywistości dane wejściowe i wyjściowe to zwykłe float-y. Możemy więc skasować wszystko co automat nam wygenerował i napisać pierwszy kod: void MX_X_CUBE_AI_Process(void) { /* USER CODE BEGIN 1 */ float nn_input[AI_FORBOT_AI_IN_1_SIZE]; float nn_output[AI_FORBOT_AI_OUT_1_SIZE]; aiRun(nn_input, nn_output); /* USER CODE END 1 */ } Taki program oczywiście nic nie robi, ani nie działa poprawnie, ale umieściłem go aby pokazać jak łatwe jest używanie sieci neuronowej. Musimy wypełnić tablicę nn_input danymi do przetworzenia, następnie wywołujemy aiRun() i w nn_output mamy wyniki. Poniżej wstawiam już pełny kod: static uint32_t next_ms = 1000; static float nn_input[AI_FORBOT_AI_IN_1_SIZE]; float nn_output[AI_FORBOT_AI_OUT_1_SIZE]; while (HAL_GetTick() < next_ms) {} next_ms += READ_DELAY_MS; lsm6_value_t acc; acc = lsm6_read_acc(); for (int i = 0; i < AI_FORBOT_AI_IN_1_SIZE - 1; i++) nn_input[i] = nn_input[i + 1]; nn_input[AI_FORBOT_AI_IN_1_SIZE - 1] = sqrt(acc.x * acc.x + acc.y * acc.y + acc.z * acc.z); aiRun(nn_input, nn_output); if (nn_output[0] >= 0.5f) HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET); else HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET); Tablica nn_input jest definiowana jako statyczna, aby zachować historię pomiarów. Pomiary są wykonywane co 20ms, stąd czekanie w pętli while. Następnie odczytywana jest wartość przyspieszeń z akcelerometru. Wyniki przechowywane w tablicy nn_input są przesuwane o jedną pozycję, a najnowszy pomiar dodawany jest na końcu. Następnie wywoływana jest funkcja aiRun() i przetwarzane są wyniki. nn_ouput zawiera dwie wartości - pierwsza pozycja to prawdopodobieństwo, że nie wykryto gestu, druga to że wykryto - ich suma wynosi 1, więc wystarczyłaby jedna wartość, ale tak była trenowana sieć. Na podstawie obliczonego prawdopodobieństwa gaszona lub zapalana jest dioda LED. Poniżej efekt działania program (z drobną poprawką o której za moment): Uczenie sieci wymaga dużej ilości danych treningowych, w sumie im więcej tym lepiej. Niestety przykładowa sieć była trenowana na bardzo małej próbce. Efekty były i tak bardzo dobre, ale po wykryciu gestu pojawiały się pewne "zakłócenia". Po pierwsze czas zapalenia diody był bardzo różny, po drugie czasem dioda zapalała się kilka razy. Oczywiście najlepiej byłoby spędzić więcej czasu ucząc sieć, ale ja zastosowałem pewne obejście. W programie, po wykryciu machania płytką dioda jest zapalana na 1s - sieć mogłaby to robić sama, ale o wiele łatwiej było zmienić program. Podsumowanie Projekt opisany w tych artykułach miał być jedynie pierwszym krokiem, taką próbą użycia X-CUBE-AI. Planowałem poprawić opisywane przykłady, dodać bardziej rozbudowany projekt z innym czujnikiem oraz wykorzystać bardziej zaawansowaną topologię sieci. Ale jak to z planami często bywa, zostały niezrealizowane. Mam jedna nadzieję, że opisane możliwości zachęcą czytelników do eksperymentowania ze sztuczną inteligencją w systemach wbudowanych oraz dzielenia się swoimi doświadczeniami. Spis treści serii artykułów: Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.1 Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.2 Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.3
  17. 4 punkty
    Mit o naszym poznawczym uniwersalizmie, o naszej gotowości odebrania i zrozumienia informacji całkowicie, przez jej pozaziemskość, nowej - trwa niewzruszony, chociaż, otrzymawszy posłanie z gwiazd, zrobiliśmy z nim nie więcej, niżby zrobił dzikus, który, ogrzawszy się u płomienia podpalonych dzieł najmędrszych, uważa, że doskonale owe znalezisko wykorzystał! - Stanisław Lem Głos Pana Tymi słowami Stanisław Lem konkludował próbę odszyfrowania przez Człowieka tajemniczego sygnału z kosmosu, którego odkrycie było dziełem przypadku. Sygnał, pochodzący najprawdopodobniej od obcej cywilizacji z krańca Kosmosu stał się zagadką, poddającą w wątpliwość ludzką poznawczość. Pomimo licznych prób i wzajemnej współpracy grona naukowców, wiadomość udało się rozpracować tylko częściowo, a nawet uzyskany wynik stanowił dalsze ogniwo niezrozumienia i zdumienia. Chcąc opowiedzieć czym był sygnał, jak został odkryty i do jakich odkryć poprowadził grupę uczonych należy sięgnąć do książki Stanisława Lema „Głos Pana”. Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Dlaczego przytoczone zostały właśnie te słowa i w jaki sposób korespondują one z tematem artykułu? Jeżeli miałeś nadzieję, że przedstawione zostaną kulisy pierwszego kontaktu z obcą cywilizacją, to niestety możesz się lekko rozczarować, ale ta era (jeszcze) nie nadeszła - jakkolwiek byśmy tego nie chcieli i próbowali zaklinać czasoprzestrzeń. Pozwólmy, rzucić sobie światło na niemniej, a nawet równie ciekawą materię. Komunikacja Powracając do powieści Stanisława Lema, komunikacja, choć jednostronna przebiega tam zdalnie. Z otchłani kosmosu dobiega sygnał w postaci ciągu neutrin – cząstek nie posiadających masy, zdolnych do przenikania materii. Cząstki te nadawane są w pewnych odstępach czasowych. Uważnie przyglądając się całości emisji dostrzec można, pewne zależności powodujące, że w procesie widoczna jest pewna wiadomość. Komunikatu zostaje zapętlony, a zarówno nadawca jak i odbiorca kosmicznego komunikatu nie widzą się wzajemnie. Są przekonani o wzajemnym istnieniu, ale fizycznie, poza komunikatem nie mają ze sobą jakiegokolwiek innego kontaktu. Czy cała idea tego procesu nie wygląda w pewien sposób znajomo? Tak, to Test Turinga! Obca cywilizacja przesyłając komunikat zainicjowała proces Testu Turinga, w oczekiwaniu na sprzężenie zwrotne, które miało udowodnić naszą inteligencję. Barierą oddzielającą „komputer” od człowieka jest ogromna przestrzeń kosmiczna, a sam sygnał przyjmuje nieco inną formę, niż tą jaką przewidział w swoich założeniach Alan Turing. Niestety, cały mistycyzm pierwszego kontaktu sprowadzony zostaje do trochę bardziej prozaicznego, wcześniej znanego pojęcia. Ale czy spoglądanie w niebo to jedyny sposób na nawiązanie dialogu z przyszłością? Schodzimy na ziemię Dialog już trwa, rozmówcą jesteś również Ty, ale to Alan Turing jako jeden z pierwszych dostrzegł możliwość rozmowy Człowieka z maszyną. Tak, sztuczna inteligencja, bo o niej mowa, choć nieco bardziej ziemska posiada kosmiczne możliwości. To sztuczna inteligencja ma nasze dane, potrafi nimi skutecznie manipulować, gromadzić i na podstawie ich podejmować poważne decyzje. Prowadzi nasze auta, wynajduje nowe lekarstwa, decyduje czy będziesz wiarygodnym kredytobiorcą, a nawet bywa „zwyczajnym rozmówcą”. Komunikacja z SI przybiera różne formy, choć dysponujemy szeregiem języków programowania, gotowymi rozwiązaniami informatycznymi, a sama wiedza jest niemalże powszechnie dostępna to właśnie komunikacja stanowi najważniejsze zagadnienie rozwoju sztucznej inteligencji. O ile, gdy stworzone przez nas oprogramowanie zawarte w ryzach algorytmów jest nam niemal całkowicie posłuszne, o tyle sztuczna inteligencja w swojej idei zaczyna „żyć własnym życiem” – życiem tak autonomicznym, że język cyfrowy schodzi momentami na drugi plan, a nawet może nie być samodzielnie wystarczający. Oczywiście nic nie wskazuje na to, abyśmy mieli w całości zrezygnować z języków programowania na rzecz… no właśnie czego? Rozmowy? Siri, Asystent Google czy Alexa to systemy bazujące na sztucznej inteligencji, które uprzednio zaprogramowane dalszą część cybernetycznego życia spędzają komunikując się poprzez komunikację werbalną. Całość procesu: gromadzenie danych, przetwarzanie czy wydawane przez nią komunikaty to głównie wiadomości głosowe. Jest to oczywiste i zrozumiałe, ale pod postacią wygodnej dla nas funkcjonalności rozpoznawania głosowego oraz syntezatora mowy, ukryty pozostaje znaczący krok w rozwoju komunikacji na linii człowiek – maszyna. W sposób nowoczesny, komfortowy, ale jednocześnie pionierski przeszliśmy z zerojedynkowej narracji do „języka człowieka”. Choć nie zawsze precyzyjnie i składnie, to rzeczywiście – maszyna rozmawia z człowiekiem. Wygląda to jak pierwszy krok w drodze do tego czym zwykliśmy nazywać świadomością. Doskonale operuje liczbami, w przeciągu ułamka sekundy zna odpowiedzi na liczbowe problemy, tworzy skomplikowane modele cyfrowe, wszystko co deterministyczne i natury arytmetycznej jest w jej zasięgu. Ma natomiast problem z pojęciami, które ironicznie, człowiekowi przychodzą nieco łatwiej – pojęciami abstrakcyjnymi. Czy w milionach tranzystorów może popłynąć rewolucyjna idea, zupełnie abstrakcyjna myśl, zakodowana jako „kilka zer i jedynek” ? Wszystkie wyniki pracy systemów sztucznej inteligencji, a w konsekwencji jej osiągnięcia bazują na dużych ilościach danych którymi jest „karmiona” i kolejno trenowana. Choć ten ciąg przyczynowo-skutkowy wydaje się pozostawać niewzruszony to ludzki umysł, również wiedziony ogromną ilością danych, popularniej zwanych bagażem doświadczeń potrafi myśleć w sposób abstrakcyjny, definiować swój byt i być samoświadomy. Czy samoświadomość sztucznej inteligencji to kwestia większej ilości danych i nowych połączeń sieci neuronowej? Jeżeli tak, to hipotetycznie moglibyśmy wskazać konkretny punkt, w którym owa sieć osiąga świadomość. Czy jest to wykonalne - jest nam ciężko określić, chociaż sam temat świadomości cyfrowej stanowi nie tylko obiekt: badań specjalistów, westchnień futurologów, ale także.. Rozważań filozofów Czy prekursorzy inżynierii elektronicznej tworząc pierwsze układy scalone, mogli doszukiwać się głębszego metafizycznego sensu w sygnałach? Jest to wątpliwie, jednak przyszłość przygotowała własną wizję - mariaż teorii informacji z humanizmem. Dwie dziedziny należące do przeciwległych biegunów nauki, połączyła potrzeba współpracy na rzecz ustalenia wspólnych mianowników etycznych dla człowieka i maszyny. Choć z pozoru nierealna wizja potrzeby dzielenia się wykuwaną przez lata ludzką moralnością, z pieśni przyszłości staje się prozą współczesności. Autonomiczne auta wyposażane są w systemy decyzyjne, których zadaniem jest podjęcie odpowiedniego wyboru w sytuacji zagrożenia życia. Ale czy faktycznie potrafimy zdefiniować odpowiedni wybór? Czy nie jest tak, że już na przestrzeni dialogu międzyludzkiego napotykamy problem z jasnym zdefiniowaniem systemu norm, który byłby spójny? Subiektywność, rozumiana bliżej jako doświadczenia życiowe i system wyznawanych przez nas wartości powodują dywersyfikację wersji algorytmu prawidłowego działania. Czy w niemożliwym do uniknięcia przypadku potrącenia pieszego system postanowi potrącić dziecko czy osobę starszą, osobę schorowaną czy kobietę w ciąży? Odpowiedź nie jest prosta, o ile w ogóle takowa istnieje. Wizja Elon Musk – właściciel firmy Tesla, produkującej innowacyjne samochody elektryczne do projektowania systemów autonomicznej jazdy wśród specjalistów z dziedzin IoT, Data Science, programistów systemów embedded rekrutuje osoby odpowiedzialne za moralne uposażenie systemów decyzyjnych – ekspertów z dziedziny filozofii. To oni są odpowiedzialni za wszystkie potencjalne dylematy natury moralnej. Zatrudnienie filozofów, ale również humanistów ogólnie w sektorze AI (Artificial intelligence – ang. Sztuczna Inteligencja) będzie niewątpliwie rosło. Urzeczywistnia się coś, o czym wcześniej nawet nie myśleliśmy – cykl technologiczny zatacza koło, a my uczłowieczamy technologię. Nie chodzi tu o podniosłe zwroty i patetyczne doszukiwanie się archetypu stwórcy w programiście – technologia brnie do przodu, a wraz z nią rola nas – jej głównych odbiorców i współrozmówców ulega ewolucji.
  18. 4 punkty
    Kamera termowizyjna to urządzenie do niedawna spotykane wyłącznie na filmach lub pokazach naukowych. Sam pamiętam jakie zainteresowanie wzbudzał taki sprzęt na Pikniku Naukowym Polskiego Radia, każdy zapewne kojarzy też sceny pościgów i sylwetki przemytników filmowanych z policyjnych śmigłowców. Oczywiście mówimy tu o sprzęcie profesjonalnym który dalej pozostaje poza zasięgiem finansowym przeciętnego pasjonata elektronika. Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Sytuacja na rynku zmieniła się jednak drastycznie między innymi dzięki dwóm firmom Seek Thermal oraz Flir One, ich produkty chociaż parametrami nie dorównują rozwiązaniom wojskowym, pozwoliły wejść technologi termowizyjnej pod przysłowiowe strzechy amatorów. W tym artykule skupię się na wykorzystaniu oraz wyborze sprzętu do naszego domowego warsztatu. Jak to działa: Kamera termowizyjna posiada przetwornik bolometryczny zbudowany podobnie do innych przetworników obrazu z tą różnicą że zamiast półprzewodnikowych detektorów światła mamy tu termistory. Optyka tez nie może być ze szkła, stosowane są soczewki np. germanowe. Ważnym problemem jest odseparowanie przetwornika tak aby nie nagrzewał się od pozostałych komponentów. W profesjonalnym sprzęcie mamy sporo miejsca i można dodać system termostatowania chociażby z użyciem ogniw peltiera. W małej „zabaweczce” nie można sobie pozwolić na takie luksusy, sytuację ratuje tu zastosowanie w przetworniku specjalnych termistorów kompensacyjnych, dodatkowo mamy też mechaniczną przysłonę która cyklicznie zasłania przetwornik pozwalając pozyskać klatkę wzorcową. Rys.2 Przechwycony obraz RAW z kamery Seek Thermal (źródło :https://www.eevblog.com/forum/thermal-imaging/yet-another-cheap-thermal-imager-incoming/300/ ) W rozwiązaniu gdzie procesor i przetwornik są na jednym laminacie, oczywistym jest powstawanie gradientu temperatury który bez ciągłej kompensacji programowej znacząco zakłócał by pomiar. Charakterystyczne cykanie podczas pracy kamery to właśnie ta przesłona. Użytkownicy forum EEVblog w jednym z tematów przyjrzeli się budowie i działaniu kamery SeekThermal, można tam znaleźć wskazówki dotyczące komunikacji, obróbki obrazu i autorskie rozwiązania programowe. Jednym z problemów który został tam dostrzeżony to pojawiający się gradient na obrazie z którym kamera sobie nieradzi. Rys.3 Gradient zaobserwowany przez użytkowników forum EEVblog. (źródło https://www.eevblog.com/forum/thermal-imaging/yet-another-cheap-thermal-imager-incoming/375/ ) Co wybrać: Na rynku dostępna jest bogata oferta modelowa a ceny zaczynają się od 1300zł, oferowane produkty maja formę przystawki do telefonu (musi on obsługiwać tryb USB OTG), gotowego urządzenia z wyświetlaczem LCD lub wbudowanych do telefonu modułów ( firma CAT). Ja postaram się skupić na przystawkach gdyż są to moim zdaniem urządzenia optymalne. Telefony firmy CAT są reklamowane jako produkt dla profesjonalistów jednak ze względu na małe doświadczenie macierzystej firmy w branży radiokomunikacyjnej cierpią na problemy z zasięgiem, GPS i Bluetooth, wodoszczelność też pozostawia wiele do życzenia (opinia na podstawie kilku egzemplarzy używanych w firmie). Gotowe kamery z wbudowanym LCD nie są znacząco droższe od przystawek, jednak trudno spodziewać się wyświetlacza i procesora porównywalnego z tym w telefonie komórkowym. Flir One czy Seek Thermal? Obaj producenci różnie podeszli do zagadnienia – Flir One postawiło na dwa przetworniki – bolometryczny i CCD w jednej obudowie, oraz unikatową i opatentowaną technologię składania obrazu z nich obu. Wizualnie obraz wydaje się świetny, pełen detali i na pierwszy rzut oka lepszy, jednak wprowadzone przesunięcie nie pozwala jednoznacznie identyfikować drobnych elementów, więc podczas pracy jesteśmy zmuszeni zatykać jeden obiektyw palcem. Rozdzielczość właściwego przetwornika nie jest porażająca i jest to dość istotny mankament. Urządzenia ze stajni Flir One stanowią wyposażenie wspomnianych telefonów CAT i z taką wersją miałem do czynienia. Istotnym ograniczeniem tańszej wersji kamerki i starszego modelu telefonu jest zakres pomiaru temperatury – tutaj aplikacja zaczyna pokazywać przekroczenie powyżej 120*C. Rys.4 Zdjęcie poglądowe z kamery FLIR od lewej oba obiektywy, zatkany obiektyw CCD.(źródło fot własna) Produkt Seek Thermal to czysta termowizja – odpowiednio 206x156 i 320x240 w bogatszej wersji. Mamy tu też lepsze odświeżanie 9 i 15Hz, należy tu wspomnieć że produkty z wyższym odświeżaniem są objęte ograniczeniami eksportu przez rząd USA jako technologia militarna. Nawet najtańsza wersja zmierzy temperaturę do 300*C, co w elektronice nie jest wartością niespotykaną. Kamera nie posiada wbudowanego akumulatora i zasila się z telefonu. Aplikacja sterująca uruchamia się automatycznie po podpięciu do telefonu (testowałem na Androidzie). Posiadacze nowszych telefonów musza zaopatrzyć się w przejściówkę z USB C na micro USB. Rys.5 Grot lutownicy. (źródło fot własna) Nie uświadczymy tu za to algorytmu scalania obrazów ale rozdzielczość modelu podstawowego pozwala na zobaczenie nawet drobnych elementów SMD. Rys.6 Przykładowe obrazy płytek drukowanych podczas pracy. (źródło fot własna) No i małe zestawienie kamer - przystawek: * Zależnie od wersji. Kolory: Kamera termowizyjna daje obraz który nazwali byśmy czarno - białym, przetwornik nie rozpoznaje długości fali więc oprogramowanie możne jedynie "pokolorować" nasz obraz. Aplikacje są pod tym względem bogate i oferują wiele filtrów. Chociaż kolory ładnie wyglądają na zdjęciu, człowiek zdecydowanie ostrzej widzi w czerni i bieli. Nie bez przyczyny operatorzy kamer w studiach telewizyjnych pracują na wizjerach B/W, a kolorem zajmuje się realizatorzy przy konsoli. Co nam to daje: Po zakupie kamery, kiedy już zrobimy zdjęcie wszystkiemu dookoła, dostrzeżemy jak długo pozostają ślady termiczne naszych palców na różnych powierzchniach, zaczniemy się zastanawiać do czego to się przyda? No bo to jednak spory wydatek jak na kilka godzin zabawy. Pierwsze i podstawowe zadanie to próby zwarciowe – dzisiejsze urządzenia posiadają sporo rozbudowanych sekcji zasilania i gdy dochodzi do zwarcia, multimetr nie pozwala go zlokalizować. Metody takie jak sprężony gaz lub izopropanol naniesione na elementy pozwalają wprawdzie stwierdzić co się nagrzewa, jednak aby nie pogłębić uszkodzeń staramy się zmniejszać prąd do minimum. Kamera pozwala w ciągu sekundy odnaleźć uszkodzony element: Rys.7 Uszkodzony układ TTL w pakiecie.(źródło fot własna) Należy tylko pamiętać aby ograniczyć napięcie poniżej znamionowego dla danej sekcji, wiadomo że pod obciążeniem nasz zasilacz ograniczy napięcie ale gdy zwarcie odpuści (np. przepalimy jakiś bezpiecznik) reszta obwodu może zostać uszkodzona, szczególnie procesory zasilane napięciem poniżej 2V. Jeżeli po podaniu napięcia zaświeci się nam stabilizator lub tranzystor kluczujący – nie wymieniajmy go bez sprawdzenia czy nie zasila on kolejnego zwarcia. Tutaj możemy przenieść nasz punkt zasilania na wyjście stabilizatora pamiętając o napięciu. Nie każdy układ który nagrzewa się odmiennie jest uszkodzony – układy CMOS lub TTL wydzielają więcej ciepła gdy pracują z większą częstotliwością, jednak mając dwa identyczne moduły i porównując ich obrazy można dostrzec nieprawidłowości. Udało mi się tak namierzyć uszkodzony układ 82c55 gdzie nie pracowała część jednego portu. W podczerwieni pracują też różne czujniki i fotoelementy stosowane w naszych robotach, wiadomo że ich światło można zobaczyć zwykłą kamerą telefonu, jednak kamera termowizyjna pozwala nam podejrzeć transoptory, szczególnie te z żółtą lub białą obudową. Wiadomo można zmierzyć napięcie i określić czy transoptor świeci ale gdy mamy np. szynę wejściową 8 transoptorów, szybki podgląd stanu wejść może się przydać. Kolejny przykład elementu którego stan możemy podejrzeć to przekaźnik – jego cewka wytwarza ciepło, w taki sposób zdiagnozowałem trudny do namierzenia problem z przełącznikiem gwiazda trójkąt, układ sporadycznie zatrzymywał się i blokował proces, ze względu na brak schematu i scholastyczny charakter usterki diagnostyka była utrudniona, olśnienie przyszło gdy zobaczyłem załączony przekaźnik który był jednocześnie chłodny. Okazało się że styki ulegały sklejeniu, wiadomo można było mierzyć napięcie na cewce ale ze względu na liczne szyny zasilania dostęp był utrudniony. Rys.8 Przekaźniki w szafie sterowniczej - widoczne ciepło pochodzi od cewek. (źródło fot własna) Kolejnym przypadkiem już bardzie amatorskim był problem z instalacją elektryczna w wynajmowanym mieszkaniu. Okresowo dochodziło do zadziałania zabezpieczenia nad prądowego w obwodzie kuchni, usterka występowała sporadycznie, odłączenie odbiorników nie pomagało. Wizja kucia i zrywania płytek, oraz plątanina przedłużaczy zmusiła mnie do poszukiwania nietypowych rozwiązań. W miejsce bezpiecznika włączyłem toster 2kW i czekałem. Drugiego dnia testów lampka tostera się zapaliła i można było szukać, lekka poświata widoczna w termowizji zdradziła miejsce uszkodzenia a kucie ograniczyło się do jednej płytki. Spalony kabel został naprawiony. Nic tak nie napawa strachem jak wiercenie otworów w podłodze z ogrzewaniem podłogowym. Oczywiście ogrzewanie z rurek pex i 16 otworów do wywiercenia – tutaj też kamera sprawdziła się wyśmienicie. Rys.9 Linie oznaczone taśmą to namierzone rurki - o trafności metody świadczy efekt końcowy ;-). (źródło fot własna) Co dalej: Zastosowanie w robotyce amatorskiej, dronach i samochodowych systemach bezpieczeństwa to nieliczne tylko pomysły, do kamer dostępnych na rynku można znaleźć alternatywne oprogramowanie np. dla dobrze znanej Malinki. Nie stanowi problemu napisanie aplikacji monitorującej temperaturę ludzi w pomieszczeniach – taki temat na czasie. Sam kupiłem Seek Thermal z myślą o budowie przystawki do drona gdzie obraz z kamery termowizyjnej i kamery RPI nakładany programowo wysyłany był by do nadajnika, zainspirowany wspomnianymi scenami poszukiwań. Możliwości są tu niemalże nieograniczone.
  19. 4 punkty
    Hexapod w V-REP. Import i konfiguracja modelu. W niniejszym artykule opisany został proces importu i konfiguracji własnego modelu robota kroczącego do programu symulacyjnego V-REP. Virtual Robot Evaluation Platform (V-REP) jest środowiskiem programistycznym służącym do symulacji robotów. Jeśli dysponujesz modelami swojego robota, dobrałeś napędy i czujniki, ale masz wątpliwości czy wszystko zadziała jak należy to V-REP może znacznie ułatwić Ci pracę. Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Ze względu na mnogość rozwiązań i przykładów z zakresu robotów stacjonarnych, chciałbym skupić się na opisie przygotowania modelu robota kroczącego do symulacji. V-REP dostarcza środowisko edytorskie, w którym użytkownik może tworzyć własne modele robotów. Niestety dostarczany interfejs jest bardzo nieefektywny. Prostszym i mniej czasochłonnym rozwiązaniem jest import gotowego modelu z innego programu edytorskiego. Możemy dodawać obiekty zapisane w plikach *.dxf, *.obj, *.stl oraz *.3ds. Interfejs graficzny programu V-REP Import modelu z innego edytora 3D Modele, które posłużyły w tym przykładzie utworzono w programie AutodeskInventor 2020. Program ten umożliwia łatwy eksport części czy całego złożenia do dowolnego formatu obsługiwanego przez V-REP za wyjątkiem formatu *.3ds. W celu importu modelu (najlepiej importować złożenie) korzystamy z paska menu -> File -> Import -> Mesh… Model robota w programie Inventor (po lewej) oraz V-REP (po prawej) Po imporcie złożenia do symulatora łatwo zauważyć, że model stanowi jedną bryłę zamiast oddzielnych elementów robota. Należy taką bryłę odpowiednio pogrupować (klikamy pasek menu -> Edit -> Grouping/Merging -> Divide selected shapes), pamiętając o wcześniejszym zaznaczeniu naszego modelu. W wyniku takiej operacji pojawia się znacznie więcej elementów niż jest to pożądane. Dlatego też należy połączyć z powrotem części, które mają stanowić zespoły nieprzemieszczające się względem siebie (Edit -> Grouping/Merging -> Merge selected shapes). Przed przystąpieniem do grupowania, warto usunąć części, które powtarzają się wielokrotnie, w tym przypadku są to nogi robota. Do konfiguracji działania modelu w symulatorze wystarczy nam tylko jedna lub dwie nogi (w moim przypadku zostawiłem dwie nogi ponieważ miałem wykonane dwa typy nóg, jeśli się przyjrzycie, dostrzeżecie różnicę ). Model podzielony na poszczególne elementy oraz usunięty nadmiar części Tak przygotowany model gotowy jest do powielenia. Kopiujemy model do nowej sceny w celu uproszczenia go i nadania jego elementom właściwości obiektów dynamicznych. W celu uproszczenia części w tym przykładzie wykorzystano konwersję zaimportowanego modelu do postaci prostych kształtów (Edit -> Morph selection into convex shapes). Kształty takie (bryły) reprezentowane są przez trójkąty opisujące ich powierzchnię (MES). Liczba elementów skończonych rzutuje na złożoność obliczeń, dlatego należy starać się uzyskać jak najmniejszą ich liczbę przy zachowaniu zarysu kształtów oryginalnych części. Warstwa modeli dynamicznych konstrukcji jest niewidoczna podczas symulacji. Model dynamiczny części (po prawej) – przełączanie pomiędzy warstwami Przeguby Aby umożliwić ruch odpowiednim częściom robota, wprowadzamy do układu przeguby. W tym celu należy wybieramy z paska menu -> Add -> Joint -> Revolute. Następnie, po zaznaczeniu przegubu, korzystając z narzędzi Position oraz Orientation ustawiamy przegub w pożądanym miejscu (rysunek powyżej, na modelu po lewej widoczne są pomarańczowe przeguby). Operację tą wykonujemy dla kolejnych przegubów nogi. Dzięki usunięciu „nadmiarowych” nóg oszczędzamy mnóstwo czasu w tym momencie – tworzymy jedynie 3 lub 6 przegubów zamiast wszystkich 18. Elementy te symulują działanie napędów robota, dlatego istotne jest ustawienie odpowiedniego momentu obrotowego. Można to uzyskać korzystając z obliczeń lub doświadczalnie – wszystko zależy od przeznaczenia naszego modelu symulacyjnego. Każdemu z przegubów można zaimplementować regulator: Parametr proporcjonalny dostarcza część sygnału sterującego proporcjonalną do błędu pozycji. Im większy jest błąd, tym większy jest sygnał sterujący, wymuszający korekcję położenia. Człon całkujący rośnie z czasem i jest proporcjonalny do sumy błędów. Człon ten minimalizuje (tłumi) błąd jaki powstaje w wyniku stałego obciążenia i powoduje dojście do pozycji zadanej. Ostatni parametr – różniczkujący jest tym większy im większa jest różnica błędu w kolejnych próbkach. Jest więc odpowiedzialny za tłumienie zmian błędu (tłumi oscylacje). Parametry dynamiczne przegubu - napędu Łańcuch kinematyczny Po odpowiednim pogrupowaniu części, stworzeniu ich reprezentacji dynamicznej oraz dodaniu przegubów, musimy utworzyć łańcuch kinematyczny. Dodajemy dwa obiekty typu Dummy – jeden nazwany foot_Tip, drugi foot_Target. Tip jest swego rodzaju TCP (ang. Tool Central Point) nogi podczas Target będzie odpowiadać za docelowe położenia TCP podczas ruchu robota. Stworzono zatem połączenie pomiędzy tymi dwoma obiektami oraz zbudowano drzewo dziedziczenia pomiędzy obiektami reprezentującymi elementy robota. Model ze zdefiniowanym TCP oraz punktem docelowym Następnie dodano nową grupę kinematyczną w celu powiązania punktów ze sobą. Korzystając z okna parametrów kinematyki, dodano element TIP jako końcówkę łańcucha. Od tego momentu w chwili przesunięcia elementu TARGET, element TIP będzie za nim podążać. Na tym etapie konfiguracji modelu można sprawdzić jej poprawność, wyłączając dynamikę w oknie właściwości modułu obliczeniowego. Następnie należy włączyć symulację oraz wybrać i przesunąć obiekt TARGET. Jeśli wszystkie kroki zostały wykonane poprawnie, noga powinna podążać za celem. Definiowanie łańcucha kinematycznego Po wykonaniu powyższych instrukcji powielono przygotowany model nogi. Skopiowane nogi umieszczono w odpowiednich miejscach. Tak przygotowany model jest gotowy do wprawienia w ruch za pomocą skryptów napisanych w języku LUA. Model gotowy do implementacji kodu sterującego Skrypty W systemie V-REP każdy skrypt jest identyfikowany z obiektem np. robotem. Wyjątkiem jest skrypt odpowiadający za symulację otoczenia. Takie zastosowanie umożliwia użytkownikowi pełną elastyczność w tworzeniu modeli, korzystania z odczytów sensorów, sterowania przegubami. W efekcie daje to możliwość budowania systemów sterowania i testowania różnych algorytmów sterowania. Przebieg sterowania pomiędzy skryptami w V-REP Skrypt główny (MainScript) uruchamia kolejne skrypty potomne utożsamione z naszymi modelami (ChildScript), które są wykonywane w trybie wątkowym lub bezwątkowym. W trybie wątkowym skrypty te pracują przez cały czas w tle, natomiast bezwątkowe są wywoływane w każdym kroku symulacji, podejmują pewne akcje i kończą swoje działanie. Najszybszym sposobem, na sprawdzenie poprawności wykonania modelu w V-REP jest wykorzystanie skryptów z przykładów dostępnych w programie, np. „hexapod” . Należy jednak zaznaczyć, że skrypty te będą działały jedynie dla hexapodów posiadające trzy stopnie swobody na każdą nogę (skrypty dodałem również w załączniku). Dostępne skrypty obsługują ruch nóg robota względem środka ciężkości oraz ruch modelu w przestrzeni symulatora. Napisane są w języku Lua, który jest opisany między innymi na stronie producenta programu V-REP. Obydwa skrypty wykorzystywane w symulatorze przypisane są do konkretnego elementu (części) robota. W przypadku sterowania nogami jest on przypisany do wizualnej części odpowiadającej za korpus robota. Takie rozwiązanie pozwala na zapewnienie sterowania nogami bez konieczności tworzenia oddzielnych skryptów na każdą nogę z osobna. Należy zaznaczyć również, że sterowanie nogami robota polega na wyznaczeniu kolejnych położeń celu, do którego dana noga ma podążać (hexa_footTarget..i). Konfiguracja modelu w edytorze graficznym pozwala na rozwiązanie odwrotnego zadania kinematyki przez symulator. Układem odniesienia dla trajektorii opisanych w skrypcie jest układ współrzędnych części, do którego ten skrypt jest przypisany. Podczas inicjacji programu/symulacji skrypt odwołuje się do obiektów footTarget, a następnie tworzy listy celów oraz końcówek nóg robota. robotBase=sim.getObjectHandle('hexa_legBase') legTips[i]=sim.getObjectHandle('hexa_footTip'..i-1) legTargets[i]=sim.getObjectHandle('hexa_footTarget'..i-1) Na inicjację składa się również wyzerowanie pozycji bazowej oraz pobranie współrzędnych końcówek nóg. W celu zapewnienia odpowiedniej kolejności przenoszenia nóg podczas danego typu chodu zadeklarowano tablicę indeksów kolejnych nóg robota. Indeks ten jest wykorzystywany w funkcji określającej przesunięcie nóg względem początku układu odniesienia. W funkcji inicjacyjnej ustawiane są też domyślne wartości parametrów chodu takie jak prędkość kroku, wysokość kroku, kierunek ruchu itp.. legMovementIndex={1,4,2,6,3,5} stepProgression=0 stepVelocity=0.5 stepAmplitude=0.16 stepHeight=0.04 movementStrength=1 realMovementStrength=0 movementDirection=0*math.pi/180 rotation=0 Funkcja sysCall_init wykonywana jest tylko raz (podczas pierwszego wywołania skryptu) i odpowiada za przygotowanie symulacji. W tym przypadku wykorzystana jest do nadania parametrów bazowych modelom. Funkcja functionsysCall_actuation() jest wykonywana w każdym momencie działania symulacji podczas fazy sterowania, z częstotliwością ustawioną w programie. Kod zawarty w tej funkcji odpowiada za obsługę wszystkich funkcji sterujących, które zapewnia symulator (kinematyka odwrotna, dynamika itp.). Tutaj znajdują się odwołania do wartości parametrów chodu w czasie rzeczywistym. Zawarta w funkcji functionsysCall_actuation() pętla for odpowiada za ustawienie działania nóg robota w zależności od jej indeksu. Nadając odpowiednie indeksy nogom, zrealizowano ruch trójpodporowy. Ponadto w pętli tej zawarte są obliczenia wyznaczające położenie kolejnych końcówek nóg robota w każdym momencie jego ruchu. Trajektorie wyznaczane są na płaszczyźnie i realizowane są poprzez skręcenie kolejnych przegubów nogi. Zmienna md (ang. movement direction) zawiera informację o aktualnym kierunku ruchu robota, a za pomocą tablicy p przekazywana jest informacja o położeniu punktu docelowego w kolejnych chwilach symulacji. for leg=1,6,1 do sp=(stepProgression+(legMovementIndex[leg]-1)/6) % 1 offset={0,0,0} if (sp<(1/3)) then offset[1]=sp*3*stepAmplitude/2 else if (sp<(1/3+1/6)) then s=sp-1/3 offset[1]=stepAmplitude/2-stepAmplitude*s*6/2 offset[3]=s*6*stepHeight else if (sp<(2/3)) then s=sp-1/3-1/6 offset[1]=-stepAmplitude*s*6/2 offset[3]=(1-s*6)*stepHeight else s=sp-2/3 offset[1]=-stepAmplitude*(1-s*3)/2 end end end md=movementDirection+math.abs(rotation)*math.atan2(initialPos[leg][1]*rotation,-initialPos[leg][2]*rotation) offset2={offset[1]*math.cos(md)*realMovementStrength,offset[1]*math.sin(md)*realMovementStrength,offset[3]*realMovementStrength} p={initialPos[leg][1]+offset2[1],initialPos[leg][2]+offset2[2],initialPos[leg][3]+offset2[3]} sim.setObjectPosition(legTargets[leg],antBase,p) end Skrypt sterujący całym robotem zawiera funkcję odpowiadającą za przekazywanie wartości parametrów takich jak: prędkość chodu, wysokość kroku czy kierunek ruchu. Dodatkowo wykorzystano funkcję moveBody, w której zdefiniowano sekwencje ruchów. W funkcji głównej wywoływana jest ona z odpowiednim indeksem aby uruchomić sekwencję o tym samym indeksie. Do wywoływania danych funkcji służy funkcja sysCall_threadmain(). Część kodu w niej zawarta wykonywana jest w momencie rozpoczęcia danego wątku, aż do jego zakończenia. Wątek może rozpocząć się wraz ze startem symulacji, ale także w trakcie jej działania. Tutaj też pobierane są informacje z interfejsu graficznego o położeniu elementów bazowych układu, określana jest pozycja startowa robota oraz nadane zostają wartości zmiennych. Podsumowanie Powyższy tekst stanowi ogólny opis czynności, które należy wykonać w celu wykonania prostej symulacji w programie V-REP. Na proces ten składa się: Import modelu z edytora 3D oraz przygotowanie poszczególnych członów robota Wykonanie modelu dynamicznego robota wraz z dodaniem przegubów Zdefiniowanie łańcucha kinematyki Implementacja skryptów w celu weryfikacji modelu Tekst ten powstał ze względu na małą popularność opisywanego symulatora oraz jeszcze mniejszą dostępność kompletnych opracowań dotyczących robotów kroczących. Pisząc ten tekst miałem nadzieję na w miarę przystępne nakreślenie procesu modelowania robota w symulatorze, jednak w przypadku chęci praktycznego wykorzystania tych informacji należałoby się zaznajomić z podstawami obsługi V-REP. Dlatego też bardzo docenię każdą sugestię co przydałoby się opisać dokładniej lub gdzie przydałoby się zaprezentować różne podejścia (np. dobór nastaw regulatorów). Dla inspiracji dodam, że na podstawie testów w V-REP udało mi się opracować konstrukcję hexapoda (mam nadzieję że jest choć trochę podobny do tego z artykułu): skrypty.rar
  20. 4 punkty
    statnio zrobiliśmy sobie smartdom z kilkoma czujnikami i obsługą przez przeglądarkę. Dzisiaj zajmiemy się bardziej frontendem, aby strona była bardziej przyjazna użytkownikom, oraz dodamy małego szpiega, który będzie nam przekazywał temperaturę do dużego ESP. Spis treści serii artykułów: 1. Omówienie, i przygotowanie środowiska 2. Zapoznanie z nowym środowiskiem, praca jako Arduino, prosty serwer WWW 3. Przyspieszony kurs na webmastera 4. Wykresy, zapis do SPIFFS, mini smart-dom 5. Odbiór danych z przeglądarki, stałe IP, łączenie modułów ESP Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Przekazywanie danych do ESP Teraz zadanie mamy takie, żeby stworzyć pole tekstowe i koło do wyboru kolorów. Tekst z textboxa wyświetlimy na LCD, a na wybrany kolor zaświecimy WS2812. Zacznijmy od tego prostszego, czyli tzw. textboxa (text field, pole tekstowe). W HTMLu mamy na to gotowy znacznik: <input type="textbox" value="koronaferie 2020" id="text"></input> Znacznik <input> to wejście ze strony użytkownika. Możemy także mieć wejście typu checkbox, radius, itd. Textbox to nasze pole tekstowe, a "value" to domyślny tekst naszego textboxa. No dobra, to było dość proste. Dane będziemy wysyłać do ESP dzięki metodzie get. Polega ona na wysyłaniu argumentów przy wywołaniu podstrony - u nas /set. Najlepiej będzie zrobić zmienną z całą stroną, i dopiero potem ją wysłać: var set = '/set?' + 'txt=' + $('#text').val(); $.post(set); Po wpisaniu "ip/set" dajemy znak zapytania - czyli wysyłamy już argumenty. Argumenty wysyłamy w formie "nazwa=dane". Tutaj będzie to "txt=", a dane to odczytanie wartości z textboxa o id #text. Kolejne argumenty zaraz dodamy - będziemy je oddzielać znakiem "&". Po przygotowaniu zmiennej wysyłamy żądanie. Najlepiej to umieścić w setInterval. Zostało nam zrobić kolejną podstronę. server.on("/set", setParams); void setParams() { String text = server.arg("txt"); lcd.print(text); server.send(200, "text/html", ""); //Send web page } Tworzymy zmienną "text" w której przechowujemy argument dla "txt". Wysyłamy jak zwykle kod 200, strony nie trzeba wysyłać - więc pozostawiamy pustą stronę. Spróbuj teraz załadować kod, i zmienić tekst w textboxie. Powinien się zmieniać także na wyświetlaczu, z niewielkim opóźnieniem. Czas na odrobinę magii Dodanie kolejnych argumentów nie będzie problemem. Dlatego to sobie zostawimy na później, a teraz zajmiemy się problemem "skąd wziąć argumenty". Dodamy sobie plugin "Spectrum" - dzięki niemu, będziemy mogli w łatwy i przyjemny sposób wybrać kolor naszych ledów. Szczegóły znajdziecie tutaj. Dodajemy zatem plik css na górze i js na dole strony: <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/spectrum-colorpicker2@2.0.0/dist/spectrum.min.css"> <script src="https://cdn.jsdelivr.net/npm/spectrum-colorpicker2@2.0.0/dist/spectrum.min.js"></script> W lewej kolumnie dodajemy tekst "LED:" oraz znacznik input - tam będziemy wprowadzać kolory. Argument "value" to początkowy kolor - podany heksadecymalnie. <p>LED: <input id="kolorki" value='#276cb8'/></p> W kodowaniu na razie tyle co nic. Dodajemy jedynie informację (w document.ready, nie w setInterval), jaki ma być typ naszego wybieracza kolorów. $('#kolorki').spectrum({ type: "color" }); Po otworzeniu i wgraniu strony, ujrzymy takie cudo: Ale to dopiero połowa magii! Druga połowa będzie fizyczna. Wyjście naszego pola z kolorkami to tekst, dosłownie: rgba(r,g,b,a) Dlatego musimy sobie zrobić funkcję, która nam to rozbije na poszczególne komponenty. Kanał alpha będzie sterował jasnością WS. function spliceRGB(input) { var color = input.slice(input.indexOf('(') + 1, input.indexOf(')')); // "r, g, b, a" var colors = color.split(','); return { r: colors[0], g: colors[1], b: colors[2], a: colors[3] } } Jest to dość prosta funkcja. Na początku usuwamy "rgba(" oraz ")", potem rozbijamy tekst na kawałki oddzielone ",". Zwracamy 4 wartości: r, g, b i a. var r = spliceRGB($("#kolorki").val()).r; var g = spliceRGB($("#kolorki").val()).g; var b = spliceRGB($("#kolorki").val()).b; var i = (spliceRGB($("#kolorki").val()).a * 255); Tak będą wyglądały zmienne, które przekażemy ESP. Do funkcji podajemy wartość którą wybraliśmy, i dajemy zmiennej tylko dany kolor. Z racji tego, że kanał alpha ma wartości od 0 do 1, mnożymy go x255 aby uzyskać liczbę przyjazną ESP. var set = '/set?txt=' + txt + '&r=' + r + '&g=' + g + '&b=' + b + '&i=' + i; $.post(set); Tworzymy dość długą zmienną, która po kolei zawiera wszystkie te wartości. Na końcu najzwyczajniej je wysyłamy. Jako zadanie dodatkowe spróbuj zrobić przycisk do włączania i wyłączania wbudowanego LEDa, ale już nie przez podstrony a get. void setParams() { int r = server.arg("r").toInt(); int g = server.arg("g").toInt(); int b = server.arg("b").toInt(); int i = server.arg("i").toInt(); String text = server.arg("txt"); cls(); lcd.print(text); for(int index=0; index<3; index++) { leds.setPixelColor(index, r, g, b); leds.setBrightness(i); leds.show(); } server.send(200, "text/html", ""); } Tak wygląda cała funkcja odczytująca dane. Odzyskujemy wartości kolorów, oraz tekst. Piszemy tekst na wyświetlaczu, po czym dla każdej diody ustawiamy wybrane kolory i jasność. Na końcu informujemy, że wszystko poszło sprawnie. Spróbuj teraz to wszystko odpalić na telefonie, i wygodnie z łóżka pobawić się LEDami - bardzo fajna zabawa, polecam :3 Suwak alpha nie dajemy na max. wartość - inaczej wyjście dostaniemy w postaci heksadecymalnej, z której nie wyciągniemy kolorów tak łatwo. Ustaw alpha na mniejszą od 1! ESP01 Na początku napisałem, że układy ESP01 są fajne dla dedykowanych rozwiązań. Zrobimy sobie w takim razie małego ESP odczytującego temperaturę z drugiego pokoju, przekazujące dane do "dużego" ESP. W związku z tym, "usuwamy" wykres z DS: void handleData() { //ds.requestTemperatures(); //String strona = (String)ds.getTempCByIndex(0); String strona = "0"; strona += "\n"; strona += analogRead(PIN_FRES); Temperatura 1 z "wyłączonym" DS18B20. Żeby zaprogramować teraz małe ESP, proponuję zrobić sobie do tego programator ze schematu. Tak wygląda mój: Dzięki niemu nie trzeba kombinować z kablami - wystarczy wsadzić ESP i wrzucić program. Jeżeli mowa o programie, warto sobie powiedzieć znowu, co chcemy napisać. Najlepiej będzie, jeżeli małe ESP w ten sam sposób co przeglądarka, wyśle nam temperaturę. Robimy nowy projekcik: Otwieramy plik main.cpp. Zacznijmy od dołączenia podstawowych bibliotek: #include <Arduino.h> #include <OneWire.h> #include <DallasTemperature.h> #include <ESP8266WiFi.h> #include <ESP8266WebServer.h> Z racji tego, że poprzedni projekt był konwertowany z projektu Arduino, biblioteki tym razem zainstalowałem przez platformio: Następnie definiujemy piny. Przyjmijmy, że zrobimy kilka małych ESP i wsadzimy je do gniazdek - każde będzie miało czujnik i przekaźnik (tutaj LED). #define PIN_LED 2 #define PIN_DS 0 Następnie tworzymy obiekty: klienta, DS, serwera. Stworzyliśmy także 4 adresy: IP, bramy, maski, i DNS. Dzięki nich będziemy mogli ustawić stałe IP naszemu ESP - w przeciwnym razie, jeżeli się zmieni, będziemy musieli zmieniać program i przepisywać wszystko. Pierwszy adres ustalamy sami - najlepiej zmienić po prostu ostatnią liczbę. DNSy zostawiamy, ponieważ z nich nie korzystamy - ale bramę i maskę możemy uzyskać z konsoli windowsowej CMD: Default gateway to nasza brama, którą przepisujemy. Podobnie z maską - prawdopodobnie będziesz miał taką samą. WiFiClient client; OneWire ow(PIN_DS); DallasTemperature ds(&ow); ESP8266WebServer server(80); IPAddress staleIP(192, 168, 43, 90); IPAddress gateway(192, 168, 43, 1); IPAddress subnet(255, 255, 255, 0); IPAddress dns(8, 8, 8, 8); Pamiętajmy o SSID i haśle naszego wifi - podaj tam wartości Twojego Wifi. const char* ssid = ""; //nazwa const char* pass = ""; //hasło Teraz zrobimy funkcję setup. Nic tajemniczego chyba tu nie ma - IP będzie takie, jakie ustawiliśmy. Obsługujemy potem dwie strony: główną i /set. void setup() { pinMode(PIN_LED, OUTPUT); Serial.begin(115200); Serial.print("Lacze z wifi"); ds.begin(); WiFi.begin(ssid, pass); WiFi.config(staticIP, gateway, subnet, dns); while(WiFi.status() != WL_CONNECTED) { Serial.print('.'); delay(400); } delay(500); Serial.println("Polaczono! IP:"); Serial.println(client.localIP()); server.on("/set", setParam); server.on("/", mainPage); server.begin(); } W loopie nic nie musimy robić poza obsługą serwera. void loop() { server.handleClient(); } Na stronie głównej będziemy wysyłać dane w tej samej formie co "duże" ESP. Dzięki temu bez problemu będziemy mogli pobrać temperaturę. Dodaliśmy także nagłówek "Access control allow origin", który pozwoli nam na łączenie się dużemu ESP. void mainPage() { ds.requestTemperatures(); server.sendHeader("Access-Control-Allow-Origin", "*"); server.send(200, "text/html", (String)ds.getTempCByIndex(0)); } Z kolei funkcja obsługująca LEDa zawiera prostego ifa - jeżeli wysłaliśmy "led=on" to włączy, "led=off" to wyłączy, a cokolwiek innego ominie. void setParam() { String led = server.arg("led"); if(led == "on") digitalWrite(PIN_LED, LOW); if(led == "off") digitalWrite(PIN_LED, HIGH); server.sendHeader("Access-Control-Allow-Origin", "*"); server.send(200, "text/html", ""); } Cały kod zatem będzie wyglądał tak: #include <Arduino.h> #include <OneWire.h> #include <DallasTemperature.h> #include <ESP8266WiFi.h> #include <ESP8266WebServer.h> #define PIN_LED 2 #define PIN_DS 0 WiFiClient client; OneWire ow(PIN_DS); DallasTemperature ds(&ow); ESP8266WebServer server(80); IPAddress staleIP(192, 168, 43, 90); IPAddress gateway(192, 168, 43, 1); IPAddress subnet(255, 255, 255, 0); IPAddress dns(8, 8, 8, 8); const char* ssid = "dobre pomaranczowe"; //nazwa const char* pass = "smerfysmerfy"; //hasło void setParam() { String led = server.arg("led"); if(led == "on") digitalWrite(PIN_LED, LOW); if(led == "off") digitalWrite(PIN_LED, HIGH); server.sendHeader("Access-Control-Allow-Origin", "*"); server.send(200, "text/html", ""); } void mainPage() { ds.requestTemperatures(); server.sendHeader("Access-Control-Allow-Origin", "*"); server.send(200, "text/html", (String)ds.getTempCByIndex(0)); } void setup() { pinMode(PIN_LED, OUTPUT); Serial.begin(115200); Serial.print("Lacze z wifi"); ds.begin(); WiFi.begin(ssid, pass); while(WiFi.status() != WL_CONNECTED) { Serial.print('.'); delay(400); } delay(500); Serial.println("Polaczono! IP:"); Serial.println(client.localIP()); server.on("/set", setParam); server.on("/", mainPage); server.begin(); } void loop() { server.handleClient(); } Wgrywamy kod do "małego" esp, podłączamy wszystko, i uruchamiamy Serial Monitor. Niestety, moje ESP się zbuntowało: Na to wystarczy pobrać "Advanced IP Scanner", który był wspomniany w malinkowym kursie. Uzyskany adres IP wpisujemy w przeglądarkę: No, to teraz małe ESP ukrywamy w innym pokoju. Pamiętajmy jedynie, żeby dalej miało zasięg naszego routera. Przechodzimy do kodu dużego ESP: var temp = new XMLHttpRequest(); temp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var temperatura = this.responseText; document.getElementById('temp1').innerHTML = temperatura; addData(wykres, temperatura, 0); } }; temp.open("GET", "http://192.168.43.75", true); temp.send(); Obok całego kodu dla "strony" dodajemy praktycznie taki sam kod dla "temp". Pamiętaj, żeby w "temp.open()" wpisać IP małego ESP. Zostało nam z "dużej" funkcji usunąć stary kod (tutaj macie całą funkcję): if (this.readyState == 4 && this.status == 200) { var zawartosc = this.responseText; var dane = zawartosc.split("\n"); var temp1 = dane[0]; var fres = dane[1] / 4; var temp2 = dane[2]; var cis = dane[3] / 100; var odl = dane[4]; var btn = dane[5]; var color = 'rgba('+fres +','+(fres - 20)+','+(fres - 20)+','+'1)'; var r = spliceRGB($("#kolorki").val()).r; var g = spliceRGB($("#kolorki").val()).g; var b = spliceRGB($("#kolorki").val()).b; var i = (spliceRGB($("#kolorki").val()).a * 255); var txt = $('#text').val(); var set = '/set?txt=' + txt + '&r=' + r + '&g=' + g + '&b=' + b + '&i=' + i; document.getElementById('temp1').innerHTML = temp1; document.getElementById('temp2').innerHTML = temp2; document.getElementById('light').style.color = color; document.getElementById('cis').innerHTML = cis; document.getElementById('odl').innerHTML = odl; $.post(set); if(!btn) document.getElementById('drzwi').innerHTML = 'otwarte'; else document.getElementById('drzwi').innerHTML = 'zamknięte'; addLabel(wykres, etykieta); addData(wykres, temp2, 1); popData(wykres, 0); } }; Wgrywamy kod. Uruchamiamy oba ESP, patrzymy czy IP się nie zmieniło (jeżeli jednak nie dałeś stałego IP), i wszystko powinno działać. Jeżeli chodzi o kurs ESP - to chyba tyle. Myślę, że dzięki temu kursowi teraz będziesz mógł postawić swój własny smartdom (lub jakiekolwiek inne urządzenie IoT). Nauczyliśmy się stawiać dynamiczne i responsywne strony WWW, odczytywać i zapisywać dane zarówno przez przeglądarkę, i FS, i ogólnie udało nam się z ESP zrobić "arduino". Możemy nawet zrobić całą sieć ESP01, gdzie każde ESP może sterować chociażby przekaźnikiem - i komunikować się do dużego ESP, lub malinki. Tak wygląda mój setup: W razie jakichkolwiek pytań - śmiało pisz na dole. Zostawiam Ci jeszcze zipa z projektami do platformio, i bibliotekami - w razie gdybyś miał problemy z posklejaniem kodów w całość. Pamiętajcie - bądźcie kreatywni, i pochwalcie się poniżej swoimi projektami! kurs esp.zip Spis treści serii artykułów: 1. Omówienie, i przygotowanie środowiska 2. Zapoznanie z nowym środowiskiem, praca jako Arduino, prosty serwer WWW 3. Przyspieszony kurs na webmastera 4. Wykresy, zapis do SPIFFS, mini smart-dom 5. Odbiór danych z przeglądarki, stałe IP, łączenie modułów ESP
  21. 4 punkty
    Przyznaję, że korespondowaliśmy z kolegą MKJB jakiś czas temu, ale pomysł - mimo entuzjazmu Autora - moim zdaniem nie rokuje. Pytany o najprostsze rozwiązania umożliwiające tanim kosztem odpalenie modelu urządzenia (czyli driver + cewka + magnes) wspomniałem o gotowych wzmacniaczach audio i modułach generatorów, co jak się okazuje, zostało zrealizowane. Niemniej jednak takie projekty "działają" dopóki nie muszą niczego rzeczywistego zrobić, coś jak prawo uchwalone w jedną noc. Jeżeli porównamy wahliwą płetwę baz żadnej szansy na uszczelnienie przepływów na krawędziach (silnik Wankla - pamiętacie?) z koniecznym do uzyskania ciśnieniem skurczowym w lewej komorze serca (jakieś 16 kPa o ile pamiętam) a do tego dodamy zwykłą fizykę czyli konieczność przepchnięcia (w wysiłku) ponad 20 l/min krwi pod tym ciśnieniem (policzcie pracę jaką trzeba wykonać czyli ile energii dostarczyć w minutę a ile np. w miesiąc) to wyjdzie, że przy sprawności 10% (a na tyle szacuję tę zabawkę) zasilanie tego czegoś będzie musiało mieć wielkość taczki. Nie mówiąc o podgrzewaniu krwi (pochodna sprawności - gdzieś zmarnowana energia musi się podziać), zjawiskach na krawędzi płetwy (kawitacja płynu - bąbelki we krwi, zgroza oraz odśrodkowa separacja osocza od krwinek i płytek w potężnych wirach - żona pracuje w laboratorium krwi, konsultowałem się) i nieznanym wpływie szybkich zmian ciśnienia (dziesiątki Hz?) na tętnice i mózg związanych z drganiami płetwy. Krew (nie woda - jak wiadomo) jest bardzo specyficzną mieszaniną płynu i mikroskopijnych "ciał stałych" a pompy do niej powinny mieć jak najmniej krawędzi i oferować jak najbardziej łagodny, laminarny wręcz przepływ co stoi w jawnej sprzeczności z zasadą działania płetwy (a także śruby znanej ze statków - dlatego sztuczne serca są cyklicznymi pompami z powolnymi tłokami i zaworami). Do tego dochodzi niejako "wbudowany" w sztuczne serce problem pogodzenia szczelności (ciśnienie, sprawność) ze zjawiskiem niszczenia (miażdżenia) krwinek na przesuwających się względem siebie sztywnych powierzchniach (łożyska, zawory, tłoki) a więc dobór materiałów możliwie blisko przypominających ściany prawdziwego serca. Jako że nie mam pojęcia o mechanice płynów a równania Naviera-Stokesa widziałem tylko w książkach oraz że nie wiem skąd wziąć kilkaset milionów dolarów na prace badawcze w zakresie technologii materiałów i optymalizację projektu (którego prototyp spełniałby choć fizyczne, zapominając o biotechnologicznych, wymagania) uznałem, że moja obecność w tym projekcie nie ma sensu. Także proszę mnie nie wiązać z tym tematem. W każdym razie życzę miłych wrażeń w dyskusji i komentowaniu tematu składającego się, nomen-omen, głównie z wody. BTW: I nie, to nie jest przypadek niedocenionego megaodkrywcy, którego wielki (wciąż nieopatentowany??? dziwne) pomysł za chwilę zostanie kupiony przez ktegoś z gigantów rynku medycznego, przykro mi.
  22. 4 punkty
    To ja podam argument żeby NIE budować czegoś takiego samemu. Pierwszą drukarkę 3d składałem sam - działała bardzo marnie, narobiłem się przy niej mnóstwo, ale właściwie zanim zdobyłem doświadczenie należałoby zacząć od początku. Drugą kupiłem gotową, składaną przez kogoś - było dużo lepiej, ale nadal daleko do ideału, a im dłużej jej używałem tym gorzej działała. Trzecią kupiłem gotową, tanią chińską - kosztowała praktycznie tyle co pierwsza, a jakbym doliczył części dokupione później do mojego składaka, to zdecydowanie mniej. Natomiast jakość wydruków gotowej drukarki była o wiele lepsza od obu pierwszych razem wziętych. Nie wiem jak to się ma do CNC, czy laserów, ale seryjnie produkowane urządzenie po prostu działa lepiej niż składane samemu - albo raczej działa lepiej niż kilka pierwszych składaków, przy których składający dopiero się uczy... Natomiast jeśli policzyłbym czas spędzony na takim składaniu, to taniej wyjdzie nie tylko gotowa chińska, ale i porządna, firmowa drukarka. Z laserami dochodzi jeszcze kwestia bezpieczeństwa - jak to ładnie kiedyś na laborkach było: na laser można popatrzeć dwa razy w życiu, raz lewym okiem, a raz prawym.
  23. 3 punkty
    STM32MP157 nie posiada pamięci flash, to nie jest mikrokontroler jak typowe STM32. Program dla rdzenia Cortex-M musi być załadowany do pamięci RAM i z niej wykonywany. Podczas debugowania można załadować program używając JTAG-a, natomiast w "produkcyjnej" wersji kod jest wczytywany z karty SD lub innej pamięci przez skrypt systemu Linux, albo bootloader.
  24. 3 punkty
    Wielkie gratulacje dla zwycięzców, A także podziękowania dla wszystkich którym się chciało. Wspaniały wysyp przeciekawych tekstów, na czasem nie oczywiste tematy. Dziękuję. No i wielka prośba do adminów by zbyt długo nie czekali z następnym pomysłem na rozruszanie bloga:)
  25. 3 punkty
    A ja bym proponował mocno zastanowić się nad wiarygodnością "zlecenia" w tym temacie. Po pierwsze, ktoś sobie zadał sporo trudu, żeby napisać prawie dwie strony "wymagań". Wygląda to profesjonalnie, wreszcie jakieś poważnie wyglądające ogłoszenie - ale autor się nie podpisał. Bo chyba Fast Rabbit to nie imię i nazwisko, ani nazwa firmy. Więc jeśli ktoś spędza sporo czasu pisząc "ofertę", ale się nie podpisuje to istotne ostrzeżenie. Bo jeśli nie chce podawać swoich danych, to czego się obawia? Druga sprawa to same "założenia" projektu. Nawet po szybkim przeglądzie widać, że nic się tutaj nie zgadza. O samym działaniu rządzenia praktycznie brak informacji - 230VAC, termostat i 3 przekaźniki 8A to właściwie cały opis części wykonawczej... Natomiast o niepotrzebnych wodotryskach jest całe mnóstwo. Po co komu aż 3 wyświetlacze: oled, tft oraz matryca LED? Dlaczego aż tyle i dlaczego akurat 128x160 pikseli? to już 480x272, albo 128x128 będzie złe? Standard MISRA, który ma mieć jakieś związki z zawieszaniem... itd. itp. Prawdę mówiąc po przeczytaniu założeń mam nieodparte wrażenie, że pisał je ktoś związany z EdW i EP, kto ma opisane moduły pod ręką i z jakiegoś powodu chce z nich ulepić projekt... Mogę się oczywiście mylić, ale radziłbym ewentualnym zleceniobiorcom daleko idącą ostrożność - a moderatorom uwagę.
  26. 3 punkty
    W poprzedniej części udało mi się zebrać dane treningowe, czas te dane wykorzystać oraz przygotować własną sieć neuronową. X-CUBE-AI pozwala na wykorzystanie sieci zapisanych za pomocą różnych bibliotek, zdecydowałem się wykorzystać Keras, która jest nie tylko bardzo łatwa w użyciu, ale jeszcze całkiem dobrze opisana. Wydawnictwo Helion ma o ofercie świetną książkę pt. "Deep Learning. Praca z językiem Python i biblioteką Keras", którą bardzo polecam i z której korzystałem intensywnie przygotowując ten artykuł. Spis treści serii artykułów: Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.1 Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.2 Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.3 Podstawą biblioteki Keras, jak i chyba większości innych związanych z AI jest język Python, więc poznanie chociaż jego podstaw jest niestety niezbędne. Kolejny brakujący element to zainstalowanie interpretera języka Python3 oraz potrzebnych bibliotek. Znacznym ułatwieniem jest wykorzystani gotowej "dystrybucji" o nazwie Anaconda. Ze strony projektu wystarczy pobrać program instalacyjny i zainstalować. Za jednym zamachem uzyskamy dostęp do języka Python3 oraz mnóstwa narzędzi pomocnych przy przetwarzaniu danych. Domyślna instalacja nie zawiera potrzebnych nam bibliotek, warto więc uruchomić program Anaconda Navigator oraz doinstalować pakiety: keras, tensorflow oraz matplotlib. Skrypty używane do trenowania sieci możemy pisać w dowolnym edytorze tekstu, a następnie uruchamiać jak każdy inny program w języku Python3, ale jest to dość niewygodne. Znacznie wygodniejszym i polecanym w wielu książkach rozwiązaniem jest wykorzystanie edytora Jupyter, albo jego nowszego kuzyna JupyterLab. W pakiecie Anaconda znajdziemy obie aplikacje. Wystarczy uruchomić wspomniany Anaconda Navigator, a w nim JupyterLab: Sam JupyterLab działa wewnątrz przeglądarki internetowej, co jest o tyle wygodne, że pozwala łatwo pracować na zdalnym komputerze - nie zawsze mamy bezpośredni dostęp do odpowiednio wydajnej maszyny, a pracując w narzędziach Jupyter możemy wykorzystać moc zdalnej jednostki. Co więcej możemy tak samo pracować na płytce typu Jetson Nano, jak i na potężnej stacji roboczej w chmurze. Zanim przejdziemy dalej warto nieco bliżej zapoznać się z zebranymi danymi. Napiszmy w pythonie prosty skrypt, który wczyta oraz przedstawi graficznie pobrane dane: Już na pierwszy rzut oka widać, że dane nie są tak łatwe do analizy jak nieco wyidealizowany przykład z wykrywaniem uderzeń serca. Na szczęście użycie sieci neuronowej zwalnia nas z obowiązku pisania programu, który będzie musiał te dane ręcznie przetwarzać. Jak pamiętamy, dane z akcelerometru to 3 liczby, które reprezentują wartości przyspieszenia względem osi XYZ. W przypadku rozpoznawania gestów możemy nieco uprościć sobie zadanie i obliczyć wypadkowe przyspieszenie. Jedna liczba to nie tylko mniej danych, ale i o wiele łatwiejsza prezentacja diagramów. Oczywiście tworząc bardziej zaawansowane projekty możemy wykorzystywać wszystkie dane. W każdym razie na wykresie widzimy jak wyglądają nasze dane źródłowe oraz momenty gdy podczas uczenia naciskany był przycisk. Poniżej jeszcze kilka przykładów kiedy następowało wykrycie samego gestu: Jak pamiętamy pomiary wykonujemy co 20 ms, ale jeden wynik nie wystarczy do stwierdzenia, czy nastąpiło wykrycie poszukiwanego gestu. Musimy więc pamiętać "historię" pomiarów. Jak pisałem w poprzedniej części, wybrałem pomiary co 20ms oraz wielkość okna pomiarowego 128 wyników. Więc nasza sieć na wejściu będzie dostawała ostatnie 128 pomiarów i ma stwierdzić - czy nastąpiło wówczas poszukiwane machanie płytką, czy nie. Teraz możemy utworzyć nowy projekt projekt w JupyterLab i zacząć trenować naszą bazę. W JupyterLab nasz skrypt można podzielić na fragmenty, edytować i uruchamiać niezależnie. Dzięki temu jeśli np. dostroimy parametry sieci, nie musimy uruchamiać całego skryptu - wystarczy ponownie wykonać zmienioną część. To ogromne ułatwienie oraz oszczędność czasu. Na początku mamy więc import używanych bibliotek: import math import numpy as np from keras import models from keras import layers from keras.utils import to_categorical Następnie podobnie jak poprzednio wczytanie naszych danych treningowych oraz wykonania niezbędnych obliczeń: f = open('data1.csv','r') values = [] results = [] for line in f: fields = line.strip().split(',') if len(fields) == 4: x = float(fields[0]) y = float(fields[1]) z = float(fields[2]) values.append([math.sqrt(x**2 + y**2 + z**2)]) results.append(float(fields[3])) f.close() print('number of values: {}'.format(len(values))) Teraz musimy zmienić trochę postać danych. Plik wejściowy to 6467 wierszy z wynikami. Do uczenia sieci chcemy jednak wykorzystać okna po 128 wyników, które dają na wyjściu odpowiedź czy wykryto uczony wzorzec, czy nie. Co więcej dane powinniśmy podzielić na część treningową oraz testową - właściwie powinna być jeszcze trzecia część przeznaczona do walidacji, jednak jako prosty pierwszy przykład mam tylko dwie części: trening i test. train_values = [] train_labels = [] test_values = [] test_labels = [] split = len(values) * 0.8 for i in range(len(values) - 128): if i < split: train_values.append(values[i:i+128]) train_labels.append(results[i+128]) else: test_values.append(values[i:i+128]) test_labels.append(results[i+128]) train_values = np.array(train_values).reshape(-1, 128).astype('float32') train_labels = np.array(train_labels).astype('float32') test_values = np.array(test_values).reshape(-1, 128).astype('float32') test_labels = np.array(test_labels).astype('float32') Przyznać się muszę do jeszcze jednego niedociągnięcia - dane powinny być również znormalizowane. Najpierw planowałem wykorzystać wszystkie składowe XYZ, a wówczas normalizacja nie była konieczna. Jednak przeliczenie przyspieszenia na wartość wypadkową wprowadziło stałą odpowiadającą przyspieszeniu ziemskiemu - a w skryptach już tego niestety nie uwzględniłem. Teraz najważniejsza część czy uczenie samej sieci. Struktura jest banalnie prosta i pochodzi bezpośrednio z przykładów opisywanych w książce "Deep Learning. Praca z językiem Python i biblioteką Keras" train_labels = to_categorical(train_labels) test_labels = to_categorical(test_labels) network = models.Sequential() network.add(layers.Dense(16, activation='relu', input_shape=(128,))) network.add(layers.Dense(2, activation='softmax')) network.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy']) network.fit(train_values, train_labels, epochs=5, batch_size=8) network.summary() Po uruchomieniu kodu zobaczymy podsumowanie naszej sieci: Jak widzimy dokładność uczenia to 0.9086, moglibyśmy uzyskać dużo lepsze rezultaty tworząc bardziej wyszukaną sieć, ale przykład jest ogromnym uproszczeniem. Warto zwrócić uwagę na liczbę parametrów: 2064 dla pierwszej i 34 dla drugiej warstwy. Musimy pamiętać, że te parametry trzeba będzie zapisać w pamięci mikrokontrolera - jednak niewiele ponad 2000 parametrów to bardzo mało i pewnie dałoby się uruchomić nawet na STM32F0. Zanim zakończymy tworzenie sieci, jeszcze dwie ważne sprawy. Pierwsza to testowanie. Wynik, który widzieliśmy wcześniej dotyczy danych, na których sieć była uczona - jednak ważniejsze jest jak zachowa się z danymi, których "nie widziała". Podczas przygotowywania danych 20% zostało zapisane w zmiennych test_values, test_results i teraz mogą posłużyć do sprawdzenia jak sieć radzi sobie z nowymi informacjami: Uzyskujemy dokładność na poziomie 89.35%, czyli podobnie jak dla danych użytych do trenowania sieci. Ostatnia rzecz to zapisanie samej sieci do pliku, czyli wywołanie: network.save('data2.h5') Teraz mamy plik data2.h5, który jest gotowy do użycia z X-CUBE-AI, co opiszę w kolejnej części. data2.zip Spis treści serii artykułów: Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.1 Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.2 Sztuczna inteligencja na STM32, czyli przykład użycia X-CUBE-AI cz.3
  27. 3 punkty
    Słowem wstępu, wyobraźmy sobie że jesteśmy na słonecznej, piaszczystej plaży wzdłuż której rosną wysokie, bogate w orzechy kokosowe palmy. Pod jedną z nich stoi lokalny, doświadczony, zbieracz kokosów, u którego mamy zamiar nauczyć się tego ciężkiego rzemiosła. Jako zaledwie początkujący adepci tej trudnej sztuki, nie mamy jednak zielonego pojęcia jak się do tego zabrać. Dlatego nasz nowy mentor musi nam wytłumaczyć co i jak powinniśmy zrobić. Może on nam wytłumaczyć wszystko krok po kroku. Opisać dokładne pozycje naszych kończyn, prędkość z jaką będziemy się wspinać i sposób odbierania kokosu. Oczywiście opisanie nam tego na tyle dokładnie, byśmy mogli bezbłędnie przystąpić do pracy wymagałoby dużo czasu i energii. Zamiast tego, może pokazać nam osobiście jak to zrobić i dać nam przystąpić do pracy. W krótkim i bogatym w informacje czasie, będziemy wiedzieli jak przystąpić do pracy. Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Po tym ciut przydługawym wstępie, którym starałem się nakreślić pewien problem, możemy przystąpić do właściwego omówienia tematu. Istnieje wiele metod programowania robotów przemysłowych (manipulatorów). Wykonują one precyzyjne prace, wymagające wielu skomplikowanych ruchów. Jednak poza nimi, roboty mogą też wykonywać mniej skomplikowane ruchy np. podczas zmiany pozycji lub przełączenia między poszczególnymi pracami. O ile do wykonania operacji precyzyjnych niezbędny jest szczegółowo opracowany program, o tyle podczas ruchów mniej skomplikowanych czas programowania można ukrócić do metod Online Programming. Krótki film przedstawiający podział metod programowania Metody te pozwalają operatorowi manualnie lub za pomocą odpowiedniego kontrolera ustawić poszczególne pozycje robota, które zostaną przez niego zapamiętane w czasie bieżącym oraz odtworzone w postaci pożądanego przez nas ruchu. Programowanie robota spawalniczego przy pomocy metody teach-in Programowanie robota Festo BionicCobot przy pomocy metody play-back Programowanie play-back opiera się na sczytywaniu przez oprogramowanie pozycji robota, zapisywanie ich oraz odtwarzanie poprzez bezpośrednie poruszanie jego konstrukcją. Aby było to możliwe, każdy stopień swobody robota musi posiadać sensor umożliwiający określenie jego dokładnej pozycji. Oprócz tego napęd robota musi działać w wyrachowany i delikatny sposób, aby konstrukcja była jednocześnie sztywna i można było nią poruszać ręcznie. Programowanie teach-in z kolei polega na ustawieniu robota w ustalonych pozycjach za pomocą odpowiedniego kontrolera, za pomocą którego te pozycje zostaną zapisane a następnie odtworzone. Odtwarzanie kolejno zapisanych pozycji daje w efekcie płynny, ciągły ruch maszyny. Wyżej wspomniane metody omówimy na realnym przykładzie. Za pomocą kontrolera będziemy mogli stosować na prostym manipulatorze metodę teach-in. Robot napędzany jest serwami modelarskimi oraz wykonany w technice druku 3D. Serwa posiadają wbudowane potencjometry sczytujące pozycje kątowe wałów, jednak bez ingerencji w ich budowę ciężko będzie korzystać z tych potencjometrów do określania pozycji robota. Dlatego manualne ustawienie pozycji manipulatora metodą play-back wypada z puli naszych opcji. Poza tym istniałaby duża szansa na uszkodzenie stosowanych w robocie serw. Zamiast tego, posłużymy się kontrolerem, który będzie kinetycznym modelem naszego manipulatora i pozwoli także zahaczyć o ideę play-back. Ramię manipulatora wydrukowane w 3D, poniżej link do źródła https://www.thingiverse.com/thing:1015238 Kontroler odpowiada kinematyce prezentowanego robota i ma umieszczone potencjometry w każdym jego punkcie swobody. Poruszając nim, możemy ustalić rzeczywistą pozycję naszego manipulatora. Budując r obota o mniej skomplikowanej budowie, możemy pozostać przy samych potencjometrach np. zamontowanych na płytce stykowej. Jednak w przypadku tego robota korzystanie z tego rozwiązania byłoby trudniejsze i mniej wygodne w użytku. Model kinematyczny manipulatora Znając już budowę kontrolera oraz manipulatora, należy je już tylko do siebie podłączyć. Oprócz tego do układu dodane zostaną przyciski nagrywania oraz odtwarzania ruchu. Elektronika opiera się o mikrokontroler atmega328p. Zasilanie układu odbywa się z sieci napięciem 230 V. W obudowie znajduje się układ prostujący, przetwornica step-down zasilająca serwomechanizmy oraz płytka arduino ze wspomnianym wcześniej mikrokontrolerem. Dla wygody wszystkie piny arduino zostały wyprowadzone na zewnątrz obudowy. Cały schemat przedstawianej konstrukcji znajduje się poniżej: Schemat układu Lista opisanych komponentów: -S1 – włącznik główny, -S2 – przycisk nagrywania, -S3 – przycisk odtwarzania, -D1 – mostek Graetza 400V ; 4A, -D2 – zielona dioda LED, -C1 – kondensator elektrolityczny 1000µF ; 50V, -C2 – kondensator elektrolityczny 220µF ; 25V, -C3 – kondensator elektrolityczny 22µF ; 25V, -C4 – kondensator ceramiczny 100nF, -C5 – kondensator ceramiczny 22pF, -C6 – kondensator ceramiczny 22pF, -C7 – kondensator ceramiczny 100nF, -R1 – rezystor 150 Ω, -R2, R3, R4 – rezystor 1 kΩ, -POT 1, POT 2, POT 3, POT 4, - 10 kΩ, -Stabilizator napięcia LM7805, -Przetwornica step-down LM2596, -X1 – kwarc 16 MHz, Połączenie układu Po podłączeniu całej elektroniki można przystąpić do omówienia kodu arduino. Program ten został już zaopatrzony w bogatą ilość komentarzy, które na pewno pomogą osobom dopiero rozpoczynającym swoją przygodę z arduino: //biblioteka umożliwiająca nie tylko sterowanie serwami, ale także ich prędkością //nie jest ona koniecznością, wystarczy standardowa biblioteka <servo.h> oraz pozbycie się zmiennej 'predkosc' z koduć #include <VarSpeedServo.h> //definiujemy serwa używane w robocie: VarSpeedServo servo1; VarSpeedServo servo2; VarSpeedServo servo3; VarSpeedServo servo4; //definiujemy przyciski nagrywania i odtwarzania: const int przycisk_A = 2; const int przycisk_B = 3; //definiujemy wartości dla wciśniętych przycisków nagrywania i odtwarzania: int przycisk_A_ON = 0; boolean przycisk_B_ON = false; //definiujemy potencjometry: const int potencjometr1 = A0; const int potencjometr2 = A1; const int potencjometr3 = A2; const int potencjometr4 = A3; //definiujemy zmienne służące do odczytu wartości napięć z potencjometrów: int potencjometr_1_odczyt; int potencjometr_2_odczyt; int potencjometr_3_odczyt; int potencjometr_4_odczyt; //definiujemy zmienne służące do zapisu wartości kąta położenia poszczególnego serwa: int potencjometr_1_zapis; int potencjometr_2_zapis; int potencjometr_3_zapis; int potencjometr_4_zapis; //definiujemy tablice zapisujące położenie serwa: int Pozycja_serva1[]={1,1,1,1,1}; int Pozycja_serva2[]={1,1,1,1,1}; int Pozycja_serva3[]={1,1,1,1,1}; int Pozycja_serva4[]={1,1,1,1,1}; void setup() { //definiujemy piny do których podłączone są serwa: servo1.attach(6); servo2.attach(9); servo3.attach(10); servo4.attach(11); //definiujemy piny wejściowe przycisków nagrywania i odtwarzania: pinMode(przycisk_A, INPUT_PULLUP); pinMode(przycisk_B, INPUT_PULLUP); //inicjalizacja portu szeregowego do podglądu działania programu: Serial.begin(9600); } void loop() { //ustalanie prędkości serw w zakresie od 0 do 180: int predkosc = 90; //zapis formuły umieszczania odczytanej z potencjometrów wartości do tabeli: potencjometr_1_odczyt = analogRead(potencjometr1); potencjometr_1_zapis = map (potencjometr_1_odczyt, 0, 1023, 20, 175); potencjometr_2_odczyt = analogRead(potencjometr2); potencjometr_2_zapis = map (potencjometr_2_odczyt, 0, 1023, 5, 175); potencjometr_3_odczyt = analogRead(potencjometr3); potencjometr_3_zapis = map (potencjometr_3_odczyt, 0, 1023, 5, 175); potencjometr_4_odczyt = analogRead(potencjometr4); potencjometr_4_zapis = map (potencjometr_4_odczyt, 0, 1023, 20, 160); //serwa przyjmują pozycje zapisane w tabelach: servo1.write(potencjometr_1_zapis, predkosc); servo2.write(potencjometr_2_zapis, predkosc); servo3.write(potencjometr_3_zapis, predkosc); servo4.write(potencjometr_4_zapis, predkosc); //przy kolejnym wciśnięciu przycisku nagrywania tabela każdego serwa zostanie //nadpisana, zapamiętując obecną pozycję serwa: if(digitalRead(przycisk_A) == HIGH) { przycisk_A_ON++; switch(przycisk_A_ON) { case 1: Pozycja_serva1[0] = potencjometr_1_zapis; Pozycja_serva2[0] = potencjometr_2_zapis; Pozycja_serva3[0] = potencjometr_3_zapis; Pozycja_serva4[0] = potencjometr_4_zapis; Serial.println("Pozycja pierwsza zapisana"); break; case 2: Pozycja_serva1[1] = potencjometr_1_zapis; Pozycja_serva2[1] = potencjometr_2_zapis; Pozycja_serva3[1] = potencjometr_3_zapis; Pozycja_serva4[1] = potencjometr_4_zapis; Serial.println("Pozycja druga zapisana"); break; case 3: Pozycja_serva1[2] = potencjometr_1_zapis; Pozycja_serva2[2] = potencjometr_2_zapis; Pozycja_serva3[2] = potencjometr_3_zapis; Pozycja_serva4[2] = potencjometr_4_zapis; Serial.println("Pozycja trzecia zapisana"); break; case 4: Pozycja_serva1[3] = potencjometr_1_zapis; Pozycja_serva2[3] = potencjometr_2_zapis; Pozycja_serva3[3] = potencjometr_3_zapis; Pozycja_serva4[3] = potencjometr_4_zapis; Serial.println("Pozycja czwarta zapisana"); break; case 5: Pozycja_serva1[4] = potencjometr_1_zapis; Pozycja_serva2[4] = potencjometr_2_zapis; Pozycja_serva3[4] = potencjometr_3_zapis; Pozycja_serva4[4] = potencjometr_4_zapis; Serial.println("Pozycja piąta zapisana"); break; } } //po wciśnięciu przycisku odtwarzania serwa będą przyjmować zapisane w tabelach pozycje //z odczekaniem 1.5 sekund w każdej pozycji: if(digitalRead(przycisk_B) == HIGH) { przycisk_B_ON = true; } if(przycisk_B_ON) { for(int i=0; i<5; i++) { servo1.write(Pozycja_serva1[i],predkosc); servo2.write(Pozycja_serva2[i],predkosc); servo3.write(Pozycja_serva3[i],predkosc); servo4.write(Pozycja_serva4[i],predkosc); Serial.println("Odtwórz ruchy"); delay(1500); } } //czas opóźnienia działania programu, od jego nastawy zależy płynnośc pracy robota: delay(200); } Program działa w sposób następujący: Po uruchomieniu programu, za pomocą modelu kinematycznego będzie można bezpośrednio kontrolować ruch manipulatora, Jeżeli wciśniemy przycisk S2 (nagrywania), obecnie ustawiona pozycja robota zostanie zapisana, Program umożliwia zapisanie pięciu różnych pozycji, Jeżeli wybierzemy przycisk S3 (odtwarzania), robot zacznie odtwarzać wybrane przez nas pozycje w nieskończonej pętli, Aby wytyczyć nową sekwencję ruchów należy zresetować układ. Efekt działania powyższego kodu przedstawia się następująco: Dzięki funkcji monitora szeregowego w arduino można obserwować pracę manipulatora bezpośrednio: Przepraszam wszystkich czytelników za jakość nagrań wideo, niestety ograniczały mnie możliwości sprzętowe. Przedstawiony wyżej kod jest prosty a przykład nieskomplikowany, jednak doskonale pokazuje podstawy działania tej metody. Nie mniej jednak mam nadzieję, że artykuł okaże się dla wielu osób pomocny. Oprócz tego zostawiam jeszcze bibliotekę <VarSpeedServo> VarSpeedServo-master.zip Powodzenia we wszelkich przyszłych projektach i przygodach z elektroniką!
  28. 3 punkty
    W poprzednim odcinku nauczyliśmy się włączać i wyłączać diodę LED przez Internet. Jedyne co wtedy zawierała strona, to krótki wyraz. Jeżeli chcemy, aby nasza strona była ładna, przejrzysta, i przede wszystkim - w 100% funkcjonalna, musimy się nauczyć podstaw HTMLa. Ale czym ten HTML jest? W skrócie, jest to język w którym piszemy strony WWW. To, jak strona ma wyglądać - piszemy w CSS, a co strona ma robić (animacje, skrypty) - piszemy w JavaScript (to pod żadnym pozorem nie jest zwykła Java). Zatem, zaczynamy! Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Spis treści serii artykułów: 1. Omówienie, i przygotowanie środowiska 2. Zapoznanie z nowym środowiskiem, praca jako Arduino, prosty serwer WWW 3. Przyspieszony kurs na webmastera 4. Wykresy, zapis do SPIFFS, mini smart-dom 5. Odbiór danych z przeglądarki, stałe IP, łączenie modułów ESP Plik index.html Proponuję zacząć od stworzenia strony na komputerze. Nie jest to trudne, a dopiero kiedy uznamy że strona jest gotowa - wgramy ja do mikrokontrolera. Stwórzmy zatem nowy folder: W środku tworzymy nowy plik: index.html. To będzie nasza strona główna. Otwieramy, i na razie wklejamy to: <!DOCTYPE html> <html> <body> <h1>Hello</h1> <p>there</p> </body> </html> Zapisujemy, i otwieramy w przeglądarce: Spróbujmy zrobić to samo na ESP. Możemy to zrobić na kilka sposobów: zrobić Stringa i co linijkę dodawać kod w htmlu (oof) wgrać na zewnętrzną pamięć i odczytać z np. karty sd (przekombinowane) na stałe zakodować stronę (to nie jest nasz cel) użyć SPIFFS (very nice) SPIFFS Już wyjaśniam co to SPIFFS. W pierwszym artykule wspomniałem, że na płytkach ESP są dwa układy: mikrokontroler i pamięć flash. Bardzo dobrze to widać na ESP01. Do tej drugiej kostki wgrywamy nasz program - ale co, gdyby na to wgrać coś innego? Espressif przewidział tą możliwość, i zrobił takie coś jak SPI Flash File System (SPIFFS). Oznacza to tyle, że dostajemy w pełni funkcjonalny (i wbudowany!) system plików. Jaka jest jego pojemność? Definiujemy to w konfiguracji płytki - zwykle dzielimy pamięć na kod i SPIFFS (dla NodeMCU może to być 2 i 2MB). Otwieramy folder z naszym projektem Platformio. Na tym samym poziomie co plik platformio.ini, tworzymy folder /data. Pliki w środku będą skopiowane do ESP. Przenieśmy zatem naszego indexa do /data, i odpalmy VS Code: VS Code jest na tyle mądry, że obsługuje składnię wielu języków - w tym i HTMLa. Po otworzeniu powinniśmy zobaczyć coś takiego: Aby wgrać naszą stronę, przechodzimy do zakładki Terminal → Run Task. Szukamy tam "Upload File System Image" - i czekamy aż się wgra nasz plik. Musimy teraz wprowadzić lekkie modyfikacje do kodu. Zaczynamy od biblioteki obsługującej nasz system plików. #include <FS.h> Zróbmy sobie teraz funkcję (tudzież zmienną) która zwróci nam całą stronę. W środku "getPage" robimy kolejnego stringa, do którego dodajemy po kolei znak po znaku. Plik musi być najpierw otwarty, potem zamknięty - możemy obsłużyć 1 plik naraz. Na końcu zwracamy stringa ze stroną. Co robi "r"? Spróbuj sam zobaczyć, naciskając Spację+CTRL. String getPage() { String strona; File file = SPIFFS.open("/index.html", "r"); while(file.available()) strona+=(char)file.read(); file.close(); return strona; } Na samym początku funkcji setup odpalamy bibliotekę. Ja to dodatkowo obudowałem w wyświetlanie wiadomości na LCD: lcd.setCursor(0, 0); lcd.print("SPIFFS "); if(!SPIFFS.begin()) lcd.println("x"); else lcd.println("√"); Na koniec zostało tylko dopisać do funkcji obsługujących klienta, że strona ma być brana z getPage. void handleRoot() { server.send (200, "text/html", getPage()); } void handleOn() { digitalWrite(PIN_LED, LOW); server.send(200, "text/html", getPage()); } void handleOff() { digitalWrite(PIN_LED, HIGH); server.send(200, "text/html", getPage()); } Wgrywamy program, i wpisujemy adres IP do przeglądarki: Można powiedzieć, że pierwsza część backendu (piękny slang informatyczny) za nami. Teraz zajmijmy się frontendem. Specjalnie do tego przygotujemy sobie DS18B20, do znalezienia w zestawach kursu na malinke lub Arduino II. Zrobimy sobie mały inteligenty domek. Nie bój się dodać kilka innych czujników, o ile wiesz co będziesz robił. Pierwsze kroki z HTML W językach z rodziny C każdą funkcję zapisujemy przez jej nazwę, i otwierając klamerki. W HTMLu piszemy <x> aby zacząć pewien element, </x> aby go zakończyć. Przeanalizujmy zatem nasz kod "hello there": <!DOCTYPE html> <html> <body> <h1>Hello</h1> <p>there</p> </body> </html> Na początku mamy doctype. Informuje on przeglądarkę o tym, z jakim rodzajem dokumentu ma ona do czynienia. Piszemy wszystko w HTML5, w starszym HTMLu deklaracja ta by wyglądała inaczej. Gdybyśmy tego nie dali, przeglądarka mogłaby pomyśleć że ma do czynienia z HTML 3 lub starszym. Po definicji, otwieramy cały dokument - <html>. Otwieramy także <body> - tam zapisujemy wszystko to, co widzimy. Mamy zatem już szkielet strony - możemy dodać nagłówek <h1> oraz tekst <p>. Ale nikt chyba nie lubi Times New Romana (chyba, że robimy gazetę) - spróbujmy zmienić czcionkę. Kiedyś, dawno temu wszystko się kodowało w HTMLu (strony z 2004/5 mocno to pokazują). Dzisiaj używamy CSS do określania stylów - Obok index.html tworzymy zatem style.css. W środku możemy zapisywać, co elementy o danym ID lub danej klasie będą miały za właściwości. h1 { font-family: Arial, Helvetica, sans-serif; } #idd { color: #ccc; } .klasa { font-size: 10px; } Plik style.css to zbiór wszystkich naszych właściwości danych elementów. W powyższym kodzie, na początku mówimy że każdy h1 będzie pisany Arialem. Elementy z id #idd będą miały kolor szarawy, a z klasą .klasa będą wysokie na 10px. Od razu powiem, czym klasa różni się od id: jeżeli mamy przyciski, które się mają różnić tylko kolorem, musielibyśmy robić prawie dwa identyczne id, a na klasach - tylko jedna pod przycisk, i dwie małe pod kolor. Ba, możemy potem klasy 'kolor' wykorzystać gdzie indziej (np. do tekstu). Zobaczmy teraz, czy style działają. Najpierw musimy jednak zmodyfikować indexa: <head> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> <h1>Hello</h1> <p class="klasa">there</p> <p id="idd">general kenobi</p> </body> Pojawił się znacznik <head>. Tam często linkujemy dokumenty niezbędne do funkcjonowania strony, oraz definiujemy takie rzeczy jak nazwa, ikona, charset, itd - jak mówiłem, body widzimy, head nie. Zauważyłeś także, że przy znacznikach <p> są przypisane klasy i id. Możemy w ten sam sposób (przy znaczniku) także podać kolory i inne właściwości, ale od tego się odchodzi (dlatego wam pokazuję css). Na końcu, musimy jeszcze podać obsługę żądania ip/style.css do kodu. Dodajemy drugiego stringa czytającego style.css, oraz obsługę dla klienta - analogicznie jak w indexie. String getStyle() { String strona; File file = SPIFFS.open("/style.css", "r"); while(file.available()) strona+=(char)file.read(); file.close(); return strona; } void handleStyle() { server.send(200, "text/css", getStyle()) } //setup() server.on("/style.css", handleStyle); Wgrywamy kod, pliki i sprawdzamy, czy działa: Spróbujmy teraz zaimplementować JavaScript. Daje nam to możliwość "podstawiania" zmiennych do tekstu i dynamiczną zmianę strony, bez jej ponownego wczytywania. Podłącz swój czujnik i napisz kod (poważnie, jeżeli termometr zrobiłeś na Arduino, to na ESP nie będziesz mieć problemu). Ja wykorzystam Dallas DS18B20, dostępnego w forbotowym zestawie z malinką i Arduino - jest to cyfrowy termometr. Link do artykułu jest tutaj, a jak już mamy do niego kod, czas modyfikować stronę. Pouczymy się teraz czystego HTMLa (no, a bardziej frameworków). Podstawowe znaczniki HTML Znaczników mamy kilka(naście). Najbardziej będą potrzebne: <p> - tekst <hX> - nagłówek, za X dajemy numer (wielkość) <div> - pusty blok <button> - przycisk <a> - link <img /> - zdjęcie (uwaga: zdjęcia nie otwieramy, tylko od razu zamykamy) <script>, <style> - tam umieszczamy fragmenty kodu JS lub CSS <html>, <body>, <head> - tworzą szkielet strony Aby te znaczniki ostylować, możemy "surowo" to zrobić w HTMLu, dodać własne CSS, lub skorzystać z gotowych frameworków. Ale jak z gotowych? Otóż Bootstrap to framework zawierający zestawy klas, umożliwiające stworzyć ładną, i responsywną stronę. Oznacza to tyle, że kodowania będzie mało, a dostaniemy własną, elegancką stronę do wyświetlenia na wszystkim co ma dostęp do internetu. Będziemy chcieli stworzyć ładną stronę z dwoma przyciskami, i etykietą pokazującą temperaturę. Bootstrap Otwieramy plik indexa. Wszystko w <head> usuwamy i wklejamy to: <meta charset="utf-8"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"> <title>domek</title> Pierwszy znacznik informuje stronę o zestawie znaków - tutaj utf-8 (umożliwi to nam wyświetlanie polskich znaków). Następnie linkujemy arkusz bootstrapa, po czym nadajemy naszej stronie nazwę "domek". Przydałoby się jeszcze dodać FontAwesome, z tego linku (trzeba się zarejestrować). Pozwoli nam to na wyświetlanie różnych ikonek. Przechodzimy do samego dołu dokumentu. Tam dopisujemy: <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script> </body> </html> Pierwszy skrypt ładuje jQuery (lekki javascript), następny Poppera (pomaga bootstrapowi w responsywności), ostatni skrypt uzupełnia Bootstrapa. Dlaczego je ładujemy na końcu? To są największe pliki do załadowania, i często da się skorzystać ze strony bez nich. Jest to optymalizacja ładowania strony przy wolnym łączu. Szkielet strony W boostrapie tak wygląda struktura strony: Mistrz painta w akcji. Strona zawiera kontenery (.container), rzędy (.row), i kolumny (.col-*-*). Umożliwia to stworzenie eleganckiej i responsywnej strony w kilku łatwych krokach. Chcemy mieć kontener, rząd i kolumnę: <div class="container"> <div class="row"> <div class="col-sm"> </div> </div> </div> "container" to klasa kontenera, "row" rzędu, a "col-sm" małej kolumny. Teraz dajmy nagłówek, opis, etykietę i dwa przyciski: <div class="container"> <div class="row"> <div class="col-sm"> <h1>Centralna Baza Dowodzenia</h1> <p>Dumnie wspierane przez ESP8266</p> <a type="button" class="btn btn-success" href="/on">ON</a> <a type="button" class="btn btn-danger" href="/off">OFF</a> <p>Temperatura: <span id="temp"> N/A </span></p> </div> </div> </div> Elementy <h1> oraz <p> są domyślnie w Bootstrapie ostylowane. Tworzymy dwa linki o typie przycisków (odsyłam do kursu HTML na dole strony), oraz przypisuję im klasy przycisku, potem konkretnie o kolorach zielony i czerwony. Odsyłają one do podstron /on i /off, tak jak było wcześniej. Na koniec dajemy tekst, oraz etykietę z id "temp" która się przyda do skryptowania. Spróbujmy zatem to wgrać do SPIFFS i uruchomić: Jeżeli chcemy podejrzeć strukturę strony, wystarczy kliknąć prawy przycisk myszy i wybrać Zbadaj Element (chrome/opera). Pierwsze przygody z jQuery Uznajmy, że skończyliśmy stronę. Teraz spróbujmy wyświetlić temperaturę. na samym dole, przed gotowymi skryptami, otwieramy <script>. <script> </script> Dodajemy funkcję setInterval, wykonującą się co sekundę. <script> setInterval(function() { }, 1000); </script> W tej funkcji dodajemy ten kod: var strona = new XMLHttpRequest(); strona.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { document.getElementById("temp").innerHTML = this.responseText; } }; Tworzymy zmienną strona, która wywołuje żądanie do strony. Kiedy mamy naszą stronę, sprawdzamy, czy wszystko się poprawnie załadowało (status 200). Jeżeli tak, zmieniamy tekst. strona.open("GET", "/temp", true); strona.send(); Na koniec tworzymy żądanie, a potem je wysyłamy. Całość zatem powinna wyglądać tak: <script> setInterval(function() { var strona = new XMLHttpRequest(); strona.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { document.getElementById("temp").innerHTML = this.responseText; } }; strona.open("GET", "/temp", true); strona.send(); }, 10000); </script> Zostało nam wgrać nową stronę do SPIFFS, i modyfikujemy kod: void handleTemp() { ds.requestTemperatures(); cls(); lcd.print("Temperatura: "); lcd.println(ds.getTempCByIndex(0)); server.send (200, "text/html", (String)ds.getTempCByIndex(0)); } server.on("/temp", handleTemp); Standardowo już, w setupie dodajemy obsługę /temp, a w funkcji ją zwracamy i wypisujemy przy okazji temperaturę na wyświetlaczu. Efekt: Przyciski działają, mamy temperaturę - zadanie wykonane. Spróbuj teraz dodać sterowanie np. serwem, i odczyt z kontaktronu. W następnym odcinku zgłębimy Bootstrapa, FontAwesome i podepniemy DNSy. Powodzenia! Jeżeli chcesz się bardziej pouczyć HTMLa, CSS i frameworków, masz tutaj garść przydatnych linków (po polsku!): Bootstrap jQuery HTML CSS Spis treści serii artykułów: 1. Omówienie, i przygotowanie środowiska 2. Zapoznanie z nowym środowiskiem, praca jako Arduino, prosty serwer WWW 3. Przyspieszony kurs na webmastera 4. Wykresy, zapis do SPIFFS, mini smart-dom 5. Odbiór danych z przeglądarki, stałe IP, łączenie modułów ESP
  29. 3 punkty
    Cześć. Chciałbym zaprezentować projekt sterownika pieca dwufuncyjnego na paliwo ciekłe - gaz ziemny. Założenia projektowe: utrzymanie zadanej temperatury w pomieszczeniu/ mieszkaniu w okresie zimowym; wyświetlanie temperatur pomieszczenia, nastaw, godziny daty, stanu pracy pieca. BOM:arduino uno, serwo modelarskie sg90, czujnik temperatury lm335, wyświetlacz LCD 1.8" ST7735 waveshare, zegar rtc, obudowa wydruk 3d, materiał easy PET-G, zasilacz 9V DC, śruby, nakrętki, magnesy neodymowe. Działanie: urządzenie mierzy temperaturę otoczenia a następnie dostosowuje położenie pokrętła w celu załączania pieca. Jest to rozwiązanie najmniej ingerujące w jego działanie. Wykorzystanie przekaźnika przerywającego zasilanie powoduje natychmiastowe wyłącznenie pompy. Powyższe rozwiązanie powoduje tylko przekręcenie pokrętła do pozycji "0", przez co piec wygasza się zgodnie ze swoim cyklem pracy. Modyfikacja w piecu polegała na wymontowaniu oryginalnego pokrętła i włożeniu w jego miejsce ramienia połączonego z serwomechanizmem. Całość zmontowano w samodzielnie zaprojektowanej obudowie, wydrukowanej w technologii 3d. Korpus utrzymywał się na obudowie pieca za pomocą magnesów neodymowych, przez co nie ma konieczności wiercenia otworów.
  30. 3 punkty
    Przetworniki analogowo-cyfrowe i cyfrowo-analogowe. Wstęp Ten artykuł jest wstępem teoretycznym do następnych, praktycznych już artykułów na temat przetworników. Tą serię artykułów, należy brać jako ciekawostkę, gdyż układy którymi się posługujemy (mikrokontrolery AVR, Arduino, ARM) posiadają w swojej budowie przetworniki ADC i bardziej rozbudowane modele posiadają również przetwornik DAC. Ta seria ma za zadanie pokazania budowy wewnętrznej przetworników i ich zasady działania. Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Spis treści serii artykułów: Wstęp (ten artykuł) Budujemy przetwornik cyfrowo-analogowy Budujemy przetwornik analogowo-cyfrowy, metoda sukcesywnej aproksymacji 1. Czym właściwie jest przetwarzanie sygnałów? W świecie rzeczywistym (analogowy) występuje wiele różnych wielkości fizycznych, takich jak temperatura, wilgotność, napięcie, natężenie prądu, natężenie światła i wiele więcej, natomiast w świecie komputerów (cyfrowym) występują tylko liczby. Do konwersji tych wielkości pomiędzy 2 światami służą przetworniki, odpowiednio: analogowo-cyfrowy (napięcie jest zamieniane na liczbę, w przypadku gdy chcemy zamienić inną wielkość to musimy najpierw ją przekonwertować w napięcie o odpowiednich proporcjach) cyfrowo-analogowy (zamienia liczby w napięcia, a później można zamienić napięcie w inną wielkość fizyczną) Odwrotnością przetwornika analogowo-cyfrowego jest przetwornik cyfrowo-analogowy, który dokonuje odwrotnej konwersji. 2. Rodzaje przetworników. przetwornik analgowo-cyfrowy – zwany ADC (Analog Digital Converter), zamienia sygnały analogowe w ich reprezentację liczbową (cyfrową). przetwornik cyfrowo-analogowy – zwany również DAC (Digital Analog Converter) – zamienia liczby (reprezentacja cyfrowa) w sygnały analogowe (napięcie). Jedyną metodą przetwarzania jest precyzyjna drabinka rezystorów o proporcjach R:2R. (szerzej opisany w artykule nr. 1) Główne metody przetwarzania sygnałów przez przetwornika ADC: porównanie bezpośrednie (zwany również flash) przetwornik z sukcesywną aproksymacją (metoda zwana również jako metoda kompensacji wagowej) – porównuje napięcie z kolejnymi wagami napięcia generowanym przez przetwornik DAC (dokładniej opisany w artykule nr. 2), nie odporny na zmianę napięcia mierzonego (daje błędne wyniki). przetwornik z podwójnym całkowaniem – mierzy czas ładowania kondensatora, napięciem mierzony i rozładowywania napięciem referencyjnym (niestety z powodu braku elementów nie byłem w stanie go szybko zbudować), uśrednia napięcie mierzone (jeżeli się zmienia). Przetwornik ADC w postaci układu scalonego, do budowy oscyloskopu, wykorzystuje metodę sukcesywnej aproksymacji (układ AD7492) Tabela porównująca właściwości przetworników analogowo-cyfrowych: Zależność pomiędzy szybkością wykonywania pomiarów (w samplach na sekundę) a ich rozdzielczością (w bitach). Elipsami oznaczone poszczególne główne metody dla przetworników ADC. Sygnał wejściowy nie jest idealnie przekształcany na sygnał cyfrowy z powodu skończonej ilości porównań sygnału. Porównanie sygnału wejściowego (siwego), z sygnałem cyfrowym (czerwonym). Jak widać sygnał cyfrowy nie pokrywają się idealnie z sygnałem analogowym, ale w miarę możliwości przybliża go. 3. Zastosowania w praktyce przetworników. Oscyloskop, przetwornik ADC z sukcesywną aproksymacją i generator DAC. Mierniki uniwersalne, przetwornik ADC, metoda podwójnego całkowania. Uśrednia wyniki przy zmieniającym się sygnale wejściowym. Przykład wykorzystania w cyfrowym systemach audio, gdzie sygnał analogowy (z mikrofonów i instrumentów) trafia do przetwornika ADC, który przetwarza sygnał wejściowy i przekształca na wersję bitową (cyfrową), na którą można nakładać efekty (jak w komputerze). Następnie sygnał trafia do przetwornika DAC, który generuje sygnał audio z liczb i przez wzmacniacz trafia on do głośników. 4. Podsumowanie Był to krótki wstęp do poszczególnych artykułów pokazujący zależności pomiędzy różnymi metodami przetwarzania przez przetworniki. Do budowy wszystkich przetworników użyję Arduino Mega. Zaznaczam że tą serię należy traktować jako ciekawostkę, gdyż większość mikrokontrolerów posiada wbudowany przetwornik ADC, a przetwornik DAC jest wbudowany w większe układy i jest rzadko używany (głównie do audio). Spis treści serii artykułów: Wstęp (ten artykuł) Budujemy przetwornik cyfrowo-analogowy Budujemy przetwornik analogowo-cyfrowy, metoda sukcesywnej aproksymacji
  31. 3 punkty
    Przetworniki analogowo-cyfrowe i cyfrowo-analogowe. Budujemy własny przetwornik DAC. Na początku naszą przygodę z przetwornikami, powinniśmy zacząć od układu przetwornika cyfrowo-analogowego, gdyż kolejne układy przetwornikowe (ADC) korzystają z tego najprostszego rodzaju konwertera. Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Spis treści serii artykułów: Wstęp Budujemy przetwornik cyfrowo-analogowy (ten artykuł) Budujemy przetwornik analogowo-cyfrowy, metoda sukcesywnej aproksymacji 1. Wstęp Przetwornik cyfrowo-analogowy (zwany również DAC – od angielskiego: Digital Analog Conwerter) jest układem zamieniającym wartość liczbową (wartość liczbową) na określoną wartość napięcia (wartość analogową), proporcjonalną do liczby. W tym wpisie zajmiemy się przetwornikami równoległymi DAC, gdyż te są najczęściej stosowane i najprostsze w budowie. Wykonuje on odwrotną konwersję w stosunku do przetwornika analogowo-cyfrowego. 2. Jak właściwie działa konwersja z wartości cyfrowej na analogową. Przetwornik ten konwertuje liczbę n bitową w odpowiadające jej napięcie elektryczne. Napięcie wyjściowe przetwornika proporcjonalne jest do napięcia odniesienia i liczby zapisanej w systemie dwójkowym (dokładniej poszczególnych bitów tej liczby). Wzór na obliczenie napięcia wyjściowego: Gdzie: Uwy – napięcie wyjściowe, Uref – napięcie referencyjne (dla Arduino ok. 5V, w zależności od napięcia zasilania) n – ilość bitów wartości cyfrowej a0 do an – wartość bitowa 0 lub 1, odpowiadająca kolejnym bitom sygnału cyfrowego (liczby), a0 najbardziej znaczący bit (MSB), an – najmniej znaczący bit (LSB) Może wydawać się to skomplikowane, jednak takie nie jest. Pierwszy bit oznacza 1/2 napięcia referencyjnego, 2 oznacza 1/4 (połowę poprzedniego) Uref itd. 3. Budowa teoretyczna przetwornika cyfrowo-analogowego. Sercem przetwornika jest drabinka rezystorów, będących ze sobą w stosunku 1R do 2R (np. 1k do 2k itp.). Na wejście przetwornika podajemy liczbę w postaci binarnej, o n bitach (im większa ilość n, tym większa rozdzielczość przetwornika). Układ posiada 1 wyjście analogowe. Schemat najczęściej stosowanego przetwornika o rozdzielczości 8 bitów, przedstawia się następująco: Przykład budowy drabinki rezystorów 8 bitowego przetwornika DAC (wartość pod lub po prawej stronie to wartość rezystancji). Poziome rezystory o wartości R, pionowe o wartości 2R. Spróbujmy obliczyć wartość napięcia dla wartości 193 8-bitowej (o 256 wartościach). Liczba ta zapisana binarnie B11000001. Możemy podstawić do powyższego wzory, jednak jeżeli posiadamy wartość w systemie dziesiętnym (193) to możemy obliczyć w prostszy sposób: Uwy = wartość/rozdzielczość * Uref, w naszym przypadku Uwy = 5V * 193/255, co daje nam napięcie około 3,78V. Przykład podstawienia do wzoru: Gdy bit jest 1 zostaje podłączony do napięcia zasilania, gdy 0 do masy (co jest bardzo ważne, niepodłączony da błędne wyniki na wyjściu). Rozpływ prądów pokazany poniżej: 4. Budujemy naszego DAC'a. Do budowy prototypu przetwornika cyfrowo-analogowego, użyję Arduino MEGA, skorzystamy z powyższego schematu. Rezystory o wartości 2R będą mieć 2,2k, natomiast R będą to 2 połączone rezystory o wartościach 10k i 1,2k (co daje około 1080 omów, jednak powinno być 1,1k). Magistralę danych (8 bitów) podłączamy do portu F arduino mega(wybieramy 1 port, 8 pinów ułożonych w kolejności, dla prostszego programu), a dla uno port C. Porty analogowe są ułożone w odpowiedniej kolejności, oczywiście my używamy ich jako cyfrowe. W naszym przypadku zbudujemy prosty przetwornik ośmiobitowy. Kod programu do ustawiania napięcia na wyjściu przetwornika i obliczanie oczekiwanego napięcia, wygląda nastepująco: // Zastępuj instrukcje pinMode(An, OUTPUT);, ustawia piny na porcie F jako wyjściowe DDRF = B11111111; // Ustawia wszystkie piny portu F na 0, odpowiada digitalWrite(An, 0); PORTF = B000000; // Komunikacja z komputerem Serial.begin(9600); Serial.print("Podaj wartosc: "); } uint8_t wartosc = 0; void loop() { // Czekamy aż zostanie przesłana wartość z komputera. while (Serial.available() > 0) { wartosc = Serial.parseInt(); // Zamieniamy ciag bajtów na wartość 8 bitową Serial.println(wartosc); Serial.print("Ustawiona wartosc napiecia wynosi "); float napiecie = (wartosc / 255.0) * Uref; // Wyliczamy oczekiwane napiecie Serial.print(napiecie, 3); // i wypisujemy je na port szeregowy. Serial.println("V"); Serial.print("Podaj wartosc: "); PORTF = wartosc; /* ustawiamy wartość na port F, przez co wszystkie piny zmienią stan w zależności od liczby 8-bitowej, port F to piny od A0 do A7 (A7 najbardziej znaczący bit)*/ } } 5. Sprawdzenie poprawności działania, pomiary. Wchodzimy w monitor portu szeregowego i podajemy wartość dziesiętną (od 0 do 255) lub można w postaci binarnej poprzedzając, ciąg 8 zer lub jedynek, duża literą B. Mikrokontroler zwraca oczekiwaną wartość napięcia, którą należy zmierzyć w układzie by porównać błąd przetwornika DAC. Pomiar napięcia rzeczywistego dla ustawionego napięcia 2,46V (wartość 128). 2 pomiar napięcia rzeczywistego dla ustawionego napięcia 0.98V (wartość 51). Przykład komunikatów do powyższych pomiarów, w polu do wysyłania przykład wpisanej liczby w systemie binarnym. 6. Dokładność przetwornika cyfrowo-analogowego. Rezystory użyte przy budowie przetwornika DAC mają swoją określoną tolerancję w moim przypadku 5%, przez co niektóre rezystory mają lekko różne wartości co powoduje nierównomierny rozpływ prądu i lekko odmienne napięcie na wyjściu od oczekiwanego. Jeżeli chcemy uzyskać jak najlepsze wyniki musimy używać rezystorów o jak najmniejszej tolerancji (najczęściej jest to 1%) i zapewnić stabilne źródło zasilania. Mierząc rzeczywistą rezystancję rezystora 2,2k, o tolerancji 5%, otrzymujemy 2,16k (Rezystancja zaniżona o 40 omów). 7. Poprawiamy przetwornik cyfrowo-analogowy Ustawiamy połowę napięcia referencyjnego (wartość 128, Uref = 4.91) i podłączamy do układu obciążenie w postaci opornika 10k i teoretycznie powinniśmy otrzymać około 2,45V (połowę napięcia referencyjnego), ale ... Wynik powinien wyjść około 2,45V, a jednak otrzymujemy 2,19V. Przetwornik daje na wyjściu oczekiwane wartości napięcia, jednak gdy podłączymy obciążenie do układu (np. rezystor o wartości 10k), zauważymy że napięcie bardzo mocno spadło, z powodu słabej wydajności prądowej układu, aby temu zaradzić trzeba wzmocnić napięcie przy wykorzystaniu wzmacniacza operacyjnego, który zwiększy wydajność prądową naszego własnego przetwornika cyfrowo-analogowego. Schemat takiego układu wygląda następująco: Schemat przetwornika DAC z wzmacniaczem operacyjnym, rezystor R17 ma rezystancję 2 razy mniejszą od R (R/2). 8. Zastosowania Układy pomiarowe (generator wzorcowy, konwersja wartości cyfrowych na napięcie) Układy audio (karty dźwiękowe, generowanie dźwięku wyjściowego dla słuchawek lub głośników) Sprzęt wideo (analogowa transmisja sygnału video np. VGA) 9. Podsumowanie Budowa przetwornika analogowo-cyfrowego jest bardzo prosta, jednak uzyskanie dokładnych wyników jest bardzo ciężkie. Przetwornik DAC jest podstawą działania przyrządów pomiarowych i przetworników ADC, które dokonują odwrotnej konwersji w stosunku do przetwornika cyfrowo-analogowego. W dalszych częściach artykułu poznasz budowę i działanie przetworników analogowo-cyfrowych w których będziemy wykorzystywać przed chwilą poznany przetwornik. W załączniku kod programu wraz ze schematami. DAC_pliki.rar Spis treści serii artykułów: Wstęp Budujemy przetwornik cyfrowo-analogowy (ten artykuł) Budujemy przetwornik analogowo-cyfrowy, metoda sukcesywnej aproksymacji
  32. 3 punkty
    Cześć! We wtorek o 20:00 robię kolejnego streama. Tematem będzie praca zdalna w embedded. Odpowiemy sobie na pytania: Jak pracować zdalnie w branży embedded? Czy to w ogóle możliwe na dłuższą metę? Jak się do tego zabrać? Link do transmisji: Temat był już wspomniany podczas odcinka o tym jak wygląda ogólnie praca w embedded, jednak aktualna sytuacja wymaga szerszego omówienia. W planie mam między innymi: - Moje dotychczasowe doświadczenia z pracą zdalną. Co się sprawdzało, a co nie. - Jakie warunki musisz spełnić, żeby dało się pracować zdalnie? - Typowe problemy - sprzęt, sprzęt i jeszcze raz sprzęt - Narzędzia i techniki programistyczne, które mogą pomóc - unit testy, continuous integration, testy automatyczne na HW, code review, dokumentacja - Wsparcie sprzętowe - zasilacze sterowane, zdalny debug, generatory, sniffery, analizatory, symulatory, emulatory - Jak przygotować projekt na możliwość pracy zdalnej? - Gaszenie pożaru - jak pracować zdalnie z projektem, który nie jest na to gotowy? Jeżeli chcecie wiedzieć coś jeszcze w tym temacie - piszcie w komentarzach. Jeżeli macie jakieś sprawdzone sposoby na tematy wymienione w powyższej liście - również możecie się podzielić.
  33. 3 punkty
    Wprowadzenie W artykule zostanie przedstawiony sposób na optymalizację układów mikroprocesorowych pod względem wykorzystania zasilania. Celem jest przedstawienie prostych metod, które nie wymagają znacznych ingerencji w konstruowany układ, a jednocześnie dających wymierne korzyści. Efekty uzyskane na podstawie lektury tego artykułu pozwolą na lepsze zrozumienie konstrukcji procesora w kwestii budowy układu zegarowego. Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Zaczynamy na 8 bitach Na potrzeby zobrazowania toku myślenia zostaną wplątane w rozważania losy hipotetycznego młodego człowieka, którego nazwiemy Adam. Adam jest młodym elektronikiem, z pewnością przyszłym inżynierem, który od swoich rówieśników słyszał, że oni projektują, programują i uruchamiają. Adam zasięgnął informacji o co tu chodzi. Zakupił płytkę Arduino UNO, zasilacz sieciowy i pełen zapału zainstalował środowisko Arduino IDE. Chcąc wiedzieć jak pokierować swoim rozwojem, zapisał się do odpowiedniej grupy dyskusyjnej i zadał pytanie „mam arduino i co mogę na nim zrobić?” . Odpowiedź pełna sarkazmu, którego nie wyczuł, brzmiała: „Zamrugać diodą LED”. Ów młody człowiek znalazł przykład „blink.ino”, wczytał, skompilował i wgrał do płytki. Ku swemu wielkiemu zaskoczeniu na płytce zaczęła migać dioda LED. Z uśmiechem na twarzy, nie wiedząc tak naprawdę co zrobił, stwierdził: „Jestem programistą”. setup(){ pinMode(13, output); } main() { digitalWrite(13, HIGH); dely(1000); digitalWrite(13. LOW); delay(1000); } Rysunek 1. Arduino UNO Na listingu nr 1 znajduje się cały program, napisany w Arduino (zobacz ramkę „Dlaczego Arduino?”), mrugający diodą LED. Czy ten kod jest optymalny? Patrząc z punktu widzenia programisty, który nie wie jak jest zbudowany mikroprocesor: TAK, ten kod jest optymalny. Z punktu widzenia inżyniera, dobrze znającego architekturę wewnętrzną procesora oraz możliwości jakie daje programowanie niskopoziomowe: NIE, ponieważ to co powstanie po kompilacji będzie bardzo nadmiarowe. Prześledźmy co procesor będzie robić: W funkcji setup zostaje skonfigurowany pin nr 13 procesora jako wyjście. Nr 13 to tylko symboliczna nazwa jednego z wyjść płytki Arduino UNO. Nr 13 odnosi się w tym przypadku do PortB.5 procesora. Do tego pinu jest podłączona dioda LED poprzez rezystor o wartości 500 Ohm. W funkcji main, w pierwszej linii pin 13 jest ustawiany w stan wysoki, dioda LED świeci. Następnie, przez 1000 ms, procesor czeka, aż upłynie 1000 ms. Procesor zajmuje się tylko sobą, czekając aż upłynie 1000 ms. Po tym czasie pin 13 ustawiany jest w stan niski, dioda LED gaśnie. I znowu procesor nic innego nie robi, tylko czeka aż upłynie kolejne 1000 ms. I tak na okrągło, przez ponad 99,9% czasu procesor nic nie robi, tylko sprawdza, czy skończyło się 1000 ms. Schemat nr 1. Zobrazowanie podłączenia diody LED na płytce Arduino UNO W czasie oczekiwania na upłynięcie 1000 ms procesor mógłby robić pożyteczniejsze rzeczy niż tylko zajmowanie się samym sobą. Jeżeli już nie damy mu żadnego konkretnego zadania, to niech nic nie robi, dosłownie nic. Młodzi ludzie, tacy jak Adam, mając nikłą wiedzę elektronika, nie zważają na wykorzystanie energii. Podłączają zasilacz sieciowy do Arduino i nie przejmują się ile to zużyje energii. Płytka arduino UNO sama z siebie, bez procesora, pobiera 19,2 mA. Gdy włożymy procesor, który nie był jeszcze zaprogramowany, pobór prądu wzrośnie do 36,7 mA, a podłączona diod LED będzie pobierała kolejne 2,7mA. W tym momencie Adam się relaksuje. Natomiast my zajmiemy się optymalizacją układu, to będzie praca dla inżyniera. Wykorzystamy przy okazji notę katalogową naszego procesora. Optymalizacja W celu optymalizacji zużycia energii możemy zastosować diodę LED, która potrzebuje mniejszy prąd by świecić, możemy zmniejszyć zużycie energii przez procesor, a nawet możemy wymontować zbędne elementy z płytki Arduino UNO. To ostatnie nie jest wskazane, bo płytka ta jeszcze nie raz zostanie zapewne użyta. Z diodą LED jest tak, że została ona wlutowana z rezystorem na płytce i jej wymiana będzie kłopotliwa. Pozostaje nam zoptymalizowanie procesora, zmontowanie układu np. na płytce stykowej. Będziemy potrzebować procesor, rezonator kwarcowy 16MHz, diodę led, kilka rezystorów o różnej wartości, oczywiście płytkę stykową, kabelki, zasilacz, stabilizatory i programator AVR ISP lub dowolny inny pozwalający na programowanie procesora. Schemat nr 2. Podłączenie samego procesora Ale dlaczego prowadzimy takie rozważania? Nie zawsze będziemy mieli obok naszego urządzenia gniazdko zasilania o nieograniczonej energii i napięciu 230V. Wtedy zasilimy nasze urządzenie z baterii lub akumulatora. Co zrobić, żeby urządzenie pracowało nieprzerwanie przez 1 miesiąc? Należy dać taką baterię, która ma odpowiednią pojemność. To prawda. A co zrobić, żeby urządzenie pracowało przez cały rok? Czy należy dać 12 takich baterii? Po dobrej optymalizacji może się okazać, że nie musi to być konieczne. Przeanalizujemy, na przytoczonym wcześniej przykładzie Blink, jak optymalizacja programowa i sprzętowa przyczyni się do zmniejszenia zużycia energii. W tym celu zamiast używać płytki Arduino UNO użyjemy samego procesora Atmega328 i zasilimy go napięciem 6V poprzez stabilizator 5V, a wszystko będziemy montować na płytce stykowej. Pobór prądu w naszym układzie będzie wyglądał jak przebieg prostokątny o wypełnieniu 50%. Dolny poziom prądu będzie odpowiadał stanowi, gdy dioda LED jest zgaszona (poziom L), natomiast poziom górny będzie odpowiadał stanowi, gdy dioda LED świeci (poziom H).Sam procesor pobiera 14.5 mA, a dioda LED 2.7 mA. Będą to dla nas dane odniesienia. Średni prąd pobierany przez układ to 15,8mA. W ciągu doby układ pobierze 380 mAh. Chcąc zasilić układ z baterii 4 x 1.5V (np. AA o pojemności ok 2000 mAh) układ będzie pracować 5dni i 6 godzin. (dla płytki Arduino będzie to 2 dni i 4 godziny). W ten oto prosty sposób wydłużyliśmy czas pracy o ponad 100%. Optymalizacja nr 1: zmieniamy diodę LED na bardziej energooszczędną, zastosujemy inny rezystor. Zastosujemy diodę, która by świecić potrzebuje zaledwie 0,15 mA. W ten sposób zmniejszyliśmy prąd pobierany przez diodę. Czas pracy na baterii wydłuży się do 5 dni i 16 godzin. W ten sposób, niejako w gratisie, otrzymaliśmy 10 godzin pracy w stosunku do układu podstawowego na płytce stykowej oraz 84h w porównaniu do układu na oryginalnym Arduino. Wykres nr 1. Zależność prądu zasilania od napięcia zasilania dla dwóch przykładowych częstotliwości zegara Optymalizacja nr 2: zmniejszamy napięcie zasilania do 4.5 V, czyli wykorzystamy tylko 3 ogniwa AA. Nie używamy w tym momencie już żadnego stabilizatora Zwiększamy wartość rezystora zachowując parametry świecenia diody LED, możemy zauważyć że zmniejszył się prąd pobierany przez procesor. Teraz nasz układ pobiera średnio 11,35 mA i będzie pracował 7 dni i 8 godzin. Optymalizacja nr 3. Przełączymy pracę procesora na wewnętrzny układ zegarowy 8MHz. Wcześniej oczywiście należy zmodyfikować parametr funkcji delay, aby zachować odpowiednią częstotliwość migania diody LED. W tym przypadku, ponieważ zmniejszyliśmy częstotliwość o połowę, więc musimy ten parametr także zmniejszyć o połowę, czyli użyjemy delay(500). Nasz układ będzie pobierał średnio 7,55 mAh, a czas pracy na bateriach wydłuży się do 11 dni i 1 godzinę. Wykres nr 2. Pobór prądu w zależności od częstotliwości zegara i napięcia zasilania Optymalizacja nr 4. Procesor atmega328 ma możliwość zmiany konfiguracji, aby częstotliwość rezonatora była zmniejszona ośmiokrotnie. Wymagana jest tylko odpowiednia konfiguracja Fuse bitów. W tak prostym programie jak blink, nie musimy mieć tak szybkiego procesora. Ustawmy Fuse bit CKDIV8 na aktywny. Spowoduje to, że procesor będzie pracować z częstotliwością ośmiokrotnie mniejszą. Aby uzyskać tę samą częstotliwość migania diody LED musimy troszkę zmienić nasz program. W miejsce oryginalnego delay(1000) wstawmy delay(500/8) lub delay(65). Po kompilacji, wgraniu i przestawienie fuse bitu, dioda nadal miga, tak jak wcześniej, ale średni prąd pobierany przez układ zmniejszył się do 3.7 mA . W efekcie optymalizacji nr 4 nasz układ będzie pracować 22 dni i 13 godzin W nocie katalogowej, wykres powyżej, możemy zobaczyć, że napięcie zasilania możemy zmniejszyć aż do 1.8V. Niestety nie mamy takiej baterii, ale możemy odłączyć kolejną. Wykres nr 3. Maksymalna częstotliwość w zależności od napięcia zasilania Optymalizacja nr 5. Zasilamy nasz układ tym razem z dwóch baterii AA, czyli napięciem 3V. Oczywiście zmieniamy rezystor przy diodzie, aby zasilać ją tym samym prądem co poprzednio. Program, zegar i fuse bity zostawiamy niezmienione. Tym razem otrzymujemy zapotrzebowanie na prąd przez procesor na poziomie 1.1 mA. Nasz układ będzie pracować przez 75 dni i 22 godzin. Optymalizacja nr 6. Wykorzystamy wbudowany w procesor wewnętrzny układ zegarowy o częstotliwości 128kHz. W naszym przypadku, po korekcie w funkcji delay, nadal układ będzie migać diodą LED. Oczywiście pozostawiamy CKDIV8 aktywny uzyskując częstotliwość zegara 16kHz. Średni pobór prądu przez nasz układ wyniesie 0,5 mA, a czas pracy na dwóch bateriach AA, wyniesie 166 dni i 15 godziny. Można wykonać optymalizację nr 7 poprzez zmniejszenie napięcia zasilania do 2.4V, wykorzystując dwa ogniwa akumulatorków o pojemności 2000 mAh. Dioda LED już przestanie prawie świecić, ale układ nadal będzie pracować pobierając średnio 0.35 mA, a czas pracy osiągnie 238 dni i 2 godziny. Idąc dalej można wykonać optymalizacja nr 8. I wykorzystać wbudowany w procesor tryb zmniejszonego pobory prądu poprzez jego usypianie. Taki zabieg spowoduje, że procesor będzie pobierać jeszcze mniej prądu, ale to zadanie pozostawiam czytelnikowi. Krótkie podsumowanie tego co zrobiliśmy Tabela nr 1. Podsumowanie optymalizacji Atmega328 Dzięki zastosowaniu kilku optymalizacji wydłużyliśmy czas pracy naszego, bardzo prostego, układu. Poza tym zmniejszyliśmy o połowę ilość potrzebnych ogniw do zasilania, co zmniejszyło koszty eksploatacji. Wydłużyliśmy czas pracy naszego urządzenia 76 krotnie, wykorzystując o połowę mniej baterii. A jeżeli już kupimy 4 baterie, tak jak to miało miejsce w pierwszej wersji, ale podłączając je równolegle w pakiecie 2 x 2 baterie, to uzyskujemy ponad 150 krotne wydłużenie czasu pracy naszego układu w porównaniu do użycia oryginalnego Arduino UNO. Czytelnik może pokusić się o zgłębienie wiedzy o możliwościach procesorów w omawianej kwestii. Jest dostępna literatura omawiająca to zagadnienie. Ten artykuł ma za zadanie tylko przybliżyć to zagadnienie szerszemu gronu odbiorców, którzy dopiero zaczynają przygodę z mikroprocesorami. Można jeszcze zastosować bardziej ambitne metody zarządzania energią[ii], ale to już zostawiam czytelnikowi. Rozwinięcie na 32 bitach Nasz Adam o tym wszystkim co zrobiliśmy nie wiedział. Ale jego ambicja przerastała jego wiedzę. Napisał, czyli przekopiował swój pierwszy program i stwierdził: „ale przecież to tylko 8-bitowy procesor, użyję 32-bitowego”. Jak pomyślał, tak zrobił, zakupił Arduino M0, skorzystał ze swojego pierwszego programu, skompilował go, wgrał i…. dioda LED miga. Radość wielka, Adam „napisał” swój pierwszy program na procesor 32-bitowy. Znów jest szczęśliwy, choć nadal nie wie co robi procesor. Program wygląda identycznie jak poprzednio. Więc przypomnę co robi procesor. Procesor w funkcji main, w pierwszej linii pin 13 ustawia w stanie wysokim, dioda LED świeci. Następnie, przez 1000ms, procesor SAMD21G18 czeka, aż upłynie 1000ms, robi to szybciej, bo jest szybszy od Atmega328. Procesor zajmuje się tylko sobą, czekając aż upłynie 1000ms. Nudzi się. Po tym czasie pin 13 ustawiany jest w stanie niskim, dioda LED gaśnie. I znowu procesor nic innego nie robi, tylko czeka aż upłynie kolejne 1000ms. I tak na okrągło, przez 99,99% czasu procesor nic nie robi, tylko bardzo szybko sprawdza, czy skończyło się 1000ms. Rysunek 2. Arduino M0 Płytka Arduno M0 została zasilona z 4 baterii AAA, tak jak poprzednio. Średni pobór prądu wynosił 26.8 mA, a czas pracy układu, na używanych wcześniej bateriach, wynosi 3 dni i 2 godzin. Optymalizacja W przypadku procesora SAMD21G18 również możemy przeprowadzić podobną optymalizację. Ograniczymy się tylko dwóch etapów, w którym zasilimy płytkę z 2 baterii AAA . Optymalizacja nr 1. Aby ograniczyć zużycie prądy przez elementy dodatkowe na płytce wykorzystamy podobnie jak poprzednio sam procesor oraz tylko te elementy, które będą niezbędne do pracy. Po optymalizacji otrzymaliśmy średni prąd zasilania wynoszący 9,6 mA. Nasz układ będzie nieprzerwalnie pracować przez 8 dni i 14 godzin. Optymalizacja nr 2. Przy użyciu trybu pracy SLEEP dla omawianego procesora możemy obniżyć pobór prądu do ok 6uA. Mając takie możliwości możemy w czasie gdy dioda LED ma być zgaszona uśpimy procesor. Przy wcześniejszej optymalizacji osiągnęliśmy średni pobór prądu na poziomie 9,6 mA, teraz usypiając procesor przez połowę czas uzyskamy 4,8 mA. Wynik może nie powala bo i tak spora wartość, ale pamiętajmy, to jest o 50% mniej. Po tej optymalizacji otrzymaliśmy średni prąd zasilania wynoszący 4,8 mA. Nasz układ będzie nieprzerwalnie pracować przez 17 dni i 4 godzin. Jeżeli chodzi o optymalizację częstotliwości zapraszam do analizy noty katalogowej producenta i wykonania własnych testów. Krótkie podsumowanie tego co zrobiliśmy Tabela nr 2. Podsumowanie optymalizacji SAMD21G18 Podsumowanie Porównajmy teraz obie płytki Arduino. Obie posiadają podobną ilość pinów do wykorzystania, obie można zasilić albo z USB, albo z zewnętrznego zasilacza, obie pracują na maksymalnych prędkościach zegara jakie udostępnia producent. Procesor w Arduino Uno ma piny, które można obciążyć większym prądem niż w procesor w Arduino M0. Oba procesory mają możliwość korzystania z metod zarządzania zasilaniem, tym samym zmniejszania prądu zasilania procesora. Dla naszego Adama, jest bez znaczenia, która płytkę wykorzysta, ale dla czytelnika tego artykułu zapewne już nie. Nie zawsze jest sens używać najmocniejszy procesor, skoro słabszy i tańszy zrobi dokładnie to samo. Dla prostych aplikacji, które nie wymagają „super szybkości” procesor Atmega328 wydaje się być lepszym rozwiązaniem w porównaniu do SAMD21G18. Natomiast gdy budujemy aplikację bardzo skomplikowaną, wymagającą szybkich operacji i krótkich czasów reakcji to SAMD21G18 tym razem będzie lepszy od Atmega328. Wszystko należy przekalkulować. Jeżeli zoptymalizujemy nasz układ sprzętowo i programowo, to możemy podkusić się o zrobienie układu, który będzie niezależny od zasilania zewnętrznego. Możemy zasilać układ np. z energii słonecznej, która zostanie dostarczona przez ogniwo słoneczne. Podczas dnia nadmiar energii wytworzonej przez ogniwo można gromadzić w akumulatorze, z którego układ będzie zasilany nocą. Dobór ogniwa i akumulatora zależy już od tego jak skomplikowany mamy układ i jakie ma zapotrzebowanie na energię, ale ważne jest by układ działał cały czas. Ktoś mógłby powiedzieć perpetuum mobile, ale my powiemy że korzystamy z energii odnawialnej. Dlaczego Arduino Nie ma wątpliwości, że rozwój elektroniki sprawił, że wiele narzędzi i produktów stało się bardziej dostępnych dla zwykłego użytkownika. Idea Arduino doprowadziła to stanu, w którym to każdy może spróbować, w naszym przypadku, programowania i konstruowania elektroniki. Programowanie w Arduino jest bardzo proste, a programista nie musi znać budowy wewnętrznej procesorów, co w przypadku innych środowisk jest konieczne. Samo środowisko jest bezpłatne. Moduły Arduino stały się bardzo dostępne na naszym rynku, a za sprawą „specjalistów” z Chin również cenowo bardzo atrakcyjne. Wsparcie producenta i dostępność dokumentacji jest szczególnym ułatwieniem w budowaniu i programowaniu układów. Rozpowszechnienie Arduino na całym świecie sprawiło, że użytkowników i osób w nim programujących jest wielu. Jest bardzo wiele grup dyskusyjnych, forum internetowych czy repozytoriów na których jest omawianych całe mnóstwo problemów i ich rozwiązań, bibliotek napisanych przez użytkowników, czy po prostu opisów typu „jak zrobić…”. To wszystko sprawia, że zamieszczony w tym artykule opis dotyczy Arduino, od którego to zaczyna przygodę z programowaniem najwięcej osób.
  34. 3 punkty
    Zainspirowany artykułem o komunikacji Raspberry Pi z ESP32 z użyciem Mqqt chciałbym pokazać jak korzystając z JavaFx stworzyć prosty panel kontrolny dla urządzenia pracującego z tymże protokołem. JavaFX pozwala tworzyć aplikacje, które wyglądają bardziej nowocześnie, są równie wydajne, a przy tym można zachować dużo większą czytelność kodu. Od Javy w wersji 8 staje się to rekomendowaną biblioteką tworzenia graficznego interfejsu użytkownika[1]. Do przygotowania projektu będziemy potrzebować Eclipse IDE for Java Developers który znajdziemy tutaj https://www.eclipse.org/downloads/packages/ oraz wtyczki e(fx)clipse którą wyszukujemy i instalujemy poprzez Eclipse Marketplace znajdujący się w zakładce Help trzecia pozycja od dołu. Kiedy nasza instalacja zakończy się uruchamiamy ponownie Eclipsa i tworzymy nowy projekt wybierając File >> New >> Projekt… Po przejściu do ostatniego kroku kreatora nasz projekt pokaże się po lewej stronie. Do budowy projektu potrzebujemy jeszcze biblioteki Eclipse Paho którą znajdziemy pod tym odnośnikiem https://www.eclipse.org/downloads/download.php?file=/paho/releases/1.1.0/Java/plugins/org.eclipse.paho.client.mqttv3_1.1.0.jar Po pobraniu biblioteki dodajemy ją do naszego projektu przechodząc do File >> Properties I w zakładce Libraries w podmenu Java Builid Path wybieramy Add External JARs... i wskazujemy plik pobranej biblioteki. Teraz w naszym wygenerowanym projekcje ustawiamy parametry połączenia, kolejno nieprzechowywanie wiadomości pomiędzy ponownymi połączeniami z serwerem MQTT, czas oczekiwania na wiadomość, nazwę użytkownika i jego hasło. Na koniec tworzymy obiekt połączenia podając adres brokera wraz z portem oraz nazwę naszego klienta. MqttConnectOptions connectOptions = new MqttConnectOptions(); connectOptions.setCleanSession(true); connectOptions.setKeepAliveInterval(120); connectOptions.setUserName("tutaj nazwa użytkownika"); connectOptions.setPassword("tutaj hasło".toCharArray()); MqttClient mqttClient = new MqttClient("tcp://tutaj adres serwera:1883", "SimpleMqqt"); W następnym kroku stworzymy lewy panel który będzie wyświetlał stan czujników przykładowo temperaturę oraz obroty wentylatora oraz prawy panel z przyciskami do sterowania przykładowo oświetleniem zewnętrznym oraz oświetleniem wewnętrznym. Label temperatureLabel = new Label("Temperatura"); Label temperatureValue = new Label("0 \u2103"); Label fanLabel = new Label("Praca Wentylatora"); Label fanValue = new Label("0 %"); VBox leftBox = new VBox(temperatureLabel, temperatureValue, fanLabel, fanValue); leftBox.setAlignment(Pos.BASELINE_RIGHT); leftBox.setBackground(new Background(new BackgroundFill(Color.SEASHELL, null, null))); Label light1Label = new Label("Oświetlenie wnętrze"); Button light1Button = new Button("WYŁ"); Label light2Label = new Label("Oświetlenie zewnętrzne"); Button light2Button = new Button("WYŁ"); VBox rightBox = new VBox(light1Label, light1Button, light2Label, light2Button); rightBox.setAlignment(Pos.BASELINE_RIGHT); rightBox.setBackground(new Background(new BackgroundFill(Color.ALICEBLUE, null, null))); Mając już stworzone panele możemy dodać akcje dla przycisków czyli przesłanie wiadomości WŁ jeżeli przycisk na etykietę WYŁ i odwrotnie. Tutaj mam dwie możliwości tworząc obiekt wiadomości lub poprzez konfigurację bezpośrednią przy nadaniu wiadomości podając nazwę tematu, zawartość wiadomości, QoS, czy powinien zachować wiadomość light1Button.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { //Wysłanie wiadomości bez tworzenia obiektu try { if (((Button) event.getSource()).getText().equals("WYŁ")) { mqttClient.publish("mqtt/control/light1", "WŁ".getBytes(), 0, false); } else { mqttClient.publish("mqtt/control/light1", "WYŁ".getBytes(), 0, false); } } catch (MqttException e) { e.printStackTrace(); } }}); light2Button.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { //Wysłanie wiadomości z wykorzystaniem obiektu try { if (((Button) event.getSource()).getText().equals("WYŁ")) { MqttMessage mqttMessage = new MqttMessage(); mqttMessage.setPayload("WŁ".getBytes()); mqttMessage.setQos(0); mqttClient.publish("mqtt/control/light2",mqttMessage); } else { MqttMessage mqttMessage = new MqttMessage(); mqttMessage.setPayload("WYŁ".getBytes()); mqttMessage.setQos(0); mqttClient.publish("mqtt/control/light2", mqttMessage); } } catch (MqttException e) { e.printStackTrace(); } }}); Możemy sterować oświetleniem ale chcielibyśmy także otrzymywać aktualizację danych z czujników. W tym celu potrzebujemy stworzyć obiekt który pozwoli nam wykonać akcję po otrzymaniu nowej wiadomości. MqttCallback mqttCallback = new MqttCallback() { @Override public void messageArrived(String topic, MqttMessage message) throws Exception { if (topic.equals("mqtt/temperature")) { Platform.runLater(new Runnable() { @Override public void run() { temperatureValue.setText(message.toString() + " \u2103"); }}); } else if (topic.equals("mqtt/fan")) { Platform.runLater(new Runnable() { @Override public void run() { fanValue.setText(message.toString() + " %"); }}); } else if (topic.equals("mqtt/state/light1")) { Platform.runLater(new Runnable() { @Override public void run() { light1Button.setText(message.toString()); }}); } else if (topic.equals("mqtt/state/light2")){ Platform.runLater(new Runnable() { @Override public void run() { light2Button.setText(message.toString()); }}); }} @Override public void deliveryComplete(IMqttDeliveryToken arg0) { // TODO Auto-generated method stub } @Override public void connectionLost(Throwable arg0) { // TODO Auto-generated method stub } }; W metodzie messageArrived dodajemy akcje ustawienia wartości temperatury kiedy otrzymamy wiadomość dla tematu mqtt/temperature , tak samo dla pracy wentylatora oraz akcję zmiany etykiety przycisku sterowania oświetleniem kiedy w temacie mqtt/state/light pojawi zmiana jego stanu. Jako że budowa JavaFx nie pozwala bezpośrednio na zmianę wartości elementów musimy skorzystać z konstrukcji Platform.runLater. Tak przygotowany obiekt wskazujemy jako odbiorcę wiadomości w naszym obiekcie połączenia, łączymy się z brokerem za pomocą obiektu z parametrami połączenia oraz obserwujemy wybrane przez nas tematy. mqttClient.setCallback(mqttCallback); mqttClient.connect(connectOptions); mqttClient.subscribe("mqtt/temperature"); mqttClient.subscribe("mqtt/fan"); mqttClient.subscribe("mqtt/state/light1"); mqttClient.subscribe("mqtt/state/light2"); Do głównego okna aplikacji dodajemy poprzednio stworzone panele. BorderPane root = new BorderPane(); root.setLeft(leftBox); root.setCenter(rightBox); Scene scene = new Scene(root, 400, 400); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.setTitle("Simple Mqqt Control"); primaryStage.show(); Ostatni krok to dodanie akcji zamknięcia połączenia z naszym serwerem, kiedy zamkniemy okno naszej aplikacji. primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() { @Override public void handle(WindowEvent event) { try { mqttClient.disconnect(); mqttClient.close(); } catch (MqttException e) { e.printStackTrace(); } } }); Nasz klient jest gotowy do użycia, jako że jest to tylko przykład nie powala swoim wyglądem. Jest podzielony na sekcję czujników i sekcję kontroli client.zip [1] https://javastart.pl/baza-wiedzy/frameworki/javafx
  35. 3 punkty
    Nie poprawiłeś - zamiast jednego masz wciąż dwa odczyty z pinu, a zamiast użyć zmiennej pozbyłeś się jej w innym miejscu. No - ale załóżmy, że to możesz poprawić. Tyle, że - jak sam zauważyłeś - nie działa to jeszcze tak jak powinno. Po prostu - sterowanie wyświetlaczem nie odbywa się za pomocą magii w zerowym czasie, ale wymaga jakiejś tam operacji na pinach, która to operacja zabiera czas. I dlatego każde wypisanie czegoś na wyświetlaczu spowoduje utratę impulsu czy kilku. Można to ominąć, rezygnując ze zmian wyświetlania kiedy rolka jest w ruchu - ale to też niezbyt wygodne... No - a gdyby udało się jakimś sposobem zmusić Arduino, aby robiło dwie rzeczy na raz? Piny 2 i 3 mają taką ciekawą możliwość, że zmiana ich stanu może spowodować natychmiastowe wykonanie jakiegoś fragmentu programu, przerywając program główny. I takie właśnie przerwanie możemy wykorzystać. volatile int licznik; void licz(void) { licznik++; } void setup() { /* tu różne pinMode, inicjalizacja lcd * i co w ogóle potrzebne */ attachInterrupt(digitalPinToInterrupt(2), licz, RISING); } volatile oznacza, że zmienna może w każdej chwili zmienić swój stan niezależnie od tego, co robi program i nie można oszczędzać na pojedynczym jej odczycie z pamięci; attachInterrupt podłącza funkcję licz() do przerwania na pinie 2 w chwili, kiedy sygnał zmieni się z niskiego na wysoki, a funkcja digitalPinToInterrupt tłumaczy numer pinu na numer przerwania (które w różnych typach Arduino są różne). Teoretycznie można by było teraz użyć owej zmiennej w analogicznym jak Twój programie: int poprzedni_licznik; void loop() { if (digitalRead(przyciskReset) == LOW) { licznik = 0; } //wyświetla na LCD ile razy czujnik wykrył magnes if (poprzedni_licznik != licznik) { poprzedni_licznik = licznik; Serial.println(licznik); lcd.clear(); lcd.setCursor(0, 0); lcd.print(licznik); } } Ale życie nie jest takie proste; cztery razy odczytujemy zmienną licznik, i jej wartość może zmienić się między odczytami; do tego dochodzi fakt, że zmenne typu int zajmują dwie komórki pamięci, a zmiana wartości może nastąpić równie dobrze pomiędzy odczytem jednej i drugiej. Na szczęście wykonanie przerwań można w Arduino wyłączyć na czas odczytu, i poprzez wprowadzenie zmiennej roboczej wyeliminować konieczność wielokrotnych odczytów: int poprzedni_licznik; void loop() { int roboczy licznik; if (digitalRead(przyciskReset) == LOW) { cli(); // wyłączamy przerwania licznik = 0; sei(); // włączamy przerwania z powrotem } cli(); roboczy_licznik = licznik; sei(); //wyświetla na LCD ile razy czujnik wykrył magnes if (poprzedni_licznik != roboczy_licznik) { poprzedni_licznik = roboczy_licznik; Serial.println(roboczy_licznik); lcd.clear(); lcd.setCursor(0, 0); lcd.print(roboczy_licznik); } } Co jednak, jeśli magnes wlezie pod hallotron akurat gdy przerwania będą zablokowane? Czy program nie zgubi impulsu? Otóż nie. Arduino zapamiętuje w takiej chwili, że przerwanie powinno być wykonane i wykonuje je jak najszybciej po wykonaniu sei(). W ten sposób o ile nie będziemy ciągnąć przewodu z naddźwiękową prędkością żaden impuls się nie zgubi. A teraz kolego @atMegaTona - po jakiego grzyba tu jakiś timer liczący zbocza i wyświetlanie w regularnych odstępach czasu? Zauważyłeś, że nie mierzymy prędkości przeciągania kabla, interesuje nas wyłącznie ilość obrotów rolki. Ale wróćmy do programu. Oczywiście - w programie trzeba dodać przeliczanie ilości obrotów na centymetry, ale to raczej nie powinno stanowić problemu. A teraz pytanie od strony mechanicznej: czy zabezpieczyłeś rolkę przed cofnięciem (łożyska jednokierunkowe lub nawet prosty mechanizm zapadkowy)? Bo cofnięcie rolki w tym przypadku nie zmniejszy stanu licznika tylko go zwiększy, niszcząc w ten sposób wynik pomiaru. Oczywiście można zrobić tak, że cofnięcie rolki będzie zmniejszało licznik - ale czy jest to konieczne? Bo wymaga nie tylko zmiany w programie, ale również dołożenia drugiego hallotronu.
  36. 2 punkty
    Uwaga ten kurs NIE jest dla osób początkujących,słabo znających język C( wymagany poziom średnio-zaawansowany)!!! Chciałbyś programować ESP w 'C' ? Zależy ci bardzo na optymalizacji ? Brakuje ci miejsca na twoim ESP ? A może chcesz nauczyć się czegoś nowego? Jeśli tak to ten tutorial jest dla ciebie! W tej części tutorialu omówię jak skonfigurować Eclipse pod programowanie ESP8266 w 'czystym' C No to zaczynamy Zanim zaczniemy coś robić musimy upewnić się, że posiadamy kompilator GCC. Jeśli go nie mamy zainstalowanego to koniecznie trzeba zainstalować! Pobieramy eclipse >>> Pobierz oraz toolchain do ESP >>> Pobierz Jak nam się nam pobierze to najlepiej jest wypakować na 'Dysk C' Importujemy projekt Hello_World z folderu Examples: U mnie wyskoczył błąd, że w Workspace istnieje już projekt o tej samej nazwie, ale to tylko dlatego, ponieważ już wcześniej go zaimportowałem. Teraz już wystarczy tylko skompilować ten projekt i go wgrać do naszego esp8266! Powinniśmy jeszcze potem w właściwościach projektu ustawić (ustawić na inny make, jeśli nie mamy mingw32-make Powinniśmy potem 2 razy nacisnąć na napis all , który odpowiada za kompilację naszego projektu. Po skompilowaniu otwieramy plik Makefile i edytujemy w nim dane potrzebne do wgrania na naszego ESP8266 projektu. I teraz już wystarczy znowu powtórzyć wcześniejszą czynność, tylko tym razem wybieramy napis flash. Projekt powinien nam się wgrać(jeśli wyskakuje jakiś błąd przy wgrywaniu to coś w Makefile jest źle ustawione ). Teraz nasze ESP powinno wypisywać po UART 'Hello World', ale jeśli źle ustawimy częstotliwość naszego ESP to dostaniemy krzaczki na UART Koniec tej części tutoriala. Jestem otwarty na krytykę, tylko proszę zwrócić uwagę, że jest to mój pierwszy taki post na tym forum. W następnym tutorialu omówię strukturę projektu oraz w jaki sposób ESP8266 wykonuje nasz kod.
  37. 2 punkty
    Kurcze... dlaczego ja w 1979 wywaliłem do śmieci zepsuty kalkulator... dzisiaj byłby jak znalazł
  38. 2 punkty
    Cześć, jestem początkującym amatorem. Zaczynam naukę programowania i z pomocą tego forum, chciałbym połączyć to z ćwiczeniami przy konstrukcji podstawowych programów. Pozdrawiam wszystkich w tych ciężkich czasach.
  39. 2 punkty
    Witam! Zrobiłem zadania z kursu Arduino na temat wyświetlaczy LCD. Tworzenie tego układu sprawiło mi wiele frajdy, bo musiałem powtarzać łaczenie chyba z 5 razy ponieważ zawsze kabelka brakowało. Przysyłam zdjęcie mojego układu
  40. 2 punkty
    @Kapixar witam na forum Tak, możesz uszkodzić Arduino, najczęściej jednak napotkasz problemy z działaniem programu. Silniki "sieją" na liniach zasilania, może się tak zdarzyć, że podczas ruchów serwa cały program będzie się resetował lub będą działy się różne dziwne rzeczy. Oczywiście można ryzykować i podłączać, co więcej - wiele osób tak robi, ale to jest zdecydowanie zła praktyka, której nie należy pochwalać. Jest to ekstremalnie "niezgodne" ze sztuką. Serwomechanizmy (szczególnie podczas ruszania) mogą pobierać piki dużego prądu, Arduino nie jest na to gotowe.
  41. 2 punkty
    Dzień dobry , zrobiłem proste , a nawet bardzo proste urządzenie do pomiaru temperatury i wilgotności powietrza za pomocą dht11 . Może nie jest piękne ale mi się podoba. Użyte przeze mnie części to : -arduino pro mini -dht11 -przetwornica step-up - akumulatorek 18650 -wyświetlacz LCD -konwerter I2C Opis działania : Czujnik wysyła dane do Arduino poprzez interfejs 1-wire . Arduino odczytuje i konwertuje dane za pomocą biblioteki "DHT.h". Na końcu za pomocą biblioteki "LiquidCrystal_I2C" wysyła dane magistralą I2C i wyświetla je na wyświetlaczu.
  42. 2 punkty
    Cześć, ja tutaj żadnego hejtu nie widzę. @Elvis ma po prostu wątpliwości, które niestety podzielam. Jeśli uważasz, że nie używanie klasy string zapewni, że program nie będzie działał wadliwie - np. zawieszał się to gratuluję pomysłu Pozdrawiam BTW: @Elvis jest akurat użytkownikiem tego forum, który potrafiłby zrealizować to zlecenie bez problemów
  43. 2 punkty
    Nie ma sensu tego powtarzać. Kupisz Arduino i zaczniesz od podstaw, np. od kursów Forbota dostepnych na pasku powyżej i po kolei, na spokojnie dowiesz się wszystkiego. A jeśli jesteś bardzo niecierpliwy, poszukaj bilioteki servo.h i opisu do niej, choć nie wiem czy na tym etapie cokolwiek z tego Ci się przyda. Albo może coś o serwomechanizmach w ogóle: http://abc-modele.pl/serwomechanizmy/ choć przypoinam, że nie jest to typowy (pod względem zachowania) serwomechanizm. Tylko sterowanie impulsami wygląda identycznie. A co do tanich płyteczek Arduino: https://www.gotronik.pl/modul-atmega168pa-zgodny-z-arduino-pro-mini-p-2302.html https://abc-rc.pl/product-pol-6146-Pro-Mini-328-5V-16MHz-ATmega328P-z-bootloaderem-Bascom-AVR-zgodny-z-Arduino.html https://abc-rc.pl/product-pol-12737-NANO-V3-16MHz-USB-ATmega168P-odpowiednik-CH340-Klon-kompatybilny-z-Arduino.html https://allegro.pl/oferta/modul-pro-mini-328-mini-5-v-16-mhz-dla-arduino-8877763464 itp itd.. No nie przesadzaj, takie rzeczy to chyba umiesz policzyć? Przecież tu już nic z elektroniki nie ma, a tylko resztki geometrii i fizyki (przepraszam, Nauki O Przyrodzie chyba teraz) z podstawówki. Skoro piszą, że 50 rpm to znaczy, że wał wyjściowy robi ok. 0.8 obr/s. Jeśli założysz na niego szpuleczkę o średnicy powiedzmy 4 cm to jej obwód wyniesie ok. 12.5 cm z czego wynika, że teoretyczna prędkość liniowa na obwodzie (czyli nawijanej linki) to 15.7 cm/s. Napęd ma 2.3 kgcm, ale na tej szpulce będziesz dysponował siłą jedynie 1.15 kg, bo jej promień to przecież 2 cm. Ponieważ Twoja winda waży jedynie 0.5 kg, to nie wygląda jak żyłowanie silnika, choć obroty spadną i pewnie zamiast tych 15cm/s dostaniesz jakieś 10-12cm/s przy zasilaniu 9V, bo przy takim podano obroty i (zapewne) moment. Jeśli przewidujesz mniej, np. 5V to wszystko będzie odpowiednio słabsze i wolniejsze.
  44. 2 punkty
    Po kursie Arduino chcesz nauczyć się czegoś jeszcze? Albo chcesz zrobić inteligenty dom? A może Arduino po prostu ci nie wystarcza - na to wszystko jest rozwiązanie! ESP8266 to wydajny i tani mikrokontroler. Znajdziemy go na wielu płytkach, od małego 01 na NodeMCU kończąc. Dzisiaj zobaczymy jakie są rodzaje płytek, co w sobie ukrywa kostka mikrokontrolera, oraz spróbujemy przygotować środowisko i wgrać Blinka. Niestety, tak nie robimy - tylko zewrzemy piny. Spis treści serii artykułów: 1. Omówienie, i przygotowanie środowiska 2. Zapoznanie z nowym środowiskiem, praca jako Arduino, prosty serwer WWW 3. Przyspieszony kurs na webmastera 4. Wykresy, zapis do SPIFFS, mini smart-dom 5. Odbiór danych z przeglądarki, stałe IP, łączenie modułów ESP Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Możliwości ESP8266 Uznałem, że najlepiej będzie zestawić NodeMCU v3 z Arduino UNO. Najważniejsze informacje podałem w tabeli. Pamiętamy, że ESP pracuje na napięciu 3.3v. Kiedy podłączałem do 5V (nie róbcie tego w domu) się tylko grzał, ale lepiej tego nie róbcie. ESP ma także pamięć FLASH w oddzielnej kostce - co pozwala na dużą jej pojemność (zależy to od wersji modułu). Co więcej, interfejsy możemy zdefiniować na (prawie) każdym pinie. ESP pracuje o wiele szybciej od Arduino - bo aż na 80MHz (z możliwością do 160!), i przede wszystkim ESP jest 32 bitowe. No, i ESP się lekko grzeje, ale to nic złego. Warianty ESP8266 Ten mały mikrokontroler możemy znaleźć na równie małych płytkach, lub znacznie większych i rozbudowanych modułach. Jednym z mniejszych, samodzielnych modułów jest ESP12. Posiada wiele wyprowadzeń, lecz nie jest (nawet z przejściówką) zbyt przyjazny dla płytki stykowej. Posiada natomiast aż 4MB pamięci FLASH (wersja ESP12F). Będzie to optymalny wybór dla rozwiązań wbudowanych (np. własnych płytek). ESP12 Jeżeli natomiast szukamy czegoś równie małego, ale bardziej przyjaznego dla nas, tutaj nadchodzi ESP01. Ten mały modulik ma niestety mniej pamięci (niebieska wersja 512kB, czarna 1MB), oraz tylko 8 wyprowadzonych pinów - lecz do konkretnego zastosowania, np. gniazdka z przekaźnikiem, wystarczy. ESP01 (niebieski) ESP03 i ESP07 to uboższe wersje ESP12, ale posiadają ceramiczną antenę - a ESP07 nawet złącze do zewnętrznej anteny. ESP07 Pozostałe moduły rzadko się spotyka, różnią się jedynie ilością wyprowadzeń, rozmiarem, i sposobem montażu. Przygotowanie ESP do programowania W zależności od tego, jaki moduł wybrałeś, będziesz musiał albo przylutować przewody do jego wyprowadzeń, lub podłączyć się przewodami do płytki stykowej. Dlatego na początek nauki, najlepiej zakupić NodeMCU (płytkę deweloperską z ESP12 na pokładzie), Wemos (troszkę mniejsze płytki z ESP12) - mają wszystko wbudowane. Jeżeli taką płytkę wybrałeś, możesz pominąć ten krok. Mając "surowe" ESP12 lub 01, musisz je odpowiednio podłączyć. Połączenie ESP01 z konwerterem USB ↔ UART. Rozpiska pinów dla ESP01. Do tego będziemy potrzebować dwóch przycisków tact switch, kondensatora elektrolitycznego (z zakresu 100-1000µF), dwóch rezystorów 10kΩ, przewodów, oraz oczywiście ESP i konwertera. Pokazałem to na przykładzie ESP01, ale każdy ESP też ma takie wyprowadzenia: pin CH_PD łączymy na stałe do napięcia zasilania przez rezystor 10kΩ pin RST podciągamy do VCC rezystorem 10kΩ, oraz podpinamy przycisk zwierający do masy pin GPIO0 podpinamy do przycisku zwierającego z masą między VCC a GND dajemy kondensator pin RX konwertera łączymy z pinem TX ESP, a pin TX konwertera z pinem RX ESP piny VCC i GND ESP łączymy z pinami VCC i GND konwertera napięcie na konwerterze ustawiamy na 3.3V! Na NodeMCU także znajdziemy dwa przyciski. Przycisk RST odpowiada ze reset mikrokontrolera - tak samo jak w Arduino. Ale co robi przycisk PRG? Otóż, jeżeli na pin GPIO0 podamy logiczne 0 podczas startu mikrokontrolera, wprowadzimy go w tryb programowania. Dzięki temu będziemy mogli wgrać do niego nasz kod. Jeżeli nie mamy zainstalowanych sterowników dla konwertera (np. CH340), powinniśmy je pobrać i zainstalować. Przygotowanie środowiska ESP możemy programować na dwa sposoby, w języku lua - oraz klasycznie, jak arduino, w c++. Opiszę wam sposób jak programować ESP jako Arduino - a do tego potrzebne będzie Arduino IDE. Jeżeli jeszcze takowego nie mamy, pobieramy najnowszą wersję stąd, po czym instalujemy. Dokładny proces instalacji został opisany na kursie Arduino - jeżeli mamy już zainstalowane środowisko, uruchamiamy je, a następnie przechodzimy do zakładki Plik → Preferencje. Powinno nam się otworzyć nowe okno. Szukamy okienka "Dodatkowe adresy URL do menedżera płytek", i wklejamy ten adres: https://arduino.esp8266.com/stable/package_esp8266com_index.json Całość powinna teraz wyglądać tak: Klikamy OK - następnie przechodzimy do zakładki Narzędzia → Płytka → Menedżer płytek Szukamy "esp8266", i klikamy Instaluj. Pobrane zostanie ok. 100MB danych. Od teraz możemy wybrać płytkę ESP jak zwykłą płytkę Arduino. ALE! Jeżeli już kiedyś programowałeś, w innym IDE, zapewne wiesz, że Arduino IDE jest troszkę przestarzałe. Brak autouzupełniania, podpowiedzi, rozbudowanego systemu plików, GIT, i innych funkcji. Jest na to sposób! Dlatego w tym poradniku także opiszę instalację i konfigurację Microsoft Visual Studio Code do pracy z Arduino. Dzięki temu będzie o wiele prościej i wygodniej pisać programy. Pobieramy zatem najnowsze VS Studio Code z tej strony. Jak widać jest ono dostępne na Windowsa, Linuxa, i MacOS. Po pobraniu i zainstalowaniu, powinniśmy zobaczyć taki widok: Jeżeli ciemny motyw nam nie odpowiada, możemy to zmienić naciskając koło zębate (lewy, dolny róg) i "Color Theme" - jasny motyw to Light+. Problemem dla niektórych może być język angielski - lecz w informatyce jest on niezbędny do funkcjonowania. Nie ma problemu aby wrócić do spolszczonego Arduino IDE. Jeżeli jednak chcesz zostać przy VS Code, musimy zainstalować rozszerzenie Platform.io. Dokładniej masz to opisane w tym forbotowym artykule. Po zainstalowaniu Platformio, klikamy magiczny przycisk F1 (musimy go zapamiętać!), i ukazuje nam się to okno: Znajdziemy tam wszystkie funkcje znane z Arduino IDE! Teraz możemy podpiąć nasz konwerter lub NodeMCU do komputera. Tworzymy nowy projekt, i szukamy po lewej pliku platformio.ini. Tam możemy wybrać inną płytkę niż ta, którą wybraliśmy podczas tworzenia projektu. Możemy także ustalić inne rzeczy - więcej w dokumentacji. Otwieramy podfolder src, i szukamy pliku main.cpp. Otworzyło nam się nowe okno - pierwsze co widzimy, to biblioteka Arduino. Dołączy ona wszystkie funkcje z starego IDE. Wklejamy poniższy kod. Największą różnicą ESP w stosunku do Arduino, jest tutaj zapalenie poprzez ustawienie LOW na pinie. #include <Arduino.h> void setup() { pinMode(LED_BUILTIN, OUTPUT); //pin drugi jako wyjście } void loop() { digitalWrite(LED_BUILTIN, LOW); //zapalamy diodę delay(1000); //czekamy sekundę digitalWrite(LED_BUILTIN, HIGH); //gasimy diodę delay(1000); //czekamy sekundę } Teraz przyszedł moment na wgranie szkicu. Klikamy przycisk PRG (gdzieniegdzie opisywany jako FLASH), trzymamy, a następnie RESET, i puszczamy. Dioda powinna raz mrugnąć - oznacza to, że ESP jest gotowe do zaprogramowania. Klikamy zatem znowu F1, a następnie "Platformio: Upload". Cierpliwie czekamy, aż program się wgra. Kiedy zobaczymy ten komunikat: Przyciskamy przycisk reset na naszej płytce - i dioda powinna zacząć mrugać. Na dole, po lewej, na niebieskim pasku są także małe ikonki pozwalające wgrać szkic. Gratulacje, udało Ci się zaprogramować ESP! Możesz je teraz wykorzystać jako moduł Wifi, samodzielne urządzenie, zastosowań jest tyle ile dla Arduino, a nawet i więcej. Udało Ci się także skonfigurować poprawnie VS Code do pracy z płytkami Arduino (i nie tylko!). W następnym odcinku zobaczymy co tak naprawdę oferuje przesiadka na VS Code, oraz spróbujemy połączyć się przez Wifi. Liźniemy nawet trochę HTMLa z CSSem, aby postawić stronę WWW! W przypadku jakichkolwiek problemów, nie bójcie się pisać komentarzy. Spis treści serii artykułów: 1. Omówienie, i przygotowanie środowiska 2. Zapoznanie z nowym środowiskiem, praca jako Arduino, prosty serwer WWW 3. Przyspieszony kurs na webmastera 4. Wykresy, zapis do SPIFFS, mini smart-dom 5. Odbiór danych z przeglądarki, stałe IP, łączenie modułów ESP
  45. 2 punkty
    Gdybym miał zrobić kolejną maszynę trzyosiową to bym kupił arduino nano i ten shield V4.0.Dlaczego?Bo już wiem jak uruchomić urządzenie.Nie wiem ile kosztuje shield do arduino uno,nie wiem ile kosztuje arduino uno wiem ile kosztuje arduino nano.Tnij koszty. O ile się orientuję to budowniczowie frezarek trzyosiowychopartych na Arduino uno mieli problem z osią Z .Arduino uno i arduino nano mają ten sam procesor.Rozwiązanie masz na tacy podane.Nie zapomnij o rezystorach.Uruchomienie maszyny zajęło mi ok.10 godzin z czego 9 godzin to znalezienie wszystkich błędów .Jedna godzina to wszystkie przeróbki.Tobie zajmie to 20minut.Bardzo ważne jest jak zrobisz układ mechaniczny.Bez zainstalowanych silników i śrub trapezowych to ruch w poszczególnych osiach ma odbywać się prawie bez oporu.Jak pochylisz maszynę to kąt 20-30 stopni musi być wystarczający do zainicjowania ruchu.W mojej maszynie prowadnice są z prętów stalowych fi12 przeciąganych zdaje się na zimno.Dokładność(tolerancja) H7 zdaje się.Takie pręty sprzedawane są w odcinkach 6m (60-80zł).W mojej maszynie zużyłem 2m prętów to oznacza ,że materiał mam na jeszcze dwie tego typu maszyny.Łożyska liniowe fi12 a uchwyty na nie kupisz za grosze w sklepie elektrycznym UZU20(uchwyty na rury) produkowane przez AKS Zielonka.Na jedno łożysko 2 uchwyty w sumie 24 bo jest 12 łożysk.Na każdą oś 4 łożyska.Rysunki wykonałem w Eaglu.Istota wykonania tkwi w dokładności.Pamiętaj,że pręty na osie na swych końcach nie mogą być ostre. Najtrudniejszym elementem montażowym jest wyśrodkowanie nakrętki śruby trapezowej.Po zamontowaniu śruby trapezowej,nakrętki trapezowej i silnika ruch na osiach mimo śruby i silnika powinien odbywać się po przyłożeniu średniej siły.U mnie w kierunku osi x wystarczyło przechylenie maszyny pod kątem 60 stopni .Powodzenia
  46. 2 punkty
    Ja mam zawsze otwartą konsolę z gitem w katalogu projektu i zdecydowaną większość interakcji z gitem wykonuję z konsoli. W IDE albo graficznych narzędzi korzystam tylko przy podglądaniu zmian/merge albo blame Czasem też odpalam gitk jeżeli w gałęziach jest makaron i nie mogę dojść co się stało (jeżeli pracuje się poprawnie to takiej sytuacji nie powinno być ale wiadomo - są osoby mniej doświadczone, które czasem coś namieszają )
  47. 2 punkty
    Kompletnie skopałeś załączanie diodek tranzystorami T2 i T3. Wskazówka: zakładając, że emiter jest ciągnięty do +5V przez LED(y) z opornikiem a bazę sterujesz z potencjałów 0-3.3V to zastanów się jakie napięcia w takim połączeniu a) na bazie, b) na emiterze uzyskasz. Dlaczego nie zrobiłeś narzucającej się tutaj konfiguracji OE na npn? Wyjście odbiornika TSOP jest w zasadzie typu open collector. Niektóre mają tam jakiś podciąg do Vcc rzędu 30k, ale nawet jeśli, to obciążenie takiego czegoś dzielnikiem R6/R7 spowoduje, że nie masz szans na sensowne napięcie w stanie wysokim na pinie procesora. I po co w ogóle te kombinacje skoro akurat TSOP4836 może pracować od 2.5V umożliwiającym zasilanie z 3.3V i "direct drive" procesora? Upewnij się, że linia sygnału z TSOPa może być przerwaniem a nie jest jakimś przypadkowym pinem. Takie transmisje dobrze jest odbierać równolegle do wszelkich innych procesów zachodzących w programie. Marnujesz mnóstwo mocy na rezystorach, zasilając z 5V aż 8 diod podczerwonych połączonych równolegle. Mając aż 5V możesz spokojnie połączyć po 3 w szereg albo skorzystać z zasilania 3.3V i dać cztery grupy po dwie sztuki. Co robi zespół D1/T1? Czy to jakiś czujnik przeszkód? Dlaczego pompujesz w LED prawie 100mA? na wszelki wypadek dałbym jakieś zabezpieczenie przed odwrotnym podłaczeniem akumulatora. To oczyiwście może być odpowiednie złącze - tym bardziej jeśli kupisz gotowy pakiet modelarski), ale jeśli jeszcze nie wiesz jak to będzie rozwiązane, wstaw może PMOS na wejściu albo chociaż diodę szeregową. Bez tego oba stabilizatory i mostek widzą bezpośrednio szynę akumulatora więc pierwsze pójdą z dymem a dalej to trudno powiedzieć.. Debugger nie zawsze jest wystarczającym narzędziem do uruchamiania programu, szczególnie takiego który "odjeżdża" gdzieś po podłodze pokoju. Dałbym UART (a XMEGA ma ich trochę) gdzieś na złącze razem z zasilaniem 3.3V. W najprostszym przypadku podłączysz tam konwerter UART/USB i zobaczysz co procesor mierzy albo co akurat robi, a gdy kabelka nie starczy zawsze możesz podstawić tam mały moduł WiFi i mieć zdalną telemetrię z toru testowego. Program do dobrego LFa nie powstaje w jeden dzień więc zawsze podczas projektowania sprzętu miej z tyłu głowy to jak będziesz go uruchamiał, testował i poprawiał. Nie tylko na biurku, ale także podczas przejazdów. Logi ze stanów automatów, odczytów czujników i wysterowania silników są bezcenną pomocą w podkręcaniu osiągów a jak wiesz poziom tej konkrencji jest w Polsce niesamowity.
  48. 2 punkty
    Witam, Nie mam pojęcia jaki jest jednostkowy koszt. To hobby więc ekonomia projektu nie była dla mnie priorytetem. Najdroższym elementem była oczywiście PCB. Resztę cen możesz sprawdzić na farnell albo podobnej hurtowni online. Projekt płytki w załączniku (format KiCad). Jakbyś robił własną PCB to najtrudniejsza jest część pod przetwornicę DC/DC. Pozdrawiam. psu.zip
  49. 2 punkty
    Wiem o co chodzi - możesz zrobić bardzo prostą rzecz: // gdzieś tam zmienna globalna int poprzedni_licznik; // a w loop taka konstrukcja: if (poprzedni_licznik != licznik) { poprzedni_licznik = licznik; Serial.println(licznik); lcd.clear(); // Czysci ekran lcd.setCursor(0, 0); // Ustawienie kursora w pozycji 0,0 (pierwszy wiersz, pierwsza kolumna) lcd.print((licznik)*15 / 10); //wyswietla dane } Można by też wyświetlać licznik zaraz po inkrementacji (licznik++), ale wtedy trzeba by było drugi kawałek kodu aby wyświetlić przy kasowaniu. OK - czekamy. A mi odwrotnie
  50. 2 punkty
    Protokół HTTP jest bardzo często uzywany w zastosowaniach IoT. Przede wszystkim decyduje tu prostota implementacji, dostępność bibliotek oraz możliwość współpracy z typowymi serwerami WWW bez konieczności instalacji dodatkowego oprogramowania. Szczególnie ta ostatnia cecha może być szczególnie przydatna z uwagi na ilość darmowych usług hostingowych, których nawet ograniczone parametry pozwalają na stworzenie prostej aplikacji niewielkim nakładem środków i przy użyciu minimalnej wiedzy. Niestety - z tą minimalna wiedzą nie jest już tak słodko. Autorzy popularnych bibliotek zakładają pewne minimum wiedzy u użytkowników i pomijają sprawy dla nich oczywiste. Użytkownicy zaś nie mogąc znaleźć jakichś informacji na temat podstaw - próbują coś zrobić metodą prób i błędów, co nikomu na zdrowie nie wychodzi. Spróbuję więc przybliżyć owo "minimum konieczne", aby nie trzeba było przekopywać się przez dokumenty typu RFC po ty tylko, by przesłać do serwera zmierzoną temperaturę na balkonie. Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Spis treści serii artykułów: Protokół HTTP w zastosowaniach IoT - część 1: trochę teorii Protokół HTTP w zastosowaniach IoT - część 2: budujemy serwer Protokół HTTP w zastosowaniach IoT - część 3: tworzymy klienta Zacznijmy jednak od czegoś prostszego, mianowicie od tego... czym jest URL. Każdy z pewnością zna to słowo, i większość z czytelników pewnie rozumie (nawet intuicyjnie) co to takiego. Aby jednak uniknąć jakichkolwiek nieporozumień proponuję zapoznać się z budową URL-a. URL (czyli Uniform Resource Locator) to znormalizowany sposób określania położenia dokumentu (zasobu) w Internecie. Ponieważ dla różnych schematów mogą istnieć różne części, skupmy się wyłącznie na protokole HTTP. Taki URL składa się z następujących części: Schemat - sposób, w jaki nasz dokument będzie przesyłany. W naszym przypadku będzie to zawsze http; Użytkownik - nazwa użytkownika, dla zasobów które mogą być serwowane różnie w zależności od użytkownika. Jeśli nie jest potrzebna, można ją opuścić wraz następujący po niej ewentualnym drukropkiem i hasłem oraz znakiem @; Hasło - jak sama nazwa wkazuje, hasło dostępu do dokumentu. Może wystąpić tylko wtedy, gdy podajemy użytkownika, a jeśli nie jest potrzebne, można je opuścić wraz z poprzedzającym dwukropkiem; Host - nazwa (lub adres IP) komputera, na którym umieszczony jest nasz dokument. Mimo, że dopuszczalne jest podanie w tym miejscu adresu IP, nazwa hosta wymagana jest jeśli serwer hostuje więcej niż jedną stronę www na tym samym adresie IP (czyli praktycznie wszędzie oprócz naszego domowego Arduino czy Raspberry); Port - port TCP, na którym nasłuchuje serwer. Jeśli jest to domyślny port (w przypadku http będzie to port 80) - może być pominięty wraz z poprzedzającym dwukropkiem; Ścieżka - czyli miejsce, gdzie na serwerze znajduje się nasz dokument. Zawsze rozpoczyna się znakiem '/', składa się z członów rozdzielonych znakiem '/' i może zawierać wyłącznie 7-bitowe znaki; Zapytanie - czyli parametr przesłany do skryptu na serwerze, jeśli ścieżka wskazuje na wykonywalny program a nie fizyczny dokument. Jeśli nie jest potrzebne, może być opuszczone wraz z poprzedzającym znakiem '?'. Używając popularnej pochodzącej z BNF notacji można to zapisać tak: <schemat>://[<użytkownik>[:<hasło>]@]<host>[:<port>]<ścieżka>?[<zapytanie>] Już słyszę pytanie: a co z fragmentem? Przecież wpisując adres do przeglądarki możemy zakończyć go znakiem '#' i etykietą informującą, który fragment ma się pokazać na ekranie... Otóż trzeba sobie uświadomić że to, co wpisujemy do przeglądarki nie jest tak naprawdę URL-em. Przeglądarki rozumieją pewien swój zapis - np. brak schematu, narodowe znaczki w ścieżce czy właśnie ten '#' i fragment. Pamiętać należy jednak, że w przypadku braku schematu przeglądarka użyje domyślnego 'http', narodowe znaczki zakoduje w pewien określony sposób a fragmentu w ogóle nie wyśle do serwera, będzie on potrzebny wyłącznie przeglądarce przy wyświetlaniu. Ponieważ dalej będziemy posługiwać się uproszczoną wersją URL-i, przyjmijmy że jego składnia będzie w większości przypadków następująca: http://<host><ścieżka>[?<zapytanie>] Należy tu wspomnieć o czymś jeszcze: o ile składnia URL-a nie narzuca żadnego specjalnego formatowania dla części "zapytanie", przyjęto zunifikowany zapis par <nazwa>=<wartość>, oddzielonych znakami '&'. I tu uwaga: w dalszej części będę posługiwał się również pojęciem URI (Unified Resource Identifier). Nie chcę wnikać w szczegóły, przyjmijmy więc uproszczenie, że URI identyfikuje zasób w obrębie serwera i składa się ze ścieżki oraz opcjonalnego zapytania (czyli de facto stanowi fragment URL-a). Tak więc wiemy już z czego się składa adres w Internecie, spróbujmy przyswoić sobie... podstawy protokołu HTTP Przede wszystkim musimy wiedzieć, że połączenie http składa się zawsze z zapytania wysłanego do serwera i odpowiedzi serwera. I koniec. Nie ma żadnej dalszej komunikacji, szczególnie po wysłaniu odpowiedzi serwer nie będzie pamiętać, o co go poprzednio prosiliśmy. Taki protokół nazywany jest bezstanowym, gdyż każde zapytanie rozpatrywane jest przez serwer niezależnie od innych. Oczywiście - serwer może wprowadzać dodatkowe mechanizmy zapewniające jakieś tam zależności (choćby mechanizm ciastek i sesji), ale nie należą one do protokołu ale do konkretnych wykonywanych na serwerze skryptów. Zacznijmy od zapytania - czyli tego, co klient (np. przeglądarka) przesyła do serwera. Zapytanie składa się z linii zakończonych parą znaków CRLF (czyli w notacji C++ "\r\n") i zawsze rozpoczyna się linią w postaci: <metoda> <URI> <protokół> Nas interesuje na razie metoda GET czyli "pobierz zawartość zasobu wskazanego przez URI" oraz obowiązujący protokół HTTP/1.1. Tak więc początkowa zawartość zapytania pobierająca np. zawartość głównej strony forum Forbota będzie miała postać: GET /forum/ HTTP/1.1 Następne linie stanowią nagłówki zapytania. Może ich być wiele - np. podających język przeglądarki czy rozpoznawane kodowanie, zawsze w postaci: <nazwa>: <wartość> Nas interesuje przede wszystkim nagłówek "Host" podający nazwę serwera. Warto również poinformować serwer, że oczekujemy na zakończenie połączenia po przesłaniu dokumentu, co załatwia nam nagłówek "Connection". Co prawda wartość "Close" jest dla tego nagłówka domyślną, ale warto przyswoić sobie zasadę podawania serwerowi wszystkiego co jest potrzebne do zwrócenia odpowiedzi - i niczego poza tym. Tak więc pełne zapytanie będzie wyglądać tak: GET /forum/ HTTP/1.1 Host: forbot.pl Connection: Close Każde zapytanie musi kończyć się pustą linią, aby poinformować serwer że już skończyły się nagłówki i teraz oczekujemy odpowiedzi. Możemy teraz spróbować przetestować najprostsze połączenie. W tym celu musimy jednak zaopatrzyć się w jakieś narzędzie, umożliwiające połączenie z siecią i przesłanie w obie strony tekstowych komunikatów. Mamy tu dwie możliwości. W przypadku Linuksa najwygodniejsze będzie użycie konsolowego narzędzia. Takim narzędziem może być telnet lub netcat. Ponieważ nie we wszystkich dystrybucjach są one zainstalowane na dzień dobry, może wystąpić konieczność doinstalowania. Dla Debiana i pochodnych (Raspbian, Ubuntu, Mint itp.) będzie to: sudo apt install telnet lub sudo apt install netcat I tu uwaga: w większości przypadków ostatnie polecenie zainstaluje nam netcat-traditional, czyli tradycyjną wersję programu. Z różnych przyczyn (szczególnie przy testach połączeń UDP) lepsza może by wersja openbsd, tak więc jeśli mamy taką możliwość warto ją zainstalować: sudo apt install netcat-openbsd Składnia obu poleceń dla naszych celów jest podobna: telnet <adres> <port> lub nc -C <adres> <port> Opcja -C w drugim przypadku włącza tryb przekazywania sekwencji CRLF zamiast LF (czyli po naciśnięciu ENTER) - co jak wspomniałem wcześniej jest wymagane dla połączeń http. Inną możliwością jest użycie znanego programu PuTTY. Nie będę tu opisywać procedur instalacji dla wszystkich możliwych systemów operacyjnych, należy jednak wspomnieć o ustawieniach: W ustawieniach sesji należy zaznaczyć "Connection type: RAW" (czyli "surowe" połączenie, bez żadnych dodatkowych protokołów) i "Close window on exit: Never" (bo nie chcemy, aby okno zamykało się od razu po odebraniu danych nie dając nam czasu na przeczytanie). W ustawieniach terminala natomiast "Implicit CR on every LF" (inaczej linie na terminalu "rozjadą się"). Takie ustawienia możemy sobie zapisać pod jakąś mądrą nazwą (np. tak jak w moim przypadku "raw http") i używać w wielu następnych testach. Spróbujmy więc na początek połączyć się z jakimś ogólnodostępnym serwerem. Na początek spróbujmy serwera Google. Wpisujemy więc następujące polecenie (lub łączymy się poprzez PuTTY z serwerem): nc -C www.google.pl 80 Oczywiście musimy sporo przewinąć w górę aby dojść do początku transmisji (przy okazji widać, że pozornie prosta strona Google wcale nie jest taka prosta). Możemy teraz zobaczyć dwa najważniejsze nagłówki odpowiedzi: HTTP/1.1 200 OK Taki nagłówek będzie zawsze pierwszą linią wysyłaną przez serwer. Składa się z trzech części: Protokół - w tym przypadku HTTP/1.1 Kod odpowiedzi - w tym przypadku 200 (czyli "wszystko w porządku"). Spis wszystkich kodów możemy znaleźć np. w Wikipedii. Opis odpowiedzi - w tym przypadku "OK" Drugi nagłówek to Content-Type. Musi być zawsze obecny w odpowiedzi i zawiera po prostu typ mime przesłanego dokumentu. Spróbujmy teraz wymusić na serwerze inną odpowiedź. Jak wiemy, wpisując do przeglądarki "google.pl" w jakiś magiczny sposób adres zmienia się na "www.google.pl" - zobaczmy dlaczego: Jak widzimy - serwer odpowiedział informacją o tym, że dokumentu należy szukać pod innym adresem, podając go przy okazji w nagłówku Location. A co będzie, jeśli w ogóle nie podamy nagłówka Host? Przecież w wielu przypadkach (np. domowy RPi) na serwerze jest tylko jeden serwis... spróbujmy. Tym razem zamiast z Google (który trochę nietypowo traktuje błędy) połączymy się z serwerem Forbota: Jak widać - serwer traktuje to jako błąd. Ale co z protokołem HTTP/1.0? Wtedy przecież, w początkach Internetu, wystarczył adres IP... Spróbujmy! Widzimy więc, że tym razem serwer nie uznał naszego zapytania za błędne. Natomiast to, że nic konkretnego nam nie napisał oprócz tego, że działa - wynika z faktu, że bez nagłówka Host nie jest w stanie stwierdzić do którego serwisu wysłane jest zapytanie! I to na dziś tyle - w następnej części spróbujemy zrobić prosty serwer. Spis treści serii artykułów: Protokół HTTP w zastosowaniach IoT - część 1: trochę teorii Protokół HTTP w zastosowaniach IoT - część 2: budujemy serwer Protokół HTTP w zastosowaniach IoT - część 3: tworzymy klienta
Tablica liderów jest ustawiona na Warszawa/GMT+02:00
×
×
  • Utwórz nowe...