Skocz do zawartości

Tablica liderów


Popularna zawartość

Pokazuje zawartość z najwyższą reputacją od 06.04.2019 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. 13 punktów
    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
  3. 13 punktów
    W poprzednich odcinkach zobaczyliśmy jak działa program, który przesyła do wyświetlacza dane dla każdego piksela osobno, następnie przetestowaliśmy wersję z buforem dla całej pamięci obrazu. Pierwsza wersja działała bardzo wolno, ale zużywała mało pamięci RAM. Druga działała bardzo szybko, ale bufor zajmował ogromną jak na mikrokontroler ilość pamięci. Teraz spróbujemy przygotować wersję pośrednią - tym razem użyjemy mniej pamięci, ale więcej czasu procesora. Spis treści: Sterowanie wyświetlaczem TFT - część 1 - wstęp, podstawowe informacje Sterowanie wyświetlaczem TFT - część 2 - analiza problemu Sterowanie wyświetlaczem TFT - część 3 - testy prędkości na STM32 Sterowanie wyświetlaczem TFT - część 4 - własny program Sterowanie wyświetlaczem TFT - część 5 - optymalizacja programu Obliczanie danych obrazu Pełny bufor obrazu jak pamiętamy zajmuje 160 x 128 x 2 = 40960 bajtów pamięci. Takie rozwiązanie zapewniło nam możliwość szybkiego tworzenia grafiki w pełnej rozdzielczości oraz 65 tysiącach kolorów. Jednak w wielu zastosowaniach wystarczyłaby nieco mniejsze możliwości, przykładowo gdybyśmy zamiast 16-bitów zastosowali 8, uzyskalibyśmy 256 kolorów, a jednocześnie zmniejszyli zużycie pamięci o połowę. W wielu przypadkach nawet 16 kolorów, czyli 4 bity mogłyby wystarczyć, a jak łatwo policzyć bufor zajmowałby wówczas 10240 bajtów. 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. Podobnie z rozdzielczością, jeśli tworzymy np. mini konsolę do grania, tryb 80x64 mógłby nam wystarczyć, nadałby nawet nieco stylu "retro". Ogólna idea jest więc taka, że spróbujemy przechowywać mniejszą ilość danych, a następnie przeliczać je na reprezentację oczekiwaną przez wyświetlacz dopiero przed wysłaniem. Tryb z paletą kolorów Metod generowania obrazu jest mnóstwo, ja spróbuję przedstawić bardzo prosty, czyli użycie 8-bitowej palety. Bufor obrazu będzie wyglądał podobnie jak wcześniej, ale zamiast typu uint16_t użyjemy uint8_t: uint8_t lcd_framebuffer[LCD_WIDTH * LCD_HEIGHT]; Jak łatwo policzyć zajmuje on teraz 160 x 128 = 20480 bajtów, czyli nadal sporo, ale zawsze można zastosować kolejne optymalizacje. Wyświetlacz oczekuje danych w postaci RGB565, czyli 16-bitowych wartości gdzie 5-bitów określa składową czerwoną, 6-zieloną, a 5-niebieską. Paleta to po prostu 256-elementowa tablica, która zawiera wartości opisujące dany kolor: uint16_t palette[256]; W docelowym programie moglibyśmy dobrać idealną paletę do naszego zastosowania i zapisać ją w pamięci flash (albo w samym programie). Jak wspomniałem tutaj prezentuję jedynie demo, więc paletę będę obliczać: Prawdę mówiąc takie rozwiązanie nie wyglądało najładniej, bo nie można w nim reprezentować bieli, więc zamiast uzupełniać zerami, uzupełniłem jedynkami - jak napisałem, to tylko demo. Bardzo prosty kod przeliczający 8-bitową paletę, na 16-bitowy kolor dla wyświetlacza wygląda następująco: static inline uint16_t palette2rgb(uint8_t color) { uint16_t r = ((uint16_t)color & 0xe0) << 8; uint16_t g = ((uint16_t)color & 0x1c) << 6; uint16_t b = ((uint16_t)color & 0x03) << 3; return __REV16(0x18e7 | r | g | b); } Ta magiczna wartość 0x18e7 to właśnie uzupełnienie jedynkami - wiem że to brzydki kod, z góry za niego przepraszam, ale to mało istotny fragment, zachęcam oczywiście do zastosowania o wiele lepszych rozwiązań. Teraz przechodzimy do najważniejszego, czyli przeliczania 8-bitowych danych w buforze, na docelowe 16-bitowe przeznaczone dla wyświetlacza. Przesyłanie po jednym bajcie nie działa wydajnie, więc utworzymy nieduży bufor tymczasowy, ja ustaliłem jego wielkość na 512 pikseli: #define TX_BUF_SIZE 512 static uint16_t tx_buf[TX_BUF_SIZE]; Skoro wyświetlacz ma rozdzielczość 160 x 128, więc jak łatwo policzyć będziemy ten bufor wypełniać i wysyłać 40 razy, aby przesłać cały obraz. Funkcja do wypełniania bufora wygląda następująco: static void fill_tx_buf(uint32_t part) { uint16_t *dest = tx_buf; uint8_t *src = lcd_framebuffer + part * TX_BUF_SIZE; for (uint32_t i = 0; i < TX_BUF_SIZE; i++) *dest++ = palette2rgb(*src++); } Jako parametr podajemy indeks bufora, czyli wartość 0..39, po jej wykonaniu bufor tx_buf będzie zawierał dane do przesłania. Teraz możemy napisać funkcję rysującą zawartość naszego ekranu: void lcd_copy(void) { lcd_cmd(ST7735S_RAMWR); HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET); for (uint32_t i = 0; i < LCD_WIDTH * LCD_HEIGHT / TX_BUF_SIZE; i++) { fill_tx_buf(i); HAL_SPI_Transmit(&hspi2, (uint8_t*)tx_buf, 2 * TX_BUF_SIZE, HAL_MAX_DELAY); } HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET); } Musimy jeszcze zmodyfikować naszą bibliotekę graficzną, tak żeby pracowała z 8-bitowymi kolorami, ale to właściwie kosmetyczna zmiana. Czas skompilować program: Zgodnie z oczekiwaniami zużycie pamięci RAM znacznie spadło i wynosi niecałe 23KB. Przetestujmy wydajność naszego programu: Jak widzimy rysowanie w lokalnym buforze zajmuje tyle samo czasu, czyli 7ms, natomiast kopiowanie trochę więcej niż poprzednio, bo 37ms zamiast 33ms. Warto przy okazji przetestować wydajność funkcji wypełniającej bufor, czyli fill_tx_buf: void lcd_test(void) { uint32_t start = HAL_GetTick(); for (uint32_t i = 0; i < 1000; i++) fill_tx_buf(i % (LCD_WIDTH * LCD_HEIGHT / TX_BUF_SIZE)); uint32_t end = HAL_GetTick(); printf("lcd_test: %ld us\r\n", end - start); } Wywołanie 1000 razy fill_tx_buf zajmuje 110ms, czyli jedno jej wywołanie ok. 110us. Jak pamiętamy używamy jej 40 razy, co zgadza się z pozostałymi pomiarami - trochę ponad 4ms zużyliśmy na obliczenia, ale zaoszczędziliśmy prawie 20KB pamięci. Użycie tablicy zamiast obliczeń pewnie pozwoliłoby skrócić ten czas, ale jak wspominałem, to tylko przykład. Nie będę wstawiał kolejnego filmu, bo już tyle razy widzieliśmy ekran testowy, że chyba każdy ma go dosyć. Czas udoskonalić nasz program. Użycie DMA W poprzedniej części korzystaliśmy z DMA, więc nowy program jest pod wieloma względami "gorszy". Użyjmy więc HAL_SPI_Transmit_DMA zamiast, HAL_SPI_Transmit. Procedura wysyłania będzie wyglądała następująco: void lcd_copy(void) { lcd_wait_ready(); lcd_busy = true; lcd_cmd(ST7735S_RAMWR); HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET); tx_part = 0; fill_tx_buf(tx_part++); HAL_SPI_Transmit_DMA(&hspi2, (uint8_t*)tx_buf, 2 * TX_BUF_SIZE); } Wypełniamy w niej bufor pierwszym fragmentem obrazu i rozpoczynamy wysyłanie. W zmiennej tx_part przechowujemy informację o numerze kolejnego fragmentu do wysłania. Musimy teraz obsłużyć przerwanie informujące o zakończeniu transmisji i w nim przygotować dane dla następnego fragmentu, albo zakończyć całą operację: void lcd_copy_done(void) { if (tx_part < LCD_WIDTH * LCD_HEIGHT / TX_BUF_SIZE) { fill_tx_buf(tx_part++); HAL_SPI_Transmit_DMA(&hspi2, (uint8_t*)tx_buf, 2 * TX_BUF_SIZE); } else { HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET); lcd_busy = false; } } Po uruchomieniu zobaczymy, że program działa tak samo jak poprzednio. Różnica jest jednak taka, że podczas transmisji przez DMA procesor może wykonywać inne zadania. Jednak czas kopiowania obrazu nadal wynosi 37ms. Jeszcze jedna ważna uwaga - w przerwaniu generujemy dane dla kolejnego bufora, więc na 110us blokujemy przerwanie. Długie procedury obsługi przerwań to nic dobrego, ale STM32 pozwala na szczęście na ustawienie priorytetów przerwań. Możemy więc przerwaniu od DMA nadać niski priorytet i dzięki temu nasze obliczenia nie będą opóźniały innych, pilniejszych zadań: Użycie podwójnego bufora Jeśli podłączymy analizator logiczny to zobaczymy, że komunikacja z wyświetlaczem ma "przerwy". Wynika to stąd, że gdy obliczamy dane dla kolejnego fragmentu ekranu komunikacja jest zatrzymywana. Możemy trochę skomplikować nasz program, ale jednocześnie przyspieszyć działanie. Tym razem użyjemy dwóch buforów, albo raczej jednego większego. Gdy DMA będzie wysyłało jedną część danych, będziemy mieli czas na przygotowanie następnej. Deklarujemy więc większy bufor: #define TX_BUF_SIZE 512 static uint16_t tx_buf[TX_BUF_SIZE * 2]; Funkcja wypełniania bufora będzie teraz zapisywać parzyste fragmenty w pierwszej części tx_buf, a nie nieparzyste w drugiej: static void fill_tx_buf(uint32_t part) { uint16_t *dest = (part % 2 == 0) ? tx_buf : tx_buf + TX_BUF_SIZE; uint8_t *src = lcd_framebuffer + part * TX_BUF_SIZE; for (uint32_t i = 0; i < TX_BUF_SIZE; i++) *dest++ = palette2rgb(*src++); } Przed rozpoczęciem transmisji wypełnimy nie jeden, ale dwa bufory: void lcd_copy(void) { lcd_wait_ready(); lcd_busy = true; lcd_cmd(ST7735S_RAMWR); HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET); tx_part = 0; fill_tx_buf(tx_part++); fill_tx_buf(tx_part++); HAL_SPI_Transmit_DMA(&hspi2, (uint8_t*)tx_buf, 4 * TX_BUF_SIZE); } Ostatnia zmiana to obsługa nowego przerwania, które będzie wywoływane po przesłaniu połowy danych: void HAL_SPI_TxHalfCpltCallback(SPI_HandleTypeDef *hspi) { lcd_copy_halfdone(); } Gdy otrzymamy to przerwanie, będziemy po prostu wypełniać następny bufor: void lcd_copy_halfdone(void) { fill_tx_buf(tx_part++); } Natomiast procedura obsługi końca transmisji prawie się nie zmieniła, jedyna różnica to wielkość bufora: void lcd_copy_done(void) { if (tx_part < LCD_WIDTH * LCD_HEIGHT / TX_BUF_SIZE) { HAL_SPI_Transmit_DMA(&hspi2, (uint8_t*)tx_buf, 4 * TX_BUF_SIZE); fill_tx_buf(tx_part++); } else { HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET); lcd_busy = false; } } Program jest nieco bardziej skomplikowany, ale w nagrodę otrzymaliśmy czas kopiowania identyczny jak w wersji z poprzedniego odcinka, ale zużyliśmy mniej pamięci: Na zakończenie jeszcze mały przykład wykorzystania naszego nowego programu. Program demonstracyjny Jakiś czas temu na forum pojawił się wątek z pytaniem o sposób wyświetlania kołowego progres baru. Pytanie sprowokowało ciekawą dyskusję na temat możliwości wydajnej realizacji takiego zadania. Skoro mamy opanowane sterowanie wyświetlacza TFT, możemy spróbować narysować nieco podobny element, a przy okazji sprawdzić jak nasza "biblioteka" sprawdzi się w realnym przykładzie. Zacznijmy od małej powtórki z matematyki oraz pewnego uproszczenia. Dla ułatwienia rysujmy tylko połowę progres-bara, zawsze możemy później rozbudować program. Rysowanie pionowych linii jest na ogół dość szybką operacją, więc zastanówmy się jak narysować wykres pionowymi (albo poziomymi) liniami. Kąt alfa oraz promienie r1 i r2 to nasze dane. Ja wybrałem rysowanie pionowych linii, więc x będzie zmienną. Wszyscy pamiętamy z matematyki wzór okręgu: x2 + y2 = r2. Po karkołomnych przejściach matematycznych uzyskujemy więc: y1 = sqrt(r1 - x2) y3 = sqrt(r2 - x2) Funkcja sqrt to pierwiastek. Jeśli ktoś jest miłośnikiem optymalizacji to wartości y1 i y2 może raz policzyć i trzymać w tablicy (najlepiej w pamięci Flash). Jak wspominałem program to demo, więc na razie nie będzie aż tak optymalny. Zostaje jeszcze obliczenie y2. Jest to odrobinę trudniejsze, może wymagać szybkiej powtórki z trygonometrii, a jak pamiętamy tg(alpha) = y/x, więc: y2 = x * tg(alpha) Tutaj znowu możemy tablicować wartości, ale na początek zostawmy prostą wersję. Mamy więc dla każdego x obliczone y1, y2 i y3. Teraz wystarczy sprawdzić jak y2 ma się do pozostałych i są możliwe 3 przypadki: jeśli y2 <= y1 to nic nie rysujemy jeśli y2 >= y3 to rysujemy "pełną" linię od y1 do y3 a jeśli y1 < y2 < y3 to linię od y1 do y2 Rysowanie możemy więc wykonać następującym programem: void draw_bar(uint32_t r1, uint32_t r2, uint32_t alpha) { float t = tan(alpha * M_PI / 180.0); bar_circle(0, 0, r1, WHITE); bar_circle(0, 0, r2, WHITE); bar_line(0, 0, r1 * cos(alpha * M_PI / 180.0) * 0.8f, r1 * sin(alpha * M_PI / 180.0) * 0.8f, WHITE); for (uint32_t x = 0; x <= r2; x++) { uint32_t y1 = (x < r1) ? sqrt(r1 * r1 - x * x) : 0; uint32_t y2 = sqrt(r2 * r2 - x * x); uint32_t y3 = x * t; if (y3 > y2) bar_line(x, y1, x, y2, WHITE); else if (y3 > y1) bar_line(x, y1, x, y3, WHITE); } } Funkcje bar_circle i bar_line są dodane aby "przenieść" nasz początek współrzędnych w odpowiednie miejsce: static void bar_circle(uint32_t x, uint32_t y, uint16_t r, uint8_t color) { lcd_circle(LCD_WIDTH - 1 - x, LCD_HEIGHT - 1 - y, r, color); } static void bar_line(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2, uint8_t color) { lcd_line(LCD_WIDTH - 1 - x1, LCD_HEIGHT - 1 - y1, LCD_WIDTH - 1 - x2, LCD_HEIGHT - 1 - y2, color); } Dodajmy jeszcze "wskazówkę", pomiar czasu działania oraz wyświetlanie wartości: void draw_test_screen(uint32_t value) { char buf[32]; uint32_t start = HAL_GetTick(); lcd_clear(BLUE); draw_bar(117, 127, value); sprintf(buf, "%ld", value); lcd_fill_rect(110, 90, 150, 120, BLACK); lcd_draw_string(120, 100, buf, &Font16, WHITE, BLACK); lcd_copy(); uint32_t time = HAL_GetTick() - start; printf("drawing: %lu ms\r\n", time); } Teraz program jest gotowy: Nawet bez tablicowania wartości i z arytmetyką zmiennopozycyjną rysowanie zajmuje ok 22ms. Kopiowanie to 33ms, mamy więc prawie 20 klatek na sekundę, ale pewnie dałoby się więcej. Podsumowanie Początkowo planowałem napisanie jednego, może dwóch artykułów odnośnie sterowania wyświetlaczem TFT. Okazało się jednak, że temat jest o wiele obszerniejszy i ciekawszy, a 5 części to właściwie dopiero wstęp. Mam nadzieję, że udało mi się pokazać jak można sterować wyświetlaczem kolorowym oraz zachęcić do własnych eksperymentów i udoskonalania zaprezentowanych rozwiązań. Spis treści: Sterowanie wyświetlaczem TFT - część 1 - wstęp, podstawowe informacje Sterowanie wyświetlaczem TFT - część 2 - analiza problemu Sterowanie wyświetlaczem TFT - część 3 - testy prędkości na STM32 Sterowanie wyświetlaczem TFT - część 4 - własny program Sterowanie wyświetlaczem TFT - część 5 - optymalizacja programu
  4. 13 punktów
    Większość osób myśląc o cyfrowej telekomunikacji, myśli o współczesne mikroelektronice: smartfonach, komputerach i Internecie. Nie każdy zdaje sobie sprawę z tego, że idea przesylania informacji zakodowanej w ciągach zer i jedynek jest dużo, dużo starsza. Pierwszym urządzeniem tego rodzaju był telegraf skonstruowany przez Emille'a Baudot w latach siedemdziesiątych XIX wieku. Urządzenie po stronie nadajnika posiadało klawiaturę złożoną z pięciu przycisków, które operator musiał wciskać w różnych kombinacjach. Urządzenie wysyłało na linię ciąg impulsów, odpowiadających wciśniętej kombinacji. Odbiornik interpretował ten sygnał drukując na papierowej taśmie odpowiednią literę lub cyfrę, w jej naturalnej formie. Na początku XX wieku idea to została udoskonalona. Nadajnik został wyposażony w klawiaturę podobną do tych stosowanych w maszynach do pisania. Tak narodził się dalekopis. Transmisja danych pomiędzy tymi urządzeniami przypominała standardowy interfejs szeregowy. Z tą różnicą, że z przeciwieństwie do TTL UART-a czy RS323 poszczególne stany logiczne nie były kodowane przez wartości napięć, ale przez fakt przepływu (bądź nie) prądu w obwodzie. Normalnie przez linię płynął prąd o ustalonej wartości (zazwyczaj 60, 40 lub 20 mA). To był stan domyślny. Rozpoczęcie nadawania kolejnego znaku wymagało nadania bitu startu, czyli chwilowego przerwania obwodu. Potem nadajnik oczekiwał na pięć bitów z kodem znaku w alfabecie Baudot (zwanym także międzynarodowym alfabetem telegraficznym). Na końcu pojawiały się bity stopu w trakcie których odebrany znak był drukowany na papierze. Ktoś zapewne zauważył już, że używając pięciu bitów można było zakodować maksymalnie 32 znaki - zdecydowanie za mało, aby pomieścić wszystkie litery alfabetu, cyfry i znaki interpunkcyjne. To prawda. Stosowano pewną sztuczkę - dwie kombinacje bitów były zarezerwowane do przełączania pomiędzy dwoma rejestrami zawierającymi litery oraz cyfry i inne znaki. Ne stosowano także rozróżnienia na male i duże litery. Dalekopis chociaż w pełni cyfrowy, był urządzeniem elektromechanicznym, radzącym sobie bez pojedynczego tranzystora (chociaż oczywiście w latach osiemdziesiątych i dziewięćdziesiątych produkowano nowoczesne, elektroniczne wersje). Dalekopisy były powszechnie używane do przesyłania wiadomości przez wojsko i państwowe służby. Poczta wykorzystywała je do transmisji telegramów. Stosowano je także w roli terminali komputerowych, przed pojawieniem się monitorów CRT. Ślad tej zaszłości historycznej zachował się w nomenklaturze stosowanej w systemach uniksowych, gdzie terminal jest oznaczany skrótem TTY - od angielskiego słowa "teletype", czyli właśnie dalekopis. Przepraszam za ten przydługi wstęp, nie byłem jednak pewien, czy wszyscy będą jeszcze kojarzyć o jakie urządzenie chodzi... Przechodząc do sedna sprawy. Na co dzień pracuję w krakowskim Muzeum Inżynierii Miejskiej. Jakiś czas temu został nam przekazany dalekopis T100, wyprodukowany w latach siedemdziesiątych w Czechoslowacji, na licencji Siemensa. Ponieważ posiadaliśmy już taki eksponat, zostałem poproszony o sprawdzenie możliwości uruchomienia go i wykorzystywania w roli interaktywnej instalacji, zamiast "martwego" eksponatu ukrytego w muzealnej gablocie. Tak rozpoczęły się moje eksperymenty. Najpierw skonstruowałem prosty interfejs USB, oparty na starym mikrokontrolerze AT89C2051 i układzie FTDI. Do generowania pętli prądowej 40 mA używałem zestawu kilku baterii 9V oraz rezystorów o dużej mocy. Komunikacja z dalekopisem ruszyła bez problemu - pojawiła się jednak inna trudność. Okazało się, że uszkodzony jest moduł wzywaka - urządzenia odpowiedzialnego m.in. za zdalne włączanie silnika dalekopisu przy połączeniu przychodzącym, sygnalizowanym odwróceniem kierunku przepływu prądu w linii. Naprawa tego modułu okazała się bardziej skomplikowana niż początkowo sądziłem, ostatecznie postanowiłem więc wymontować wadliwą część i sterować silnikiem za pomocą przekaźnika, zamontowanego w moim interfejsie. Finalna wersja interfejsu zawiera mikrokontroler PIC32MX270F256B oraz moduł GSM SIM800L. Wykorzystałem także 2MB pamięć SPI flash, do wykonywania elektronicznej kopii przychodzących wiadomości. W osobnej obudowie znajduje się generator pętli prądowej, złożony z zasilacza transformatorowego oraz zestawu kondensatorów. Całość można obecnie oglądać na wystawie "Uwaga! Nieprzyjaciel podsłuchuje." w Muzeum Inżynierii Miejskiej w Krakowie. Po wysłaniu SMS-a na podany numer można oglądać dalekopis podczas pracy.
  5. 12 punktów
    Ech, prezenty, prezenty... Taki byłem zadowolony, że prezent dla synka skończę przez święta i będę mógł zająć się swoimi robocikami. A tu guzik: okazało się, że siostra weszła w posiadanie kota (czy odwrotnie, z kotami to różnie bywa) i jakiś prezent trzeba zrobić. Wyszło mi na to, że najszybciej będzie zrobić taką laserową latawicę za którą lata kot - bo i na instructables są kompletne projekty, i na thingiverse gotowe STL-e, a i na naszym Forbocie ktoś coś ostatnio publikował... niewiele myśląc obiecałem, że taką zabawkę zrobię i kończąc świąteczną wizytę udałem się do domu (drugi koniec Polski). Po przyjeździe okazało się, że: Forbotowy projekt (LaserCat) jest mi absolutnie do niczego nie przydatny - zero konkretów, nawet kawałka kodu nie ma żeby sobie zerżnąć Projekty z instructables są jakieś takie uproszczone i nie pasują mi do niczego (oprócz podpatrzenia na filmikach na YT jak lata mucha) Jedyne na czym mogę się wzorować (ale tylko wzorować) to zawieszenie lasera z thingiverse. Postanowiłem więc opracować sobie jakieś wstępne założenia (jak zwykle w maksymalnym stopniu używając części z szuflady). Wyszło mi coś takiego: Mikrokontroler - moduł Arduino Pro Mini; Sterowanie - żadnych błękitnych ząbków, super wypasionych aplikacji na komórkę, serwerów http i wifi, ma być najprostszy pilot na podczerwień plus klawisz START na obudowie; Zasilanie z akumulatora; Dwa tanie serwa SG90 jako napęd; Możliwość łatwego zaprogramowania obszaru, po którym ma latać mucha. Obudowę chciałem początkowo wydrukować w całości, ale okazało się, że leży mi i zawala miejsce nówka Kradex Z-5 - oczywiste było więc jej wykorzystanie. Zacząłem od mechanizmu pan-tilt. Ten z thingiverse nie podobał mi się z dwóch powodów: po pierwsze oba serwa były w nim ruchome, podczas gdy wystarczałoby tylko jedno, po drugie wydruk wymagał za dużo wysokich podpór. Ponieważ i tak musiałem czekać na zamówione kilka części (nie miałem np. diody laserowej ani niepotrzebnego pilota), postanowiłem przeznaczyć ten czas na zaprojektowanie i sprawdzenie mechanizmu. Przede wszystkim postanowiłem wydrukować oddzielnie obejmę diody i mocowanie do serwa. Pozwoliło mi to na pozbycie się niepotrzebnych podpór przy druku, a jednocześnie dało większą możliwość jakichś manipulacji przy ewentualnym błędzie (jak się okazało - sprawdziło się to, ale o tym później). Dodatkowo chciałem tam zrobić jakieś miejsce na przewody (do diody i serwa), a przy okazji zrobić ten element nieco bardziej uniwersalnym - czyli z możliwością poprowadzenia przewodów z jednej lub drugiej strony. Dodatkowo niepotrzebny okazał się główny element mocujący - ponieważ serwo poziomu jest nieruchome, przymocowane zostało bezpośrednio do obudowy. Tak więc mechanizm działa w ten sposób: Oto zestaw elementów potrzebnych do złożenia całości (bez wkrętów i serw) oraz zmontowany mechanizm: Jak widać, orczyk musiał być przycięty tak, aby zmieścił się w przygotowanym rowku w uchwycie. Jeden z wkrętów mocujących orczyk do uchwytu (użyłem oryginalnych wkrętów dołączonych do serwa) - ten na krótszym ramieniu - musiał być również lekko przycięty, inaczej zawadzałby o obudowę serwa. Najlepiej po prostu skrócić oba wkręty i wkręcić je od wewnątrz (od strony orczyka) tak, aby nie wystawały poza obejmę. Oczywiście otwory w orczyku należy rozwiercić tak, aby wkręt się zmieścił! W załączniku STL-e i plik OpenSCAD-a: LaserFly.zip UWAGA! Co prawda oficjalna stabilna wersja OpenSCAD-a wystarcza do otwarcia pliku i wygenerowania STL-i, ale do działania customizera wymagana jest wersja co najmniej 2019.05! Teraz przyszła kolej na stworzenie schematu zabawki (co w rzeczywistości sprowdzało się do rozstrzygnięcia, które biblioteki gryzą się ze sobą i co podłączyć do którego pinu). Jako że biblioteki Servo i IRremote używają timerów (uniemożliwiając działanie PWM na niektórych pinach) a chciałem jednak mieć możliwość regulacji świecenia diody - wyszło mi coś takiego: I tu od razu uwaga: kondensator C1 został dodany w czasie eksperymentów z ustaleniem przyczyny niedziałania serwa. Najprawdopodobniej nie jest potrzebny - ale nie chciało mi się go już wyciągać Jako że w międzyczasie doszły zamówione brakujące części, mogłem zabrać się za zmontowanie całego urządzenia i pisanie programu. Przede wszystkim stwierdziłem, że obudowa Z-5 to jakiś wynalazek diabła; sterczący pośrodku jakiś szpindel ze śrubką wielce skutecznie blokuje możliwość zamontowania tam czegokolwiek, co jest większe od pudełka po zapałkach i nie ma dziury w środku. Na szczęście największy element (koszyk na akumulator) udało mi się tam upchnąć, reszta była już prosta. Dwa uchwyty mocowane do spodu obudowy śrubami od nóżek służą do utrzymania przetwornicy oraz modułu ładowarki (trzeci element niewidoczny na zdjęciu to podkładka pod ładowarkę, utrzymująca ją na właściwej wysokości koniecznej dla prawidłowego dostępu do gniazda USB): I tu kolejna uwaga: Moduły ładowarki różnych producentów mają różne wymiary (a nawet kształty płytki) - warto to sprawdzić i ew. poprawić moduł rholder w pliku OpenSCAD-a! Arduino i gniazda połączeniowe umieściłem po prostu na kawałku płytki uniwersalnej przykręconej do jednego z uchwytów. Koszyk akumulatora jest przykręcony do podstawki tak, aby możliwe było przeprowadzenie przewodów między koszykiem a podstawką, a ta z kolei skręcona jest z uchwytami. Zmontowana całość wygląda tak: Jak widać, mikroswitche są przylutowane znów do kawałka płytki uniwersalnej a ta przykręcona do ściany obudowy - to chyba najszybszy, a jednocześnie niezawodny sposób na w miarę estetyczne klawisze... Po złożeniu wszystkiego całość przedstawia się następująco: Jak widać, akumulator zamocowany jest dodatkowo opaską zaciskową. Nie jest to absolutnie niezbędne (koszyk tego typu z blaszkami zapewnia zarówno niezły styk, jak i pewne zamocowanie akumulatora) ale urządzenie miało przed sobą podróż w paczce - a wolałem nie sprawdzać, czy akumulator przypadkiem nie wypadnie w transporcie (czort jeden wie co oni tam z tymi paczkami robią, na pewno nic przyjemnego). W międzyczasie oczywiście powstawał program (w załączniku). W tym przypadku jest on dość prosty - pozwala na zaprogramowanie za pomocą pilota obszaru ruchu "muchy", punktu zerowego oraz regulację sygnału PWM diody laserowej. Jedna tylko uwaga: kod funkcji getKey dostosowany jest do konkretnego pilota z kodowaniem NEC, w razie użycia innego fragment funkcji odpowiedzialny za odczyt pilota musi byc przerobiony! Na tylnej ścianie urządzenia umieszczone są kolejno: dioda sygnalizująca i przycisk AUTO (włączający program ruchu) dioda sygnalizująca i przycisk PROG (włączający tryb programowania) dioda kontrolna, wyłącznik urządzenia oraz gniazdo USB ładowarki Odbiornik IR umieściłem w czymś w rodzaju obrotowej wieżyczki - nie wiedziałem w jakiej pozycji będzie używana zabawka a znając prawa Murphy'ego gdzie bym go nie umieścił to by akurat patrzył w odwrotną stronę Jako że nie mam możliwości umieszczenia filmiku z interakcją z kotem: krótka demonstracja działania urządzenia (ruch muchy po podłodze) oraz zapewnienie siostry, że kotu się spodobało - muszą wystarczyć I to by było na tyle. Tym razem nie liczę na mnóstwo komentarzy, ale byłoby mi miło, gdyby ktoś wykorzystał mój projekt choćby częściowo.
  6. 11 punktów
    Wyświetlacze stosowane w urządzeniach elektronicznych przeszły ogromną ewolucję. Można ją łatwo prześledzić chociażby na przykładzie telefonów komórkowych. Pierwsze modele miały monochromatyczne, często tekstowe wyświetlacze. W latach 90. ubiegłego wieku popularne były już wyświetlacze graficzne (oraz gra w węża). W kolejnych latach wyświetlacze monochromatyczne zostały prawie zupełnie wyparte przez modele z kolorową, aktywną matrycą, czyli popularne TFT. Spis treści: Sterowanie wyświetlaczem TFT - część 1 - wstęp, podstawowe informacje Sterowanie wyświetlaczem TFT - część 2 - analiza problemu Sterowanie wyświetlaczem TFT - część 3 - testy prędkości na STM32 Sterowanie wyświetlaczem TFT - część 4 - własny program Sterowanie wyświetlaczem TFT - część 5 - optymalizacja programu W przypadku urządzeń wbudowanych, wiele nowości dociera ze znacznym opóźnieniem. Podczas kursu Arduino wykorzystany został alfanumeryczny wyświetlacz 2x16 znaków, kursy STM32 F1 oraz STM32 F4 wykorzystywały graficzne, ale nadal monochromatyczne wyświetlacze (chociaż F4 o wiele nowocześniejszy OLED). Celem niniejszego artykułu jest pokazanie jak można we własnym projekcie wykorzystać kolorowy wyświetlacz TFT. Będzie to okazja do pokazania zarówno wad, jaki i zalet tego typu wyświetlaczy oraz podstawowych technik optymalizacji. 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. Wybór wyświetlacza Jednym z dość istotnych powodów powolnego wzrostu zainteresowania wyświetlaczami TFT była ich dość wysoka cena (w szczególności w porównaniu ze starszymi modelami monochromatycznymi). Obecnie ceny wyświetlaczy TFT bardzo spadły i model o niewielkiej przekątnej można kupić za podobną cenę do starszych urządzeń. Jako przykład użyję wyświetlacza o przekątnej 1.8 cala i rozdzielczości 160 na 128 pikseli firmy WaveShare. Dokumentację wyświetlacza, użytego sterownika oraz przykładowe programy znajdziemy na stronie producenta: https://www.waveshare.com/wiki/1.8inch_LCD_Module Również z tej strony pochodzi zdjęcie modułu wyświetlacza: Jak widzimy na zdjęciu jest to kompletny moduł. Na stronie producenta znajdziemy zarówno instrukcję obsługi, jak i schemat, jednak jest on tak prosty, że raczej niezbyt interesujący. To na co powinniśmy zwrócić uwagę to opis wyprowadzeń: Kolejny ważny parametr to model kontrolera, czyli ST7735S oraz jego dokumentacja. Wszystkie wspomniane dokumenty warto oczywiście przeczytać. Ale jeśli nie mamy akurat czasu na czytanie nieco ponad 200 stron, powinniśmy chociaż pamiętać gdzie szukać informacji. Podłączenie wyświetlacza Czas wybrać mikrokontroler i podłączyć do niego wyświetlacz. Przykłady dostarczone przez WaveShare są przeznaczone dla Arduino UNO, STM32F103, Raspberry Pi oraz Jetson Nano. Sensowność podłączania tak małego wyświetlacza do potężnego komputera jakim jest Raspberry Pi (o Jetson Nano nawet nie wspominając) jest mocno dyskusyjna więc ograniczę się do Arduino oraz STM32. Na początek Arduino UNO, bo to chyba najlepiej znana wszystkim platforma. Oczywiście wiele osób pewnie stwierdzi, że układ atmega328 jest o wiele za słaby na sterowanie kolorowych wyświetlaczem, ale skoro producent dostarczył gotowe przykłady warto chociaż spróbować. Sam interfejs jest opisywany jako SPI, ale szybkie spojrzenie na listę wyprowadzeń może nieco zaskoczyć. Po pierwsze używane są nieco inne nazwy, po drugie komunikacja jest jednokierunkowa (można tylko wysyłać dane do wyświetlacza), a po trzecie wreszcie jest sporo linii, które nie są obecne w standardowym interfejsie SPI. Musimy również zadbać o zasilanie układu z napięcia 3.3V oraz konwersję napięć sterujących - Arduino UNO używa sygnałów o napięciu 5V, co może uszkodzić wyświetlacz TFT. Do podłączenia użyłem modułu opartego o układ 74LVC245, można to zrobić taniej, lepiej itd. ale akurat miałem taki moduł pod ręką. Krótki opis wyprowadzeń wyświetlacza: 3V3, GND - zasilanie DIN - linia danych, podłączamy do MOSI interfejsu SPI CLK - zegar interfejsu SPI CS - odpowiada linii CS interfejsu SPI DC - informuje o rodzaju przesyłanych danych, stan niski oznacza komendę sterującą, a wysoki dane RST - stan niski resetuje sterownik wyświetlacza BL - sterowanie podświetlaniem Podłączenie do Arduino UNO jest dość proste, linie interfejsu SPI, czyli DIN i CLK należy podłączyć do pinów zapewniających dostęp do sprzętowego modułu SPI, pozostałe linie są sterowane programowo (chociaż BL można podłączyć do wyjścia PWM, aby uzyskać sterowanie jasnością podświetlenia). W programie przykładowym użyte wyprowadzenia są zdefiniowane w pliku o nazwie DEV_config.h, powinniśmy więc ustawić je odpowiednio do wybranego sposobu podłączenia. U mnie ten kod wygląda następująco: //GPIO config //LCD #define LCD_CS 7 #define LCD_CS_0 digitalWrite(LCD_CS, LOW) #define LCD_CS_1 digitalWrite(LCD_CS, HIGH) #define LCD_RST 5 #define LCD_RST_0 digitalWrite(LCD_RST, LOW) #define LCD_RST_1 digitalWrite(LCD_RST, HIGH) #define LCD_DC 6 #define LCD_DC_0 digitalWrite(LCD_DC, LOW) #define LCD_DC_1 digitalWrite(LCD_DC, HIGH) #define LCD_BL 4 #define LCD_BL_0 digitalWrite(LCD_DC, LOW) #define LCD_BL_1 digitalWrite(LCD_DC, HIGH) #define SPI_Write_Byte(__DATA) SPI.transfer(__DATA) Pierwsze uruchomienie Czas skompilować i uruchomić program przykładowy. Po drobnych poprawkach oraz ustawieniu wybranych pinów powinno udać się program skompilować. Warto zwrócić uwagę na ilość użytej pamięci: Jak widzimy wykorzystane zostało raptem 9348 bajtów pamięci Flash (28%) oraz 453 bajtów pamięci RAM (22%). To bardzo mało i jak później się przekonamy to jeden z powodów "niedoskonałości" tego rozwiązania. Ale na razie czas zaprogramować Arduino i podłączyć zasilanie: Dobra wiadomość jest taka że działa - i to w wysokiej rozdzielczości (160x128) oraz w kolorze... Zła jest taka, że może "niezbyt szybko" działa. Zanim stwierdzimy, że wyświetlacz do niczego się nie nadaje, warto nieco dokładniej przyjrzeć się przyczynom takich, a nie innych osiągów. Dzięki temu może uda się działanie programu nieco poprawić. Czas rysowania zawartości ekranu Pomiary "na oko" to niezbyt doskonała metoda, nieco lepiej dodać wywołanie millis() przed rozpoczęciem i po zakończeniu rysowania ekranu. Różnica między zwróconymi czasami pozwoli nam oszacować jak szybko (albo wolno) działa pierwszy program. Przy okazji możemy jeszcze sprawdzić ile zajmuje skasowanie zawartości ekranu. Nowy program w pętli rysuje i kasuje zawartość ekranu: Wyniki pomiarów to ok. 1240 ms dla rysowania oraz 96 ms dla kasowania zawartości. "Not great, not terrible" chciałoby się powiedzieć... Prosta animacja Wyświetlanie ekranu demonstracyjnego jest oczywiście interesujące, ale może warto sprawdzić jak ekran zachowa się w nieco bardziej pasjonującym zastosowaniu. Zacznijmy od odbijającej się piłeczki, taki wstęp do napisania gry w "ponga". Pierwsze podejście jest mocno uproszczone - w pętli głównej kasujemy zawartość ekranu, obliczamy pozycję piłki i rysujemy: Jak widzimy tempo gry jest raczej spokojne, a miganie irytujące. Warto jeszcze przetestować ile czasu zajmuje naszemu programowi rysowanie: Wyszło trochę ponad 100ms, to nieco więcej niż czas samego kasowania ekranu, więc jak łatwo się domyślić właśnie ta czynność zajmuje najwięcej czasu. Optymalizacja Skoro już wiemy, że najwięcej czasu zajmuje kasowanie ekranu, to może zamiast kasować wszystko warto usuwać jedynie poprzednią pozycję "piłki" i rysować nową. Taki program działa już znacznie lepiej: Porównajmy jeszcze czasy rysowania: Jak widzimy skasowanie i narysowanie piłeczki zajmuje o wiele mniej czasu. Nieco jednak uprzedzając dalsze części, spróbujmy jeszcze zamiast okrągłej piłki użyć kwadratowej. Może nie brzmi to zachęcająco, ale efekt robi wrażenie: Podsumowanie To co zobaczyliśmy to pierwsze możliwości optymalizacji. Nawet jeśli odrysowanie całego ekranu zajmuje mnóstwo czasu, możemy uzyskać o wiele lepsze efekty zmieniając sposób rysowania. To jedna z metod optymalizacji - nie jedyna i w kolejnej części postaram się opisać dlaczego rysowanie działa tak wolno i o ile możemy poprawić wydajność. Spis treści: Sterowanie wyświetlaczem TFT - część 1 - wstęp, podstawowe informacje Sterowanie wyświetlaczem TFT - część 2 - analiza problemu Sterowanie wyświetlaczem TFT - część 3 - testy prędkości na STM32 Sterowanie wyświetlaczem TFT - część 4 - własny program Sterowanie wyświetlaczem TFT - część 5 - optymalizacja programu
  7. 11 punktów
    Interaktywna zabawka dla kotów gwarantująca zabawę w każdej chwili, żaden kot nie oprze się uciekającej czerwonej kropce. Jest to niewielkie pudełeczko z wbudowanym modułem wifi i banalnie prostą obsługą. Główne funkcje: sterowanie dowolnym urządzeniem z przeglądarką internetową. losowe ruchy lasera o zmiennej prędkości. ustawianie czasu jak długo ma działać. ustawianie harmonogramów automatycznego włączenia. regulacja jasności lasera. regulacja zakresu ruchu i prędkości lasera. możliwość sterowania z dowolnego miejsca na świecie przez internet. sterowanie za pomocą google asystenta. prosta konfiguracja. Zabawka może być zasilana dowolną ładowarką od telefonu, może to być również powerbank. Przy pierwszym uruchomieniu zabawki, zostanie uruchomiona nowa sieć wifi ..::LASERCAT::.. wystarczy połączyć się z nią i wskazać naszą sieć domową, a po zrestartowaniu urządzenie automatycznie podłączy się do niej i już możemy korzystać z zabawki. Z tyłu znajduje się wejście zasilania micro USB, jak w telefonie oraz przycisk. Krótkie wciśnięcie to włączenie/wyłączenie lasera, przytrzymanie przez 3 sek. powoduje rozłączenie obecnej sieci wifi i uruchomienie ponownej konfiguracji. Gdy urządzenie jest już podłączone do naszej sieci wifi to po wpisaniu adresu zabawki w przeglądarce internetowej zobaczymy panel sterujący: Zastosowany laser jest małej mocy, taki sam jak w innych tego typu zabawkach czy bazarkowych wskaźnikach. Dodatkowo dla bezpieczeństwa jest możliwość ustawienia mocy świecenia lasera od 0% do 100%. Pozostałe ustawienia pozwolą dostosować zakres ruchów do miejsca w którym znajduje się zabawka i określić czy kropka ma się poruszać tylko po podłodze, czy częściowo wchodzić na ścianę co może dostarczyć dodatkowej frajdy dla kota. Schemat jest bardzo prosty: Widok płytki PCB: Jak zwykle w garażowym zaciszu metodą "żelazkową" - elektronicy używają żelazka zdecydowanie częściej jak ich partnerki - powstaje mała płytka. Płytka została zabezpieczona przed utlenianiem lakierem PVB16. Całą robotę wykonuje tutaj tani i lubiany układ ESP8266, który posiada moduł WiFi. Dioda laserowa jest zasilana źródłem prądowym dodatkowo kluczowanym z PWM-a co pozwala płynnie regulować jasność od 0% do 100%. Skoro już bebechy mam, to teraz trzeba to wszystko złożyć w całość. Obudowę wykonałem ze sklejki wyciętej laserowo, składanej na wczepy palcowe. No to składamy: Dodanie serwomechanizmów do których przyczepiony jest laser. Oczywiście bez trytytki projekt by się nie udał No i sprzęt jest gotowy, ale co nam po sprzęcie jak on zupełnie nie wie co ma robić? Nie wie, że teraz trzeba machać tym laserkiem tak żeby kot ganiał w tę i we w tę Trzeba to wszystko zaprogramować. Uruchamiamy nasze ulubione IDE czyli Visual Studio Code z wtyczką PlatformIO i zaczynamy pisać program. Soft został napisany z wykorzystaniem Arduino Core, a na całość składa się kilka części: główny program sterujący silniczkami, wyznaczanie losowych ścieżek. serwer www, który udostępnia ładny panel sterowania. konfiguracja sieci WiFi z wykorzystaniem Captive Portal. multicast DNS. stworzenie strony www (html + css + javascript). obsługa komunikacji po websockecie. zdalne wgrywanie plików przez stronę www, np. zmiana wyglądu głównej strony. zdalna aktualizacja oprogramowania bez zbędnych kabli. W oczekiwaniu na gotowe oprogramowanie tester cierpliwie czeka. Panel sterujący dostępny z poziomu przeglądarki internetowej nie jest hostowany nigdzie na zewnątrz, całość znajduje się w zabawce, wykorzystałem bootstrapa plus kilka dodatkowych komponentów. Zastosowany mDNS pozwala połączyć się z urządzeniem wpisując w przeglądarce adres "lasercat.local" zamiast adresu IP. Niestety na chwilę obecną android nie wspiera tego typu rozwiązań, ale na iPhonach działa to bardzo dobrze. Na filmie mała prezentacja z trochę już wybawionym głównym testerem Elroyem a poniżej pokazano jak można włączyć zabawkę po prostu mówiąc do telefonu "Ok Google, włącz laser"
  8. 10 punktów
    Mam zaszczyt przedstawić: Roko Kopłapow - czyli Robot Koncepcyjny, Konserwator Płaskich Powierzchni. Co mniej więcej ma oznaczać: mopopodobny twór samojezdny, którego zadaniem jest sprawdzenie wszelkich koncepcji przed zaprojektowaniem i skonstruowaniem prototypu robota do czyszczenia podłogi na mokro. Niestety - nie wszystko poszło po mojej myśli. Niektóre koncepcje okazały się absolutnie nietrafione, inne wymagają dopracowania i ponownego sprawdzenia. Sam robot jednak doskonale się sprawdził pokazując mi, w którą stronę mają iść dalsze prace. Jakie były założenia: robot ma służyć do przetarcia podłogi na mokro; ma być sterowany pojedynczym Arduino Pro Mini; miło by było aby realizował wszystkie funkcje, ale równie dobrze może pracować jako "inteligentne podwozie ze szczotkami", czyli jako nośnik różnej maści przystawek; Robot powinien umieć poruszać się po podłodze bez jej zmywania (czyli podnieść mechanizm szczotek choćby w celu przejechania przez próg); Robot powinien umieć znaleźć stację dokującą lub miejsce parkingowe. Co się nie sprawdziło: Podnoszone szczotki Założenie całkiem fajne, ale zbyt duża siła jest potrzebna do podniesienia całego mechanizmu. W dodatku podniesiony mechanizm zajmuje zbyt dużo miejsca. Teoretycznie dało by się to obejść ale robot musiałby być dużo większy, a tego nie chciałem. Podwozie gąsienicowe O ile na normalnym podłożu zachowywało się bez zarzutu - o tyle na mokrej, śliskiej podłodze to absolutna porażka, gąsienice po prostu nie łapią przyczepności. Podwozie Pololu (testowo z pojedynczą nieruchomą gąbką) w ogóle nie chciało skręcać, jeśli napędzana była tylko jedna gąsienica jechał sobie dalej prosto, przy napędzanych dwóch przeciwbieżnie robił jakieś dziwaczne ewolucje nie mające nic wspólnego ze skręcaniem. Podwozie Tamiya (maksymalna długość gąsienic, szerokość zwiększona do 15 cm) - niewiele lepiej. Co prawda przy podniesionych szczotkach dało się tym manewrować, ale jako że podnoszone szczotki też nie zdały egzaminu - zrezygnowałem z gąsienic i zrobiłem kołowe. Żyroskop Teoretycznie wszystko było w porządku, robot bardzo ładnie jechał prawie po prostej mimo wirujących szczotek które rzucały nim na boki, skręcał dokładnie o tyle ile chciałem... przez pierwsze 30 sekund. Tyle czasu udało mi się utrzymać mojego MPU6050 przy życiu. Niestety - po bardziej wnikliwych poszukiwaniach okazało się, że nie tylko ja mam takie problemy, niektóre egzemplarze działają bezbłędnie, inne się wieszają (zawieszając przy okazji Arduino). Tak więc na razie robot obywa się bez żyroskopu, chociaż w przyszłości na pewno się na niego zdecyduję (może taki? Poradzicie?). Stacja dokująca W związku z brakiem podnoszonych szczotek musiałem z niej zrezygnować - robot nie przejedzie przez próg, a stacja dokująca powinna być w innym pomieszczeniu. Szkoda... Na razie zamiast dokowania robot powinien wjechać w wyznaczony kąt pomieszczenia i się wyłączyć, ale do tego muszę wymyśleć jakiś inteligentny wyłącznik (to już w oddzielnym wątku). Wąska wiązka czujnika laserowego Chciałem przy "rozglądaniu się" robota ograniczyć szerokość matrycy. Niestety - tu już nie pozwoliło mi na to oprogramowanie. Mamy do dyspozycji dwie biblioteki do czujnika VL53L1X: jedną uproszczoną (która nie pozwala na ustawienie regionu) i drugą pełną (adaptacja ST, pozwala na wszystko tyle że zajmuje 20 kB, jak na małe Arduinko to nieco za wiele). Być może któregoś pięknego dnia uda mi się dopisać tę możliwość do tej mniejszej biblioteki, ale na razie niespecjalnie mam na to czas, a z szerokością wiązki 27° muszę nauczyć się żyć... Inna możliwość to postawienie drugiego procesorka tylko do obsługi lasera, mógłbym wtedy zmienić serwo na mały silnik krokowy (nawet taki mi w szufladzie leży), tylko czy jest sens? A co się sprawdziło: Przede wszystkim - napęd szczotek. Obie szczotki to po prostu kuchenne gąbki włożone w uchwyty, napędzane są pojedynczym silnikiem z dwustronnym wałem. Szczotki są obrócone wobec siebie o kąt 90°, aby zmniejszyć szerokość robota i pozbyć się martwego pola pomiędzy szczotkami, obracają się przeciwbieżnie. Gąbki można w każdej chwili wyjąć i wymienić na czyste, w dodatku mogą być włożone zarówno miękką, jak i szorstką stroną do podłogi. Druga rzecz bez której dalsza praca nie miałaby sensu to przedni zderzak. Musi być bardzo lekki, znajdować się dość nisko nad podłogą (progi) a jednocześnie mieć wysokość wystarczającą, aby robot nie usiłował wleźć pod jakąś kuchenną czy łazienkową szafkę. Jednoczęściowy zderzak zapewnia wykrycie przeszkody w promieniu 180°, jest na tyle lekki że nie mam nawet żadnych sprężyn - cztery krańcówki wystarczają. Krańcówki są połączone po dwie równolegle z każdej strony, zwierają przez rezystor do masy co umożliwia bezproblemowy odczyt zderzaków przez pojedyncze wejście analogowe Arduino. Z drobiazgów: materiał. Oprócz elementów drukowanych z PLA mam tu: płytę główną podwozia z 3mm PCW. Łatwe w obróbce, w miarę elastyczne i wystarczająco wytrzymałe. podłogę piętra ze spienionego PCW 4mm. Nie musi wytrzymywać dużych obciążeń, jest bardzo lekkie i możliwe do cięcia zwykłym ostrym nożem. koszyki na akumulatory wydrukowane z PET-G z integralną sprężyną. Początkowo obawiałem się że nie będą kontaktować - okazało się, że sprawują się dużo lepiej niż kupowane podwójne (już nie mówię o porażce pod tytułem "koszyk na jeden akumulator"), a przy okazji mam tu porządny przewód (ucięty od pecetowego zasilacza) a nie jakiś chiński włosek. Dla zainteresowanych podaję link na thingiverse. A z rozwiązań: Enkodery na kołach. No - powiedzmy częściowo, trochę za mała rozdzielczość ale działają. Do bardziej precyzyjnych rzeczy pewnie by się nie nadały, ale tu zdają egzamin. Ponieważ nie wyszedł mi napęd gąsienicowy, zacząłem szukać jakichś fajnych kółek. Kiedyś znalazłem na Allegro za jakieś śmieszne pieniądze silniczki z kątową przekładnią (odpowiedniki Pololu), dokupiłem do tego koła Pololu 42x19, zmontowałem jakieś prowizoryczne podwozie... i na tym się skończyło bo jakoś straciłem zainteresowanie projektem. Czyli silniki z kołami już miałem. W pierwszej wersji robot miał się orientować w przestrzeni za pomocą żyroskopu i laserowego czujnika odległości, ale z braku żyroskopu zostałem skazany na enkodery. Co prawda oficjalnie te silniki nie miały możliwości zamontowania enkoderów, ale na szczęście w to nie uwierzyłem Postanowiłem umieścić enkodery wewnątrz kół. Co prawda miejsca jest dość mało, musiałem wydrukować nowe felgi, ale jakoś cała konstrukcja się zmieściła. Użyłem popularnego TCRT5000L (zawsze mi kilka w szufladzie leży), dokleiłem uchwyty do silników... i ruszyło! Co prawda enkodery czasami fałszują, ale uczciwa bramka Schmitta powinna im w przyszłości pomóc. Na razie podłączenie wygląda tak: Dla zainteresowanych - zdjęcia i pliki STL/SCAD. Czujnik odległości 360° Niedawno ktoś pytał o coś takiego - o ile pamiętam chodziło o ultradźwiękowy czujnik odległości o zakresie ruchu 360°. Ponieważ moje rozwiązanie działa całkiem nieźle (należałoby tylko maksymalnie zmniejszyć luzy, ale to już zależy od możliwości drukarki) mam tu prostą przekładnię, zakładaną bezpośrednio na serwo i mocowaną tymi samymi śrubami co serwo, z tarczą pozwalającą na zamocowanie czujnika (w moim przypadku laser, ale może to być równie dobrze czujnik ultradźwiękowy). Dwie wersje: większa i kompaktowa (jeśli nie starcza miejsca). Jako osi użyłem śruby z podwozia Tamiya oryginalnie służącej do mocowania kół jezdnych z uwagi na fagment bez gwintu, ale w ostateczności można spróbować ze zwykłą śrubą M3. Tym razem nie ma STL-i jako że każdy ma inne potrzeby, zamiast tego plik dla OpenSCAD-a. Potrzebne będą biblioteki: spur_generator oraz parametric_involute_gear_v5.0. Co do programu: Program jest dość prosty, nie ma sensu publikowanie bo pewnie jutro jak mi się coś przyśni to będzie inny (możliwe że lepszy, ale niekoniecznie). Zresztą tak jak wspominałem - to jest egzemplarz koncepcyjny, wystarczy mi stwierdzenie że "da się to zrobić". Mój SMARDZ niesamowicie ułatwia programowanie, musiałem go tylko trochę inaczej zamocować (dodatkowy uchwyt i przedłużacz) bo przy gwałtowniejszych manewrach zsuwał się z pinów - prawdopodobnie bez czegoś takiego rzuciłbym to po paru godzinach. Robot ma cztery tryby pracy: Zdalne sterowanie - wiadomo o co chodzi. Zrobiłem kiedyś takiego uniwersalnego radiopilota, to już czwarte urządzenie które nim steruję; Jazda losowa - taki typowy wszędołaz, potrafi omijać przeszkody, stara się nie jechać prosto za długo aby się poszwendać w ciekawszych miejscach; Plama - analogicznie jak w Roombach, wyciera podłogę w promieniu do ok. 70 cm od miejsca startu i zatrzymuje się po skończeniu pracy; Sprzątanie - tu program nie jest dopracowany, ale w wersji koncepcyjnej nie ma to większego sensu. Ogólnie robot powinien podjechać do najbliższej ściany (to potrafi), ustawić się do niej równolegle (to też potrafi) i krążyć po pomieszczeniu coraz bliżej środka (tu ma problemy związane z niedokładnością enkoderów). Tak wygląda robot w trybie "plama": Ktoś za chwilę powie: zaraz, przecież to robot do sprzątania na mokro, a gdzie woda? Na razie nie ma. Po prostu: ponieważ nie ma stacji dokującej, przed uruchomieniem robota należałoby ręcznie uzupełnić wodę w zbiorniku. Dużo wygodniejsze jest po prostu spryskanie wodą podłogi w pomieszczeniu przed uruchomieniem robota. A czemu "na razie"? Bo jak wspomniałem na początku, ten robot powinien mieć możliwość współpracy z przystawkami, a taką przystawką może być np. urządzenie do spryskiwania płynem do mycia paneli. Ale taka przystawka powinna mieć swój procesor, robot w tym momencie powinien przestawić się w tryb "inteligentnego podwozia", a to wszystko jest kwestią przyszłości Na razie robot spełnił swoje zadanie, popracuję jeszcze trochę nad programem (dopóki się da), i zaczynam zbierać kasę na wersję finalną Obiecane pliki STL i SCAD w załączniku:koplapow.zip Jak zwykle jestem gotów odpowiedzieć na pytania - choć tym razem chyba ich nie będzie...
  9. 10 punktów
    Kolejna ciekawostka - mało widowiskowa, ale dla mnie bardzo ważna! Po 11 miesiącach udało się uzyskać 2 poniższe dokumenty od Urzędu Patentowego RP. Oznaczają one, że od tego roku logo FORBOT oraz słowo FORBOT są zastrzeżonymi znakami towarowymi (m.in. w kontekście produktów związanych z nauką elektroniki i programowania). W praktyce chroni to FORBOTa przed wieloma nieprzyjemnymi sytuacjami. Od teraz mogę też zgodnie z prawem dodawać obok logo to słynne R w kółeczku FORBOT®
  10. 10 punktów
    Jakoś tak przed dwoma miesiącami mój kochany synuś przy okazji wizyty w domu stwierdził, że jest mu niesłychanie potrzebny theremin i to szybko. Jako że oryginalne instrumenty są conieco przydrogie postanowiłem po konsultacji zrobić odpowiednik instrumentu samodzielnie. Początkowo myślałem, że mam czas do Gwiazdki - okazało się że zamiast dwóch miesięcy mam na zrobienie wszystkiego dwa tygodnie. W tej sytuacji nie było szans na jakąś bardziej skomplikowaną konstrukcję - postanowiłem jak zwykle wykorzystać w większości elementy które miałem w domu i postawić na czysto cyfrową wersję. Z uwagi na krótki termin musiałem pracować praktycznie bez planu, metodą "kolejnych przybliżeń", stąd trochę nietypowa kolejność prac. Jako "serce" instrumentu postanowiłem wykorzystać ESP32 (płytka DevKit miała zawiadywać konstruowanym właśnie robotem, który na razie został odłożony na półkę). DAC ze wzmacniaczem został na razie wyjęty z "Ciapka". Przede wszystkim musiałem sprawdzić, jaki dźwięk mogę uzyskać z tego zestawu. Po prowizorycznym podłączeniu okazało sie, że mój pecet na gnieździe USB ma ok. 4.3 V, a po podłączeniu ośmioomowego głośniczka próba wyemitowania jakiegokolwiek sensownego dźwięku kończy się brownoutem ESP. Cóż - lutownica w dłoń, znalazłem jakieś luźne gniazdko 5.5/2.5, porządny zasilaczyk 5V, diodę dla bezpieczeństwa... no i zagrało. Nawet całkiem nieźle. Teraz przyszła kolej na sensory. Wypróbowałem kilka różnych, począwszy od zwykłego zestawu dioda/fototranzystor, poprzez HC-SR04, laserowe TOF aż po czujniki odległości Sharpa. O ile lasery sprawdziłyby się pewnie bardzo dobrze - o tyle ESP ma poważne problemy z jednoczesnym generowaniem dźwięku i gadaniem po I2C (niestety, nie doszedłem do tego skąd się tak naprawdę biorą ale nie miałem czasu sprawdzać), pozostałem więc przy Sharpach. W tym momencie mogłem już założyć, że coś mi wyjdzie. Jeszcze nie bardzo wiedziałem co, ale ważne, aby wyszło - mogłem się więc zająć czymś, co szumnie nazwałem "projektowaniem". Przede wszystkim, po policzeniu nóżek w ESP32 oraz przejrzeniu zawartości szuflad doszedłem do wniosku, że instrument będzie miał: klawiaturę matrycową 4x4 (do czego miałaby służyć miałem wymyślić pisząc program) cztery potencjometry regulujące różne ciekawe rzeczy (jakie - to miałem rozstrzygnąć później) cztery dodatkowe klawisze (dwa do przełączania oktaw i po jednym do zakresu sensora PITCH i trybu DUAL) pięć diod LED sygnalizujących różne mądre rzeczy mały wyświetlacz OLED 128 x 64 Dodatkowo docelowo miałem wykorzystać inny przetwornik DAC, ale to już musiałem dodatkowo zamówić; ponieważ ten przetwornik już znałem i miałem go w radyjku internetowym postanowiłem, że zamówię go później (być może coś jeszcze będzie potrzebne), a w razie konieczności (np. skończą sie w magazynie w Botlandzie) jakoś przeżyję jakiś czas bez radyjka. Na szczęście nic to na tym etapie nie zmieniało, więc pozostałem na razie przy podłączonym moim przetworniko-wzmacniaczykiem od Ciapka i głośnikiem (chyba) od jakiegoś taniego gramofonu. Tak więc pod względem elektroniki całość przedstawiałaby się następująco: A więc Eagle, projekt, laminat, termotransfer... i tu zonk: moja nowiutka drukarka nie nadaje się do tych celów, a wysłużona Kyocera właśnie się wyprowadziła do nowego właściciela Nie ma problemu. Telefon do kumpla od frezarki, zrobisz mi... A tak, zrobi. Za dwa tygodnie bo akurat swoją frezarkę przerabia (co u niego jest normalne). Dlatego właśnie na zdjęciu widzimy płytkę uniwersalną. Wyprzedzę trochę bieg wydarzeń: co prawda sprawę zasilania rozwiązałem na samym końcu (chciałem koniecznie mieć akumulator ale gdyby mi nie starczyło czasu podłączyłbym zwykły zasilacz), ale jako że pokazuję urządzenie praktycznie przed ostatnim zamknięciem obudowy - pokażę również jak mam to zrobione: Ponieważ zarówno przetwornicę, jak i ładowarkę miałem nie było sensu wysilać się na jakieś poważniejsze rozwiązania. Jeśli ktoś mi podpowie jak to zrobić lepiej (najchętniej bez przełącznika zasilacz/akumulator) będę bardzo wdzięczny. Skoro sprawę kabelków miałem już rozwiązaną, przyszedł czas na zaprojektowanie jakiejś obudowy. Oczywiście mógłbym całą obudowę wydrukować, ale z uwagi na termin i na to, że pewnie będzie fafnaście poprawek postanowiłem zrobić to inaczej: dolna i górna płyta wycięte ze spienionego PCW 4mm,a wydrukowane tylko ramka i boki. Ponieważ obróbka PCW jest prosta i zajmuje niewiele czasu (w sumie kwestia przycięcia ostrym nożem) mogłem pozwolić sobie na wydrukowanie jedynie szkieletu, mocując doń obie płyty. Powiem szczerze że był to dobry pomysł; co prawda górna płyta wyszła "nieco" krzywo (tak to jest, jak się tnie i wierci w ręku), ale nie traciłem czasu na wielokrotne próby. Dzięki temu jedyne co mnie ograniczało to wielkość stołu mojej Anetki (ramka musiała być wydrukowana w całości). Co prawda ktoś może twierdzić że mógłbym wydrukować jakąś wersję finalną - ale na to naprawdę nie było już czasu (całość skończyłem na kilka godzin przed oddaniem maszynki w ręce użytkownika, w związku z tym nawet drukarka by się nie wyrobiła w czasie). Wyszło jak wyszło - wygląda to tak: Z przodu instrument nie wygląda za ciekawie, ale chciałem pozostawić dostęp do przycisków RESET i BOOT na płytce. I mała dygresja: program pisałem na komputerze z zainstalowanym Ubuntu 18.04, od paru dni robiłem ostatnie poprawki używając lapka z Debianem Stretch. Ta sama wersja Arduino IDE, ta sama wersja definicji ESP32. Na Ubuntu wymagane było wciśnięcie BOOT do zaprogramowania płytki - na Debianie nie trzeba. Ktoś wie może o co chodzi? Bo chciałbym, aby moje Ubuntu zachowywało sie tak samo I to by było na tyle jeśli chodzi o kabelki, śrubki i plastiki - warto sobie przypomnieć, że cały czas równolegle powstawała część najważniejsza, czyli PROGRAM. Ponieważ napisanie w ciągu dwóch tygodni od zera programu na płytkę, którą pierwszy raz trzymam w ręku i to z dziedziny, w której akurat mistrzem wcale nie jestem to bardzo ryzykowny pomysł - postanowiłem nie wygłupiać się i użyć sprawdzonych rozwiązań. Jako że grałem kiedyś na małym Moogu (Rouge) naturalne dla mnie było zasymulowanie właśnie analogowego syntezatora. Program symuluje działanie dwóch generatorów (VCO), z których jeden może być lekko odstrojony. Do wyboru są: sinus, prostokąt, piła oraz sześć dodatkowych fal składanych z 32 harmonicznych oraz szumu. Szum przepuszczany jest przez filtr o częstotliwości środkowej odpowiadającej wysokości dźwięku generatora A. Oba generatory mogą pracować z różnymi kształtami fal, dodatkowo zamiast filtrów VCF zastosowałem podwójne fale: fala pomocnicza miksowana jest z podstawową, a stosunek amplitudy obu fal regulowany jest albo poprzez sensor PITCH (najniższy dźwięk to fala podstawowa, najwyższy to pomocnicza, pośrednie wysokości to odpowiednio zmieszane obie fale) albo przez sensor VOLUME (wtedy do połowy odległości od sensora głosność wzrasta do 100%, w miarę dalszego zbliżania się następuje przejście z fali podstawowej na pomocniczą). Daje to dużo większe możliwości niż klasyczne filtry. Oczywiście - ten tryb (czyli Dual Mode) można wyłączyć - wtedy oba generatory pracują z falą podstawową. Generatory zrealizowane są poprzez przemiatanie z odpowiednim krokiem tablic fal. Każda z 4 tablic składa się z 4096 próbek float. Początkowo zastosowałem typ double do obliczeń - niestety okazało się, że ESP32 po prostu się nie wyrabia w czasie. Szkoda, bo różnica jest w niektórych przypadkach wyraźnie słyszalna; przy pewnych kształtach fal (szczególnie zawierających wysokie harmoniczne) przy odstrojeniu generatorów powstają artefakty, których nie było w wersji double (prawdopodobnie były, ale poniżej granicy słyszalności). Zakres instrumentu to 7 oktaw, przy czym sensor PITCH reguluje wysokość w zakresie max. dwóch oktaw, przełącznik OCTAVE UP/DOWN zmienia ton podstawowy. Dodatkowo przełącznik transpozycji pozwala na podniesienie skali od zera do 11 półtonów, a precyzer płynnie dostraja skalę o +/- jeden półton z rozdzielczością 5 centów. Najwięcej kłopotu sprawiły mi te nieszczęsne Sharpy. Generatory są skonstruowane tak, że wysokość dźwięku jest proporcjonalna do wartości sterującej (korzystam z wstępnie przeliczonych tablic dla interwału 5 centów, czyli praktycznie poza zakresem czułości ludzkiego ucha). Niestety - poziom napięcia na wyjściu czujnika jest proporcjonalny do odwrotności odległości (w pewnych granicach), do tego sygnał jest dość mocno zaszumiony. Okazało się jednak, że zastosowanie filtrów Kalmana poskutkowało praktycznie całkowitym usunięciem szumów, a 12-bitowe przetworniki wejściowe ESP32 pozwoliły mi uzyskać na wejściu wystarczającą rozdzielczość. Ponieważ zabawa z 32 harmonicznymi przy pomocy 16-klawiszowej klawiatury i zabawkowego OLED-a to absolutnie chybiony pomysł - do ustalania fal służy dość prosta aplikacja napisana w Pythonie. Aplikacja łączy się poprzez port Serial z instrumentem oraz uruchamia serwer HTTP na adresie localhosta. Działa bez problemu na Linuksie (Firefox, Chrome, Chromium), na Windows mogłem sprawdzić tylko Chrome i Edge. O dziwo - Edge poradził sobie prawie bez problemu (prawie robi wielką różnicę - okazało się że do odczytu wartości selecta musiałem użyć jQuery zamiat po prostu selectedIndex), natomiast Chrome nie potrafił nawet wyświetlić prawidłowo interfejsu... który wygląda mniej więcej tak: Oczywiście - konieczność zastosowania tej aplikacji nie oznacza konieczności ciągania za sobą komputera. Ponieważ użytkownik ma do dyspozycji 9 zestawów po 6 fal w każdym i może zapisać je w pamięci Flash urządzenia - powinno wystarczyć na koncert Minęło kilka dni... jako że w międzyczasie przyszedł z Botlandu DAC mogłem już zabrać Ciapkowy wzmacniaczyk. Ponieważ ta wersja nie posiada własnego wzmacniacza, a podłączenie jakiegoś prostego odsłuchu bywa konieczne - po prostu podłączyłem tam mały moduł wzmacniacza PAM8403 z potencjometrem (widać go na zdjęciu wnętrza urządzenia). W efekcie tylna ściana instrumentu wygląda tak: I to by było na tyle... A nie, wyjaśniam, że: Klawiatura służy do obsługi generatorów (przyciski A i B), zmiany transpozycji (przycisk C) i presetu/zestawu fal (przycisk D) LED-y sygnalizują tryb działania klawiatury (A, B, C i D) oraz włączenie stroju temperowanego Potencjometry regulują odstrojenie generatora B, balans generatorów, precyzyjne strojenie oraz czas portamento Wyświetlacz pokazuje: Fale generatora A (podstawowa 1, pomocnicza 2), tryb synchronizacji (LFO), fale generatora B (1 i 2), strój generatora B (Prima) Transpozycja (tonacja C, oktawa 2) tryb Double Mode (Vl - volume) Zakres sensora PITCH (1 oktawa) Numer presetu (Pg - zero) Numer zestawu fal (W - zero) Numer pasma głównego filtra dolnoprzepustowego (F - 8 ) A skąd nazwa InfraWave? Bo theremin (na falach eteru) produkcji Mooga nazywa się Etherwave, a mój jest a podczerwień Mam nadzieję że Moog nie opatentował jeszcze słowa Wave... I kilka słów o możliwej przyszłości. O ile powstanie następna wersja instrumentu (zależnie od zamówienia i widzimisię synka) należałoby poprawić kilka rzeczy. Przede wszystkim - trzeba by było zająć się generatorami. Może udałoby się zmusić je do pracy w arytmetyce double (potrzebne cztery mnożenia i trzy dodawania), a resztę (filtry, szumy itp.) robić na floatach. Prawdopodobnie jest to możliwe - ale na razie nie miałem czasu a eksperymenty. Ilość przeróżnych manipuladeł jest stanowczo za mała. Bezwzględnie konieczne są potencjometry do regulacji częstotliwości LFO, głośności ogólnej oraz dobroci głównego filtra szumów. Licząc potrzebne klawisze na razie wychodzi mi 22 (mam cztery) i 26 sztuk LED (mam pięć)... Warto by było pozbyć się pecetowej aplikacji - do tego potrzebny jest co najmniej enkoder obrotowy z przyciskiem i większy ekran (jakiś TFT, przynajmniej 320x240 albo i więcej). O ile czujnik Sharpa bardzo dobrze nadaje się na sensor VOLUME, o tyle sensor PITCH mógłby być nieco lepszy. Wymaga trochę eksperymentowania... Ogólnie jestem otwarty na propozycje - może ktoś ma jakieś lepsze pomysły Natomiast świetnie się sprawdza DAC od Adafruit, a więcej czasu pomogłoby mi w lepszym opanowaniu ESP32 (to on ma słuchać mnie, a nie ja jego!) Kody programów w załączniku: infrawave.zip. Nie jest potrzebna żadna dodatkowa biblioteka (drivery wyświetlacza i audio są zawarte w programie). Jak zwykle pozostaję do dyspozycji w razie pytań.
  11. 9 punktów
    Researcher jest robotem badawczym przeznaczonym do badania składu gleby oraz powietrza. Robot jest wyposażony w łamane ramię z łyżką przeznaczoną do pobierania próbek gleby oraz umieszczania próbek w specjalnych pojemniczkach wyposażonych w czujniki składu gleby (np. wilgotności, ph). Próbki mogą być pobierane z głębokości 10 cm dzięki zastosowaniu wiertła zamontowanego na siłowniku liniowym o wysuwie 10cm. Za obroty wiertłem jest odpowiedzialny silnik DC. Element wiertniczy jest zamontowany na zawiesie i mocowany na jedną śrubę, ułatwia to transport robota. Za działanie ramienia odpowiedzialne są 4 serwa, ramię jest wykonane z aluminium, dzięki czemu jest wytrzymałe i lekkie. Aby odciążyć serwo odpowiedzialne za obracanie ramieniem zastosowaliśmy płytę przymocowaną do podwozia, poruszają się po niej kułeczka zamocowane do części ruchomej. Konstrukcja robota jest wykonana w całości z aluminium i stali. Obudowa i podstawka pod elektronikę są wykonane z polisterynu. Robot jest w stanie poruszać się po trudnym terenie i wjeżdżać na krawężniki, umożliwia mu to zastosowanie zawieszenia biegunowego dla dwóch przednich osi. Tylna oś jest zamocowana na amortyzatorach. Za ruch silników odpowiedzialnych jest 6 silników DC zamontowanych do płaskowników aluminiowych za pomocą obejm i śrub. W tylnej części zamontowany wspornik mocujący do czujników wiatru. Robot waży około 13 kilogramów i ma 70 cm długości i ok 40 cm szerokości. Za pracę odpowiada moduł Arduino MEGA 2560, w robocie zastosowaliśmy 4 sterowniki silników jednokanałowych po jednym na każdą stronę robota (po 3 silniki do jednego) 2 pozostałem odpowiadają za ruch siłownikiem oraz silnikiem w module wiertniczym. Zastosowaliśmy czujniki temperatury powietrza i gleby, składu powietrza i gleby. Aby nie uszkodzić robota zastosowaliśmy czujniki odległości dzięki którym robot nie wjeżdża w ściany i większe przeszkody. Do komunikacja pomiędzy robotem a aparaturą używamy modułu radiowego. Do sterowania używamy przerobionej aparatury. Znajduje się w niej Arduino UNO, 3 gałki analogowe, wyświetlacz i płytka dotykowa z przyciskami oraz da przełączniki dźwigniowe do zmiany trybu sterowania. Całego robota budowaliśmy około 10 miesięcy w 3 osobowym zespole, uczestniczyliśmy w licznych zawodach m in East Robo, Mikrobot, Explory. Do sukcesów można zaliczyć 4 miejsce w Konkursie El Robo Mech dzięki któremu zdobyliśmy indeksy na studia. Zastosowaniem robota ma być wykorzystywany do badania gleb w miejscach trudno dostępnych oraz jako robot ratunkowy w wypadku skażeń chemicznych i biologicznych. Robot mógłby zbadać glebę i powietrze na terenie skażonym, a dzięki czujnikom kierunku i prędkości wiatru można będzie oszacować przemieszczanie się chmur z zanieczyszczeniem. Operator robota może odczytywać dane z czujników z wyświetlacza na kontrolerze oraz mieć podgląd z kamery umieszczonej na robocie.
  12. 9 punktów
    Witam całego Forbota. Chciałbym przedstawić tu mój autorski projekt, który stworzyłem w sumie w technikum, później go udoskonalałem, aż do wersji mikroprocesorowej. Dzisiaj chciałbym wam pokazać jego jedną z pierwszych wersji (to nie znaczy gorszą tylko ciekawszą). Zapraszam do lektury. Wprowadzenie Niniejsze urządzenie powstało jako unowocześnienie i ulepszenie tanich popularnych na rynku tzw. „klaskaczy”. Jako że te opierają się wbrew temu co mówi nazwa na pomiarze głośnych dźwięków, a nie klaśnięcia to ich niezawodność pozostawia wiele do życzenia. Główną ich wadą jest działanie na wszelkie głośniejsze dźwięki co uniemożliwia np. słuchanie głośniejszej muzyki, ponadto czasem nawet upadający przedmiot jest w stanie aktywować taki „klaskacz”. Projekt włącznika akustycznego jest wysoce efektywnym i niezawodnym zamiennikiem wcześniej wspomnianego rozwiązania, duża czułość powoduje iż nawet najsłabszy gwizd będzie efektywnie interpretowany, jedynym chyba mankamentem jest fakt, że trzeba potrafić gwizdać. Zasada działania jest prosta. Należy gwizdnąć zmieniając w odpowiedni sposób ton gwizdu aby włączyć lub wyłączyć podłączone urządzenie (np światło w pokoju). Włącznik akustyczny w wersji v 1.0 jest układem niemalże całkowicie analogowym. Elektroniczny: Blokowy: Ogólny opis zasady działania Sygnał akustyczny z mikrofonu elektretowego o małej amplitudzie musi zostać najpierw wzmocniony aby mogła zostać przeprowadzona jego analiza, dlatego też trafia najpierw na wzmacniacz oparty na tranzystorach T5,T4 Następnie wzmocniony sygnał podawany jest przez kondensator separujące na wejścia dwóch układów detekcji tonu LM567 gdzie jest porównywany do częstotliwości wzorcowej ustawionej przez zewnętrzne elementy RC. Układy te posiadają wyjście typu open colector, dlatego konieczne stało się zastosowanie rezystorów pull up. Wykrycie takiej samej częstotliwości podawanej na wejście jak częstotliwość referencyjna objawia się pojawieniem stanu niskiego na wyjściu LM567. Dalej sygnał trafia na układy czasowe NE555 pracujące w trybie monostabilnym, których zadaniem jest powiązanie czasowe wystąpienia obu sygnałów z detektorów tonu. Zwiększa to niezawodność całego układu dlatego, że wykrycie drugiej częstotliwości w odpowiednim przedziale czasowym zapoczątkowanym wykryciem pierwszej częstotliwości umożliwia przełączenia układu wykonawczego. Czas trwania sygnału aktywnej częstotliwości można płynnie regulować zmieniając stałą czasową RC układów NE555. Następnie sygnały z generatorów monostabilnych trafiają na bramkę AND zbudowaną w oparciu o 3 tranzystory T2, T3, Q1. Sygnał z bramki jest podawany na wejście zegarowe aktywowane zboczem narastającym przerzutnika T (ang. Toggle) zrobionego z przerzutnika D zawartego w układzie CD4013. Pod wyjście przerzutnika podłączony jest tranzystor wykonawczy w roli klucza T1. Obciążalność tego tranzystora reguluje nam jakim urządzeniem możemy sterować. Na płytce została umieszczona dioda włączona w kierunku zaporowym na wypadek indukcyjnego obciążenia tegoż tranzystora np. cewką przekaźnika. Szczegółowy opis zasady działania Układ wejściowy i wzmacniacz Na układ wejściowy składa się taki mikrofon podłączony przez rezystor 3,3k który polaryzuje wewnętrzny tranzystor polowy. Następnie sygnał trafia przez kondensator separujący składową stałą na wzmacniacz zbudowany na tranzystorze T5 BC547B . Jest to bardzo prosty wzmacniacz, którego baza polaryzowana jest jednym rezystorem. Punkt pracy został dobrany na podstawie charakterystyk tranzystora BC547B, prąd bazy wynosi około 3,3uA, a współczynnik wzmocnienia prądowego β wynosi około 310 daje to prąd kolektora na poziomie 1[mA], zakładając spadek napięcia na rezystorze równy połowie napięcia zasilania, a więc 4V można przyjąć, że wartość rezystora powinna wynosić ok. 4000Ω. W projekcie została użyta wartość znormalizowana 3,3k. Co przesunęło nieco charakterystykę pracy w kierunku wyższego potencjału. Następnie sygnał trafia przez rezystor - w celu zwiększenia impedancji wejściowej i kondensator - aby wyeliminować składową stałą na układ dzielnika napięcia wstępnie polaryzującego bazę tranzystora bipolarnego NPN BC547B. Tranzystor ten pracuje w układzie wspólny emiter. Pomiar współczynnika wzmocnienia prądowego tranzystora β wykazał wzmocnienie na poziomie 310. Zakładając niewielką rezystancję emiterową rzędu 220 Ohm oraz przyjmując na niej spadek napięcia 1V możemy wyliczyć prąd emitera, który wynosi Ie = 1/220 = 4,5[mA]. Upraszczając, że prądy emitera i kolektora są sobie równe jesteśmy w stanie policzyć przy spadku napięcia zasilania 9V o połowę wartość rezystora R1 która będzie równa R1 = 4,5/0,0045 = 1k. Prąd bazy wynosi Ic/Beta = 4,5/310 = 14,5[µA] Należy zatem przyjąć dzielnik napięcia w którym prąd płynący będzie wielokrotnością prądu bazy. Ponieważ wcześniej przyjęte zostało, że spadek napięcia na rezystancji emiterowej R4 wynosi 1[V] to napięcie jakie się odłoży na rezystorze R3 będzie wynosiło 1 + 0,7V =1,7[V], a na rezystorze R5 odpowiednio 9 - 1,7 = 7,3V. Przyjmując wartość rezystora R5 = 22k prąd płynący przez ten rezystor wyniesie 7,3/22000 = 330[µA] , natomiast przez rezystor R3 popłynie ten sam prąd pomniejszony o prąd bazy, więc będzie on wynosił 315,5[µA]. Znając spadek napięcia oraz prąd obliczamy rezystancję i przyjmujemy najbliższą znormalizowaną jej wartość – w tym przypadku 4,7k. Poniżej znajduje się wykres oscyloskopowy sygnału wejściowego i wyjściowego wzmacniacza na samy tranzystorze T1: Do pomiarów sygnału posłużyła karta dźwiękowa i program SoundCard Osciloscope v1.41. Wzmacniacz został przetestowany na sygnale 1kHz. Jak widać cechują go bardzo małe zniekształcenia oraz wzmocnienie napięciowe na poziomie = 3.37. Oprócz tego sygnał jest przesunięty w fazie o 180ᵒ co jest cechą charakterystyczną wzmacniacza w układzie OE. Jeśli jeszcze chodzi o realizację praktyczną to pierwszy stopień wzmacniacza został zrealizowany na osobnej płytce. Układ detekcji tonu LM567 Następnie sygnał akustyczny z wzmacniacza trafia na układ detekcji tonu oparty na popularnym LM567, zawiera on w swojej strukturze wewnętrzny wysokostabilny generator (źródło częstotliwości wzorcowej) oraz wejście do którego podaje się sygnał badany. Częstotliwości obu przebiegów są porównywane w układzie fazowej pętli synchronizacji (PLL) i – o ile są one z zadaną dokładnością sobie równe – następuje uaktywnienie wyjścia układu. Wejściem sygnału jest nóżka nr 3. Do wyprowadzeń nr 5 i 6 dołączone są zewnętrzne elementy generatora decydujące o częstotliwości jego pracy. Kondensator podłączony do nóżki nr 2 służy do ustalenia głównego parametru wewnętrznej pętli PLL: tzw. zakresu trzymania. Jest to największa dopuszczalna wartość o jaką mogą różnić się porównywane częstotliwości, by układ uznał je za równe. Kondensator podłączony pod nóżkę nr 1 jest elementem wyjściowym filtra dolnoprzepustowego eliminującego ewentualne przypadkowe krótkotrwałe przełączenia wyjścia. Wejście układu jest spolaryzowane wewnętrznie i wymaga jedynie doprowadzenia sygnału wejściowego, wyjście natomiast jest typu open colector, dlatego konieczne jest zastosowanie rezystorów podciągających. Dokładna zasada działania układów LM567 jest długa do opisania. Generalnie cały układ opiera się na detektorze I-fazowym (In-phase detector) i Q-fazowym (quadrature-phase detektor). Sygnały I/Q są sygnałami wartości składowych zespolonych sygnału, a otrzymuje się je przez mnożenie sygnału wejściowego przez sygnał referencyjny i przez sygnał referencyjny przesunięty w fazie o 90*. Następnie wartość zespolona sygnału wejściowego przechodzi przez filtr dolnoprzepustowy gdzie regulowana jest maksymalna wartość o jaką mogą różnić się sygnał referencyjny i wejściowy aby układ mógł uznać je za równe sobie. Sygnał z filtru przestraja odpowiednio generator sterowany napięciowo (VCO), którego sygnał porównywany jest z sygnałem wejściowym w detektorze fazy (I chase detector) składowej rzeczywistej sygnału wejściowego. Na wyjściu pojawia się różnica tych dwóch sygnałów, więc jeśli są sobie równe wyjście układu przyjmuje stan niski. Częstotliwość detekcji wylicza się ze wzoru podanego przez producenta: Fo=1/(1.1*R1*C1), w naszym przypadku R1=R10+R9, C1=C8 Układ czasowy NE 555 NE555 zawiera komparatory mające swe wyprowadzenia na nóżki 2 i 6. Napięcia na tych nóżkach są porównywane z napięciami panującymi na wewnętrznym dzielniku napięcia. I tak jeśli napięcie na nóżce 6 przekroczy 2/3 napięcia zasilania to wyjście układu przyjmuje stan niski, a wewnętrzny tranzystor przewodzi. W przypadku spadku napięcia poniżej poziomu 1/3 Ucc sytuacja jest analogicznie odwrotna czyli na wyjściu jest stan wysoki, a wewnętrzny tranzystor nie przewodzi. W zależności od podłączenia zewnętrznych elementów można uzyskać 3 rodzaje pracy: układ astabilny, monostabilny oraz przerzutnik RS. W projekcie został zastosowany tryb monostabilny w przypadku którego sygnał wyzwalający podawany jest na nóżkę 2 układu. Sygnałem wyzwalającym jest sygnał o wartości mniejszej niż 1/3 Ucc, a więc w naszym przypadku mniejszym niż 3V(ponieważ cały układ zasilany jest z 9V). Sygnał ten pobierany jest z wyjść układów LM567. Pojawienie się go na nóżce 2 powoduje iż na wyjściu pojawia się stan wysoki, a kondensator wcześniej non stop rozładowywany przez wewnętrzny tranzystor zaczyna się ładować. Stan wysoki na wyjściu trwa dopóty dopóki napięcie na zewnętrznym kondensatorze ładowanym przez potencjometr osiągnie wartość większą niż 2/3 Ucc. Gdy to nastąpi stan na wyjściu układu zmienia się na niski, a wewnętrzny tranzystor zaczyna rozładowywać kondensator do zera. Powtórne aktywowanie stanu wysokiego na wyjściu wymaga podania stanu niskiego na nóżkę 2. Jak podaje datasheet układu NE555 czas trwania impulsu na wyjściu opisuje zależność: T=1.1*R*C, w naszym przypadku R= Bramka AND Aby mogło nastąpić przełączenie układu wykonawczego w stan przeciwny sygnał z układów czasowych NE555 musi zostać podany na bramkę AND, innymi słowy aktywacja urządzenia wyjściowego następuję wówczas gdy oba sygnały wyjściowe z przerzutników monostabilnych, są aktywne jednocześnie. Bramka AND została zrealizowana na 3 tranzystorach. Schemat bramki został przedstawiony niżej: Gdy nie ma żadnego sygnału na wejściach podpisanych channel A channel B tranzystor Q1 nie przewodzi, rezystor R20 podciąga do dodatniej szyny zasilania jego bazę dzięki czemu nie ma możliwości aby jakikolwiek potencjał zgromadzony w okolicy nóżki bazy tranzystora Q1 przez przypadek go aktywował natomiast wyjście bramki jest podciągnięte rezystorem pull down R25 do ujemnej szyny zasilania. W momencie podania sygnału na bazy tranzystorów T3 i T4 te zaczynają przewodzić. Przez rezystory R20, R19 oraz bazę tranzystora Q1 zaczyna płynąć prąd w skutek czego na wyjściu bramki pojawia się stan wysoki. Rezystory zostały dobrane tak żeby bez problemu każdy tranzystor pracował w stanie nasycenia, a jednocześnie nie zostały przekroczone dopuszczalne parametry pracy. Maksymalny prąd płynący przez tranzystory T2,T3 wyniesie uwzględniając napięcia Ucesat na obu tranzystorach ok. 1,3 [mA] Przerzutnik T Aby móc zrealizować przerzutnik typu T na przerzutniku typu D należy sygnał z jego zanegowanego wyjścia podać na wejście. Sygnał wyjściowy będzie pobierany z jego niezanegowanego wyjścia. Zmiany stanu wyjścia na przeciwny idą w takt zbocza narastającego sygnału podawanego na wejście zegarowe. Sygnał podawany jest na wejście zegarowe przez rezystor zabezpieczający prosto z bramki AND. Przerzutnik T został zrealizowany na jednym przerzutniku typu D zawartym w układzie CD4013BP. Drugi przerzutnik zostaje niewykorzystany dlatego jego nóżki wejściowe (CLOCK2, RESET2, SET2,D2) są wg zasady podłączone na stałe do masy aby ograniczyć straty mocy. Stopień wyjściowy Stopień wyjściowy został zaprojektowany tak aby był możliwie jak najbardziej uniwersalny. Jako element przełączający posłużył tranzystor BC237 o maksymalnym prądzie kolektora Ic = 100mA , co w zupełności wystarcza do zasilenia cewki przekaźnika, oraz maksymalnym napięciu kolektor emiter Uce wynoszącym 45V. Mimo iż układ zasilany może być maksymalnie z 9V bo na tyle pozwalają układy LM567 to napięcia panujące na złączu tegoż tranzystora mogą być znacznie większe np. z racji indukcyjnego obciążenia przez cewkę przekaźnika, która w momencie odłączenia zasilania indukuje wysokie napięcie, o polaryzacji przeciwnej do wcześniejszego zasilania cewki. Aby zniwelować wpływ tego napięcia na tranzystor T4 została wprowadzona dodatkowo dioda podłączona równolegle do obciążenia, której zadaniem jest ograniczenie wyindukowanego się wysokiego napięcia do napięcia spadku na diodzie 0,6-0,7V. Rezystor R24 ogranicza prąd bazy tranzystora T7 i został tak dobrany aby tranzystor w momencie aktywacji znalazł się w stanie nasycenia przy jednocześnie dopuszczalnym prądzie bazy. Przy zasilaniu układu z 9V spadek napięcia na rezystorze wyniesie: 9V-0,7V=8,3V Rezystancja wynosi 4,7k co po podstawieniu do wzoru daje nam prąd I=1,7[mA], który wystarcza do pełnego otwarcia się tranzystora. Testy praktyczne Na podstawie testów przeprowadzonych w laboratorium można stwierdzić bardzo małą awaryjność urządzenia. Dzięki wysokiej selektywności układów LM567 układ reagował nawet przy dużym hałasie z mnóstwem innych składowych np. przy słuchaniu muzyki, znacząco deklasując tradycyjne „klaskacze”. Zdarzały się jednak sporadyczne aktywacje jednej z nastawionych częstotliwości co oczywiście nie zmieniało stanu wyjścia układu. Dzięki podwójnemu wzmacniaczowi urządzenie wychwytuje określone częstotliwości już przy najcichszym sygnale akustycznym. Testy wykazały, że reaguje na słaby gwizd do 2.5m, natomiast największa odległość z jakiej udało się aktywować urządzenie wynosiła nieco ponad 10 m. Parametry te są w zupełności wystarczające biorąc pod uwagę metraż przeciętnego pokoju. Układ powinien być zasilany napięciem z przedziału 5-9V. Dolną granicę zasilania determinują głównie układy NE555, ponieważ poniżej tej wartości zaczynają się same wzbudzać, natomiast górną komparatory częstotliwości LM567. Najlepsze efekty (czułość) uzyskuje się zasilając układ z 8-9 V. I to już koniec tego mam nadzieję wyczerpującego opisu. Troszkę może to przypomina powtórkę z podstaw elektroniki, ale mimo wszystko mam nadzieję że komuś taka analiza się przyda w przyszłości aby łatwiej rozpocząć przygodę z elektroniką. Dodam tylko, że całość została zaprojektowana w programie Eagle i wykonana metodą termotransferu na laminacie jednostronnym oraz starannie pocynowana i zabezpieczona kalafonią (stąd ten może brzydki wygląd). Wzięło się to stąd, że układ robiłem koledze do zamontowania w ścianie z kartongipsu, dlatego też cała elektronika nie ma stosownej obudowy oraz trzeba było jakoś ścieżki zabezpieczyć przed ewentualną wilgocią w ścianach ;D Na zakończenie filmik przedstawiający działające urządzenie oraz zdjęcia. W załączniku płytka w Eaglu. Eagle.rar
  13. 9 punktów
    Zaczynając przygodę z elektroniką, prawdopodobnie każdy z nas jako jedno pierwszych narzędzi jakie kupił to sprzęt lutowniczy. Zazwyczaj zaczynamy od taniej "transformatorówki" lub zwykłej lutownicy kolbowej. Z czasem do coraz bardziej zaawansowanych projektów/napraw zaczyna brakować nam wielu funkcji w takim sprzęcie lub zwyczajnie chcemy czegoś lepszego i zastanawiamy się nad zakupem stacji lutowniczej. Tak wyglądał też mój początek z lutownicami.Pierwsze stacje które miałem, to popularne chińczyki które szybko się psuły lub zużywały. Po przepaleniu którejś kolby z rzędu, powiedziałem basta! Po dość długich poszukiwaniach, stwierdziłem że sprzęt który mnie zaspokoi jest dość drogi i zostaje DIY, tak właśnie stworzyłem idealną stację lutowniczą dla siebie, wzorując się na ciekawych i gotowych już pomysłach z sieci. Uwaga! W niektórych miejscach na płycie występują napięcia 230V! Niezachowanie ostrożności może grozić śmiercią! Założenia jakie miałem: Możliwość podłączenia różnych kolb lutowniczych( 24V z termoparą) Funkcja uśpienia(stacja przechodzi w tryb 180 stopni) Pamięć 3 najczęściej używanych temperatur Sterowanie pochłaniaczem dymu z poziomu stacji Wyświetlanie wszystkich informacji w czytelny sposób na LCD Niska awaryjność(pisze ten artykuł po kilku latach użytkowania stacji i póki co zero problemów) Niewielkie koszty Myślę że założenia udało mi się spełnić idealnie. Do stacji można podłączyć dowolną kolbę z grzałką na 24V. Posiada pamięć 3 zaprogramowanych wcześniej temperatur. Po 15 minutach od odłożenia kolby przechodzi w tryb uśpienia, co za tym idzie temperatura spada do 180 stopni i jest podtrzymywana, tak aby po podniesieniu kolby szybko się rozgrzać do poprzedniej temperatury, dodatkowo w trybie uśpienia wyświetlacz się wygasza. Po 30 minutach bezczynności stacja wyłącza się. Na tyle obudowy jest dodatkowe wyjście 230V pod które możemy podłączyć wyciąg powietrza, załączamy je odpowiednią kombinacją klawiszy. Dwie diody służą do sygnalizacji nagrzewania grzałki i załączenia wyjścia 230V. Obecnie pracuję na taniej kolbie firmy Zhaoxin ze względu na fakt, że posiadam bardzo duży zapas grotów i chcę je wykorzystać. Jednak gorąco polecam Solomon SL-30, którą używam w pracy i zdecydowanie dużo lepiej się nią lutuje. Układ załączania grzałki oparty jest na triaku i optotriaku który dodatkowo wykrywa przejście sieci przez zero. Jako ciekawe rozwiązanie warto tu wspomnieć o układzie włącznika. Podając impuls przyciskiem włączającym układ, tak naprawdę zawieramy styki przekaźnika na chwilę, procesor w ułamku sekundy wystawia stan wysoki na tranzystor który steruje przekaźnikiem załączającym zasilanie na całej płycie. Wyłączenie polega na wystawieniu stanu niskiego na tranzystor i odłączenie zasilania przez przekaźnik. Dodatkowe wyjście 230V również zbudowane zostało na popularnym układzie triak+optotriak. Wyświetlacz LCD w czytelny sposób prezentuje interesujące nas informacje takie jak stan podniesionej kolby, temperatura grota jak i żądaną, tryb pracy czy stan wyjścia 230V. Program do stacji został zaczerpnięty z innego projektu stacji który znalazłem w sieci, wspólnie z autorem dokonaliśmy kilku poprawek i modyfikacji. Po dopracowaniu całości i długich testach urządzenia prezentuje wam moim zdaniem najlepsza stacje lutownicza DIY. Poniżej zdjęcia i link ze szczegółowymi informacjami oraz plikami do konstrukcji Szczegółowy opis i materiały do pobrania
  14. 9 punktów
    Dawno, dawno temu, kiedy Internet jeszcze raczkował... Miałem niesamowite szczęście — zostaliśmy zaproszeni do zwiedzenia Studia Hensona. My — to znaczy cały zespół pewnego teatru. Łazilismy po tym studio, nasi artyści rozmawiali z ichniejszymi artystami... a ja się nudziłem jak mops. Na szczęście Cheryl Henson to zauważyła, i po mojej informacji że jestem technikiem a nie aktorem — zaprowadziła mnie do pracowni. Dużo mógłbym na tem temat mówić (nie wiem czy mi wolno, ale technologie chyba nie są już tajemnicą), w każdym razie szczękę z podłogi zbierałem jakiś tydzień. Kermit na selsynach (kto pamięta co to?), kostium misia (tego z reklamy Coli) nafaszerowany elektroniką chyba bardziej niż skafander Armstronga, tajemniczy proces tworzenia skóry Piggy czy czterdziestocalowy kineskopowy monitor w montażowni... Wtedy postanowiłem sobie, że też muszę zrobić lalkę. Może nie jestem mistrzem w konstrukcji lalek, ale coś na ten temat wiem... Problem był prosty: o ile lalki Hensona przeznaczone są do filmu (czyli trzy osoby animujące Kermita, akumulatory wystarczające na kilka minut i zasada "czego nie widzi kamera tego nie ma") — o tyle ja chciałem zrobić lalkę teatralną. Dla niezorientowanych mam proste porównanie. Lalka Hensona jest jak wyścigowy bolid, który zasuwa najszybciej jak może (ale co okrążenie trzeba mu wymienić opony razem z kołami, zatankować, najlepiej zrobić pełny przegląd a do jazdy potrzebna jest kilkuosobowa ekipa). Lalka teatralna przypomina bardziej terenówkę — wjedzie wszędzie, tankuje się raz na jakiś czas jeśli gdzieś w pobliżu jest stacja benzynowa, przegląd się robi raz przed wyjazdem, a do prowadzenia jest potrzebny kierowca sztuk jeden. A jednak postanowiłem, że coś tam podpatrzę z Hensona, coś z typowych teatralnych lalek, i może coś wyjdzie. Wybór padł na jawajkę z dwóch powodów: po pierwsze jest stosunkowo prosta w konstrukcji, po drugie osoba która miała ową lalkę animować specjalizuje się właśnie w jawajce. Od razu uprzedzam ewentualne pretensje i aluzje do worklogów: moim celem było zrobienie pełnej konstrukcji i sprawdzenie, czy animacja jest w ogóle możliwa. Ubranie i makijaż to inna sprawa, muszę wybrać czy będę płacić, czy nauczę się szyć (to drugie chyba bardziej mi odpowiada) czy w ogóle zostawię lalkę taką jaka jest na pamiątkę i wezmę się za następną; w każdym razie projekt jest z mojej strony skończony. Zacząłem od założeń. Przede wszystkim postanowiłem trochę zmienić zasady ruchu głowy. W najprostszej jawajce głowa jest nieruchoma. Dodatkowe możliwości to ruch obrotowy, ew. możliwość skłonu. O ile z ruchem obrotowym nie ma problemu, o tyle różne mechanizmy przenoszące napęd na głowę bywają chimeryczne i awaryjne (a to zerwie się gumka prostująca głowę, a to coś stanie się z linką). Dodatkowo aby zmusić lalkę do skłonu głowy potrzebna jest stosunkowo duża siła. Tak więc naturalne mi się wydało zastąpienie takiego mechanizmu elektronicznym. Chciałem również wyposażyć lalkę w możliwości w jawajkach rzadko spotykane: możliwość poruszania oczami oraz przynajmniej jedna chwytna ręka. Tu też z uwagi na samą konstrukcję jawajki i sposób animacji nie jest możliwe zrobienie tego poprzez czysto mechaniczne rozwiązania. I tak mam zaszczyt przedstawić: Maestro Zittaurus, mistrza obojga magii, dla znajomych Ziutek (dla dalszych znajomych pan Ziutek). "Kręgosłup" lalki (zastępujący tradycyjny kij) został wykonany z dwóch rurek: wewnętrzna grubościenna rurka wodociągowa PCW 17mm przenosi ruch obrotowy na głowę, a jej sztywność wystarczy aby się nie uginała w czasie poruszania lalką. Zewnętrzna to zwykła cienkościenna rurka instalacyjna, na której spoczywa ciężar głowy i do której mocowane są ramiona. Głowę zaprojektowałem w programie FaceGen 3d Print (bardzo dobrze działa pod Wine) jako solid model i OpenSCAD-em wyciąłem w niej miejsca na oczy oraz mechanizmy napędowe.Przewody do serw biegną wewnątrz rurki. Użyłem tu zwykłych serw micro TowerPro SG90 przede wszystkim ze względu na ich popularność (a poza tym kilka ich miałem). Niestety — z kilku przyczyn nie było to dobry wybór, ale o tym później. Trochę problemu sprawił mi mechanizm dłoni. Nawet najmniejsze dostępne serwa są za wielkie, aby umieścić je wewnątrz dłoni. Tym samym umieszczając serwo w przedramieniu musiałem zmienić tradycyjny sposób mocowania dłoni (sznurek) na jakiś sztywny. Myślałem na początku o czymś w rodzaju bowdena, ale w mońcu wpadłem na pomysł zastosowania podwójnego przegubu kulowego. Kule zostały wydrukowane, łączący je pręt to ucięty kawałek śruby M4 przewiercony wzdłuż wiertłem 1.8 mm. Przycisk wyzwalający zamknięcie dłoni został umieszczony w rękojeści czempuritu. Początkowo projektowałem sterowanie proporcjonalne — ale szybko zdałem sobie sprawę, że nie ma to żadnego sensu: układ dłoni animatora na rękojeściach nie pozwala na tego typu sterowanie, dużo lepszym rozwiązaniem jest po prostu przycisk, po którego naciśnięciu zmienia się stan dłoni (zamknięta/otwarta). W rękojeści została umieszczona cała elektronika: akumulator 18650 2600 mAh, ładowarka z zabezpieczeniem, przetwornica MT 3608 oraz Arduino Pro Mini. Do sterowania ruchami głowy i oczu służy płaski joystick oraz pierścień (żółty na zdjęciu) do obracania głową na boki. Joystick został umieszczony zgodnie z życzeniem animatorki — tak jest jej najwygodniej operować. Do kontroli stanu akumulatora słuzy dioda — jeśli zaczyna migać, warto rozejrzeć się za jakąś ładowarką. Nie zamieszczam schematu — w końcu to trywialne podłączenie joysticka, przycisku i trzech serw do małego Arduino. Kod jest bardzo prosty, od typowego różni się wyłącznie klasą MyServo automatycznie odłączającą serwomechanizm jeśli nie jest używany przez co najmniej sekundę: #include <Servo.h> class MyServo: public Servo { public: MyServo(int pin); void writePos(int pos); void heartbeat(void); private: bool rlyAttached; int lastPosition; int servoPin; uint32_t lastMillis; }; MyServo::MyServo(int pin) { servoPin = pin; rlyAttached = 0; lastPosition = -1; } void MyServo:: writePos(int pos) { pos = constrain(pos, 0, 180); if (pos != lastPosition) { if (!rlyAttached) { rlyAttached = true; attach(servoPin); } write(pos); lastPosition = pos; lastMillis = millis(); return; } heartbeat(); } void MyServo:: heartbeat(void) { if (rlyAttached && millis() - lastMillis > 1000UL) { detach(); rlyAttached = 0; } } #define OCZY_PIN A1 #define GLOWA_PIN A0 #define REKA_PIN A2 #define VOLT_PIN A3 #define OCZY_SRV 11 #define GLOWA_SRV 10 #define REKA_SRV 12 #define LED_PIN 13 MyServo reka(REKA_SRV), glowa(GLOWA_SRV), oczy(OCZY_SRV); #define MINVOLT 675 #define MAXVOLT 775 void setup() { reka.writePos(90); oczy.writePos(90); glowa.writePos(180); pinMode(LED_PIN, OUTPUT); pinMode(REKA_PIN, INPUT_PULLUP); } uint32_t handStateChanged; bool handState; int handLevel = 180; int headLevel = 180; int eyesLevel = 90; #define OKO_BL 500 #define OKO_BU 550 #define OKO_LL 250 #define OKO_LU 810 void getInput(void) { int v_oko = analogRead(OCZY_PIN); int v_glowa = analogRead(GLOWA_PIN); int v_volt = analogRead(VOLT_PIN); bool v_reka = digitalRead(REKA_PIN); v_volt = constrain(v_volt, MINVOLT, MAXVOLT); v_volt = map(v_volt, MINVOLT, MAXVOLT, 100, 1000); digitalWrite(LED_PIN, (millis() % 1000) < v_volt); v_glowa = constrain(v_glowa, 220,490); v_glowa = map(v_glowa, 490,220,180,0); if (v_glowa == 180 || v_glowa == 0 || abs(v_glowa - headLevel) >10) { headLevel=v_glowa; } v_oko = constrain(v_oko, OKO_LL, OKO_LU); if (v_oko < OKO_BL) v_oko = map(v_oko,OKO_LL, OKO_BL,0,90); else if (v_oko > OKO_BU) v_oko = map(v_oko,OKO_BU, OKO_LU,90,180); else v_oko = 90; if (v_oko == 0 || v_oko == 180 || v_oko == 90 || abs(v_oko - eyesLevel) >= 6) { eyesLevel = v_oko; } if (v_reka == handState) return; if (millis() - handStateChanged < 200UL) return; handState = v_reka; handStateChanged = millis(); if (!handState) { handLevel=handLevel?0:180; } } void loop() { getInput(); reka.writePos(map(handLevel,0,180,0,90)); glowa.writePos(headLevel); oczy.writePos(180-eyesLevel); } Ziutek wyszedł mi całkiem nieźle (tzn. realizuje wszystko co założyłem), a teraz kolej na możliwe poprawki: W rękojeści powinien się znaleźć wyłącznie stosunkowo ciężki akumulator wraz z ładowarką. Arduino i przetwornicę należałoby podwiesić do rurki na wysokości tułowia lalki. W tym przypadku nie wchodzi już w grę przeprowadzenie przewodów wewnątrz rurki, ale rozwiązanie tego nie powinno być problemem. Dodatkowo należałoby wyprowadzić na jakimś gnieździe interfejs UART. Przycisk sterujący otwarciem/zamknięciem dłoni powinien być zdublowany na rękojeści. Sterowanie ruchami głowy powinno być możliwe za pomocą jednego palca. W ten sposób uzyskam pewniejszy chwyt rękojeści i możliwość łatwiejszego operowania manipulatorami oczu i dłoni. Sama rękojeść powinna być dopasowana do dłoni konkretnego animatora. No i oczywiście należy zastosować inne serwa; jakie — o to już będę pytał we właściwym dziale. W razie pytań jestem do dyspozycji.
  15. 9 punktów
    Aktualizacja: poniższy post został opublikowany przed wydzieleniem części wiadomości do kosza. Już na samym początku prosiłem o to, aby darować sobie zgryźliwości. Jeszcze raz ponawiam moją prośbę i obiecuję od teraz usuwać wszelkie posty, które będą miały nawet jedno złośliwe zdanie. Niezależnie od tego czy autorem będzie nowa osoba, czy użytkownik, który ma na koncie 100 tysięcy postów - każdego obowiązują takie same zasady. To, że opisywany projekt jest częściowo reklamą tego projektu jest chyba oczywiste - wystarczy poświęcić kilka sekund i obejrzeć filmy, które są w pierwszym poście. Nie przypominam sobie, aby gdzieś był zakaz publikacji tego typu opisów. Nie jest to produkcja masowa, nie widzę więc powodu, aby od razu "rzucać" się na autora i krytykować za wszystko co możliwe. Czym taki projekt różni się od innych DIY, w których autorzy nie podają dokładnej instrukcji budowy i nie dzielą się projektami? Moim zdaniem niczym. Ten projekt wypada nawet lepiej, bo jak ktoś będzie bardzo chciał to sobie chociaż może kupić takie urządzenie. Prawie nikt nie zadał konkretnych pytań technicznych, pojawiały się tylko różne uwagi "byle udowodnić, że projekt jest zły". Autor próbował Wam wszystko wyjaśnić, wstawiał zdjęcia i filmy, a dla Was nadal jest źle. Rozumiem, że komuś projekt może się nie podobać, wystarczy raz wyrazić swoje zdanie lub zignorować temat. Jak widać po głosach czytelników, raportach oraz otrzymanych przeze mnie wiadomościach jest sporo osób, którym ten projekt się zwyczajnie podoba. Proszę więc wszystkich krytyków, aby wzięli pod uwagę, że nie każdy musi się z Wami zgadzać, niektórym taki projekt się podoba i tyle. Nie musicie na siłę wszystkich przekonywać, że jest źle i atakować ciągle autora. Projekt ten zaakceptowałem świadomie i widziałem, że jest to częściowo reklama. Uznałem jednak, że projekt jest ciekawy, nie było u nas opisu takiej maszynki. Liczyłem więc, że takie DIY może być dla wielu osób ciekawą inspiracją. Autor nie jest pierwszą osobą na świecie, która zrobiła takie urządzenie. W Internecie jest sporo maszynek do ręcznej produkcji filamentu. Jak ktoś będzie chciał to wykona urządzenie samodzielnie. To czy jest sens sprzedawać taka maszynkę to już zweryfikuje rynek - jak będą klienci to będzie sens, a jak nie będzie zainteresowanych to trudno, przynajmniej ktoś próbował. Dziwie się osobom, które poświęcają tyle czasu na pisanie krytyki i szukanie zaczepek. Ciężko mi uwierzyć, że liczą one na merytoryczną dyskusje. Nie macie ciekawszych zajęć od krytykowania projektów? Jeśli macie gorszy dzień lub nadmiar energii to proponuje skupić się na chwaleniu DIY, które według Was są ciekawe. Sprawdźcie listę opisanych projektów, znajdźcie coś ciekawego i napiszcie autorowi, że robi dobrą robotę. Nie widzę wielu postów, które chwaliłyby ciekawe DIY - tak ciężko napisać komuś dobre słowo? Jeszcze raz apeluję o ograniczenia zgryźliwość, a wszystkim będzie przyjemniej czytać posty i więcej osób będzie chętnie brało udział w dyskusjach. Zachęcam również osoby, które nie zgadzają się z takimi złośliwościami, aby wyrażały to jakoś publicznie (np. przez posty lub reakcje). Oczywiście doceniam raporty i PW kierowane do mnie, ale może jednak byłoby dobrze, aby autorzy takich wiadomości widzieli, że to "nie są tylko moje wymysły", a jest wiele osób, którym taki ton dyskusji zwyczajnie nie odpowiada.
  16. 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
  17. 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
  18. 8 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
  19. 8 punktów
    W poprzedniej części zobaczyliśmy jak działa wyświetlacz TFT podłączony do Arduino Uno. Wiemy już, że nie jest to demon szybkości, czas przeanalizować nieco dokładniej przyczyny takiego, a nie innego działania. Ta część będzie nieco bardziej techniczna od poprzedniej, znajdziemy w niej więcej obliczeń oraz odniesień do dokumentacji. Niestety pewna dawka "teorii" będzie konieczna dla zrozumienia działania wyświetlacza oraz poprawienia wydajności. Spis treści: Sterowanie wyświetlaczem TFT - część 1 - wstęp, podstawowe informacje Sterowanie wyświetlaczem TFT - część 2 - analiza problemu Sterowanie wyświetlaczem TFT - część 3 - testy prędkości na STM32 Sterowanie wyświetlaczem TFT - część 4 - własny program Sterowanie wyświetlaczem TFT - część 5 - optymalizacja programu Dlaczego to tak wolno działa? Na początek coś prostego, czyli oszacowanie ilości danych koniecznych do przesłania. Dla porównania zacznijmy od wyświetlacza alfanumerycznego, takiego jak był używany w kursie Arduino. Wyświetlacz posiada dwa wiersze po 16 znaków, a każdy znak jest przesyłany jako bajt. Mamy więc razem 32 bajty danych. Mnożąc przez 8 bitów w każdym bajcie otrzymujemy więc 256 bitów. W kursie STM32 F1 używany był graficzny wyświetlacz o rozdzielczości 84 na 48 pikseli. Każdy piksel mógł być albo zapalony, albo wygaszony, więc do jego sterowania wystarczał jeden bit. Mamy więc 84 x 48 = 4032 bity, czyli inaczej 504 bajty danych. 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. Teraz czas na bohatera tego artykułu - domyślna biblioteka używa trybu 16-bitowego koloru, a rozdzielczość ekranu to 160 na 128 pikseli. Mnożymy więc 160 x 128 x 16 i uzyskujemy wynik 327680 bitów, albo 40960 bajtów. Z tych ogólnych rachunków łatwo wywnioskować, że aby wyświetlić obraz na naszym ekranie TFT musimy przetworzyć 1280 razy więcej danych niż w przypadku wyświetlacza alfanumerycznego. To chyba jest najprostsza i najbardziej ogólna odpowiedź na pytanie dlaczego jest wolno... Oczywiście nie jest to pełna odpowiedź, ale mam nadzieję że pokazuje ona o ile trudniej jest szybko wysterować wyświetlacz TFT. Jak szybko powinien działać nasz program? Poprzednio zmierzyliśmy czas rysowania ekranu i wyszło nam ponad 1200 ms, kasowanie zawartości zajmowało ok. 96 ms. Spróbujmy teraz oszacować jak szybko "powinien" nasz program działać, albo raczej jaka jest granica związana z prędkością interfejsu SPI. W konfiguracji interfejsu widzimy linijkę: SPI.setClockDivider(SPI_CLOCK_DIV2); A jak wiemy Arduino UNO jest taktowane z częstotliwością 16 MHz. Dzieląc przez dwa, otrzymujemy 8 MHz dla interfejsu SPI, co daje 8'000'000 bitów na sekundę. Skoro dane obrazu wymagają 327800 bitów, więc teoretycznie przesłanie danych zajmuje 327800 / 8000000 = 0,041 s, czyli 41 ms. Taki czas dawałby nam przyzwoite 24 klatki na sekundę, ale nawet kasowanie ekranu działa ponad dwa razy wolniej niż powinno, o rysowaniu nawet lepiej nie wspominać. Analiza procedury kasowania ekranu Zacznijmy od podłączenia analizatora stanów logicznych i upewnienia się, że wszystko działa jak oczekujemy. Na początek pomiar czasu kasowania ekranu - łatwo go zmierzyć, bo wysyłamy kod koloru białego czyli same bajty 0xff: Na razie wszystko wygląda zgodnie z oczekiwaniami. Sprawdźmy więc, czy na pewno SPI pracuje z częstotliwością 8 MHz. Częstotliwość się zgadza, ale niemiłym zaskoczeniem są przerwy między wysyłanymi bajtami danych: Niestety, ale między wysłaniem kolejnych bajtów procesor musi mieć trochę czasu - na odczyt statusu zakończenia transmisji, powrót z procedury, załadowanie kolejnej danej, a to wszystko trwa. Optymalizacja programu jest oczywiście możliwa, ale wymagałaby rezygnacji z użycia klasy SPI dostarczanej przez Arduino oraz chyba wskazane byłoby dodanie wstawek w asemblerze. W każdym razie taka optymalizacja nie jest prostym zadaniem, to niewątpliwie ciekawe wyzwanie, ale niekoniecznie przeznaczone dla początkujących. Na tą chwilę pozostaje się chyba pogodzić z tym, że maksymalna transmisja to nie 8 Mb/s, ale około połowa tej prędkości. To nadal całkiem niezły wynik, gdyby rysowanie zajmowało tyle samo czasu, co kasowanie mielibyśmy 10 klatek na sekundę. Zobaczmy więc dlaczego rysowanie jest tak strasznie powolne. Sterownik obrazu Nasz wyświetlacz wyposażony jest w kontroler ST7735S, poprzednio podawałem do niego link, ale na wszelki wypadek podam jeszcze raz. Zachęcam do zapoznania się z całą dokumentacją, ale chwilowo opiszę tylko absolutne minimum niezbędne z zrozumienia sterowania oraz poprawienia wydajności programu. Do komunikacji z ST7735S można użyć jednego z czterech interfejsów. Dostępne są dwa interfejsy równoległe (6800/8080) oraz dwa szeregowe, określane jako 3- i 4- przewodowy. W przypadku opisywanego modułu niestety nie ma wyboru i dostępny jest tylko ostatni, czyli "4-line Serial". Jak pisaliśmy wcześniej jest to SPI, ale nie do końca... co więcej nazewnictwo linii w module, standardzie SPI i dokumentacji sterownika jest inne. Poniżej przykładowy diagram przedstawiający komunikację: Linia CSX to odpowiednik linii CS zarówno w module wyświetlacza, jak i standardzie SPI. Nazwa SDA jest najczęściej używana w interfejsie i2c, ale tutaj oznacza po prostu linię danych, która na module ma nazwę DIN, a łączymy ją z pinem MOSI (Master-Output-Slave-Input). Warto pamiętać, że SDA może być używana też do odczytu danych... Zostańmy jednak przy jednokierunkowej transmisji. Kolejna linia to D/CX, na naszym module oznaczona jako DC - która nie jest częścią standardu SPI, więc pozostaje ją połączyć z pinem GPIO i sterować "ręcznie". Na koniec SCL, czyli linia zegara, na module dostępna jako CLK, a standardowo nazywana SCLK, albo SCK. Mamy więc straszny bałagan w nazwach, ale na koniec w sumie prostą sytuację - jednokierunkowy interfejs SPI oraz dodatkową linię DC. Jak widzimy transmisja polega na przesyłaniu 8-bitowych bajtów, czyli nic strasznego. Sterowanie ST7735S polega na przesyłaniu do niego komend. Pierwszy bajt zawiera wówczas kod komendy, a podczas jego przesyłania linia DC powinna być ustawiona w stan niski. Same kody dostępnych poleceń znajdziemy w dokumentacji sterownika, przykładowo: Po niektórych komendach przesyłane są parametry - podczas ich transmisji linia DC musi być w stanie wysokim. To ile parametrów jest potrzebnych znajdziemy w dokumentacji. Obsługiwanych komend jest dużo, na szczęście nie musimy ich wszystkich od razu poznawać. Dostarczony program przykładowy zawiera kod niezbędny do uruchomienia wyświetlacza, właściwie jedyne na co warto w nim zwrócić na początek uwagę to możliwość zmiany orientacji wyświetlacza. To bardzo wygodna opcja, dzięki niej nasz wyświetlacz może pracować zarówno w pionie (jak w przykładach z poprzedniej części), jak i w poziomie, co moim zdaniem ładniej wygląda. Ale najważniejsze, że wszystko jest wspierane sprzętowo, nie musimy więc programowo obracać obrazu. Przesyłanie danych obrazu Inicjalizacja wyświetlacza jest wykonywana tylko raz, więc w naszych amatorskich zastosowaniach jej optymalizacja może być mniej istotna. Warto natomiast zrozumieć jak wygląda przesyłanie obrazu. Do tego celu wykorzystywane są trzy komendy: CASET (0x2a), RASET (0x2b) oraz RAMWR (0x2c). Pierwsze dwie definiują wielkość okna, do którego będziemy zapisywać dane: Gdy przesyłamy CASET podajemy dwa parametry (każdy 2-bajtowy), które ustalają współrzędne x początku i końca okna, RASET ustala współrzędne y. Ustalenie wielkości okna jest konieczne przed rozpoczęciem zapisu danych. Do zapisu używamy komendy RAMWR: Po wysłaniu RAMWR możemy przesyłać już dane obrazu. Jak widzimy to nic specjalnego. Dlaczego więc kod przykładowy działa tak wolno? Teraz gdy wiemy już trochę więcej o działaniu ST7735S, możemy wrócić do kodu dostarczanego przez WaveShare. Na początek metoda LCD_ST7735S::LCD_SetWindows: void LCD_ST7735S::LCD_SetWindows( POINT Xstart, POINT Ystart, POINT Xend, POINT Yend ){ //set the X coordinates LCD_WriteReg ( 0x2A ); LCD_WriteData_8Bit ( 0x00 ); //Set the horizontal starting point to the high octet LCD_WriteData_8Bit ( (Xstart & 0xff) + sLCD_DIS.LCD_X_Adjust); //Set the horizontal starting point to the low octet LCD_WriteData_8Bit ( 0x00 ); //Set the horizontal end to the high octet LCD_WriteData_8Bit ( (( Xend - 1 ) & 0xff) + sLCD_DIS.LCD_X_Adjust); //Set the horizontal end to the low octet //set the Y coordinates LCD_WriteReg ( 0x2B ); LCD_WriteData_8Bit ( 0x00 ); LCD_WriteData_8Bit ( (Ystart & 0xff) + sLCD_DIS.LCD_Y_Adjust); LCD_WriteData_8Bit ( 0x00 ); LCD_WriteData_8Bit ( ( (Yend - 1) & 0xff )+ sLCD_DIS.LCD_Y_Adjust); LCD_WriteReg(0x2C); } Można oczywiście dyskutować, czy używanie "magicznych liczb" to elegancki sposób programowania, ale jak widzimy są tutaj wykonywane trzy, znane nam komendy: CASET, RASET oraz RAMWR. Więc po wywołaniu tej metody możemy już przesyłać dane obrazu (używając np. metody LCD_SetColor). To brzmi całkiem nieźle, ale teraz zobaczmy implementację rysowania punktu: void LCD_ST7735S::LCD_SetPointlColor ( POINT Xpoint, POINT Ypoint, COLOR Color ){ if ( ( Xpoint <= sLCD_DIS.LCD_Dis_Column ) && ( Ypoint <= sLCD_DIS.LCD_Dis_Page ) ){ LCD_SetCursor (Xpoint, Ypoint); LCD_SetColor ( Color , 1 , 1); } } void LCD_ST7735S::LCD_SetCursor ( POINT Xpoint, POINT Ypoint ){ LCD_SetWindows ( Xpoint, Ypoint, Xpoint , Ypoint ); } Czyli chcąc narysować jeden punkt najpierw ustawiamy okna o wielkości 1x1, a następnie zapisujemy dwa bajty koloru. Czyli jak łatwo policzyć na jeden piksel przesyłamy 13 bajtów. Pomijając niezbyt optymalny kod, to właśnie jest przyczyna tak strasznie powolnego rysowania. Każda linia, okręg, a nawet znak rysowane są jako punkty. A każdy z tych punktów wymaga aż 13 transmisji przez SPI (po których są jeszcze przerwy). To wszystko daje nam rysowanie demonstracyjnego ekranu w czasie ponad sekundy zamiast 40-90 ms. Co ciekawe kasowanie obrazu jest wykonywane optymalniej - raz ustawiane jest okno na cały ekran, a następnie po prostu przesyłane są wszystkie dane. Dlatego zajmuje to 96, a nie 1000 ms. Podsumowanie Mam nadzieję, że w tej części udało mi się pokazać dlaczego sterowanie wyświetlaczem działa wolniej niż moglibyśmy tego oczekiwać. Okazuje się, że kod dostarczany przez producenta to raptem demo. Jeśli chcemy sensownie sterować wyświetlaczem, musimy napisać własny program. Możliwości optymalizacji to między innymi: ograniczenie odrysowywanego obszaru zwiększenie prędkości komunikacji przez SPI (wyświetlacz obsługuje do 15MHz, wypadałoby również wyeliminować przerwy między wysyłanymi bajtami) o wiele wydajniejsze jest jednoczesne odrysowywanie okna, rysowanie punktów działa strasznie wolno Użyte rzez Waveshare rozwiazanie ma za to jedną, niezaprzeczalną zaletę - używa bardzo mało zasobów mikrokontrolera. Wszystkie dane obrazu są od razu wysyłane, więc za cenę prędkości ograniczyliśmy wykorzystanie pamięci. W kolejnej części przetestuję działanie przykładów dla STM32 oraz pokażę jak można przyspieszyć rysowanie obrazu - chociaż już nie na Arduino Uno. Spis treści: Sterowanie wyświetlaczem TFT - część 1 - wstęp, podstawowe informacje Sterowanie wyświetlaczem TFT - część 2 - analiza problemu Sterowanie wyświetlaczem TFT - część 3 - testy prędkości na STM32 Sterowanie wyświetlaczem TFT - część 4 - własny program Sterowanie wyświetlaczem TFT - część 5 - optymalizacja programu
  20. 8 punktów
    Idea działania Komora jonizacyjna, to urządzenie składające się z dwóch elektrod do których doprowadzane jest stałe napięcie, co powoduje powstanie pola elektrycznego w jej środku. Gdy kwant promieniowania "uderzy" w atom gazu znajdującego się w komorze, "rozbija" go na dwa jony (dodatni i ujemny), które są przyciągane do elektrod (dodatni do ujemnej i vice versa). Mierząc prąd płynący między dodatnią, a ujemną elektrodą, będzie on proporcjonalny do ilości tych jonów, zaś to będzie proporcjonalne do mierzonego promieniowania. Utrudnieniem jest, że wspomniane prądy są małe, więc wymagają dużego wzmocnienia, co zaś wymaga m.in. starannego filtrowania zasilania, oraz ekranowania, by urządzenie się nie wzbudzało. Poniżej znajduje się schemat blokowy prezentowanego rozwiązania. Część elektroniczna Pierwotnie do wytworzenia napięcia polaryzującego, miałem w planach użycie przetwornicy, jednak okazało się, że napięcie 12V z baterii jest wystarczające. Sygnał z komory trafia do wzmacniacza transimpedancyjnego, w pętli sprzężenia znajdują się szeregowo dwa rezystory o wartości aż 50G. Połączenie owych rezystorów, elektrody komory jonizacyjnej i nóżki wzmacniacza musi być wykonane w powietrzu, by uniknąć pasożytniczych rezystancji. Poniżej znajduje się render w KiCADie, oraz rzeczywiste urządzenie (wiem, fotka jest dość niskiej jakości). Część programistyczna Sygnał analogowy trafia do przetwornika ADC, i jest zbierany przez procek - tu użyłem dość mało popularnego STM8. Zaskoczeniem było dla mnie, że GCC nie obsługuje tych procesorów, zaś poświęcony im SDCC nie umie wycinać z binarki funkcji, które nie są używane! Do komunikacji z hardwarem użyłem stdperiph. Komunikacja z światem zewnętrznym jest jednokierunkowa (urządzenie wysyła pomiary co kilka sekund) za pomocą UARTa. Założeniem było, by urządzenie można było pozostawić same sobie i zdalnie je flashować, czy też pobierać z niego dane. W skrócie, wolę programować leżąc w łóżku, niż garbiąc się nad stołem pełnym kabli :) Zostało to zrealizowane przez dodanie Raspberry Pi, do której podpięty jest zarówno programator, jak i przelotka UART/USB. Soft na procka napisałem w C. Pobieranie danych po stronie maliny jest wykonywane przez skrypt w Pythonie, dane są wizualizowane skryptem w R. Użyłem R, mimo, że Python też ma biblioteki do tworzenia wykresów, bo bardzo podobają mi się wykresy w R. Dokumentacja powstała w LATEXie. Całość dostępna jest na GitHubie - zapraszam do odwiedzenia Wyniki Poniżej znajdują się dane pomiarowe zebrane za pomocą urządzenia.
  21. 8 punktów
    Stacja meteorologiczna służy do przeprowadza dokładnych pomiarów pogody oraz sprawdzania jakości powietrza. Urządzenie pobiera dane z czujników, następnie zapisuje je do bazy danych po czym zostają wyświetlone na stronie internetowej. Całe urządzenie zostało zamknięte w obudowie wydrukowanej w drukarce 3D. Czujniki zainstalowane w urządzeniu pobierają dokładne dane pogodowe. Stacja posiada zaawansowaną metodę pomiaru prędkości wiatru przy użyciu ultradźwiękowych czujników ruchu. Stacja działa na Raspberry PI 3+, obsługuje również starsze modele (z wbudowanym wifi) oraz na Raspberry PI ZERO (W). System operacyjny to Linux wersja Raspbian STRETCH LITE bez interfejsu graficznego. Kod źródłowy czujników został napisany w Python’ie. Dane z czujników zapisywane są przy użyciu Raspberry PI do bazy danych MySQL. Następnie zostają wyświetlone w aplikacji internetowej, która napisana została w PHP. Urządzenie wymaga połączenia z Internetem. Aktualnie wykorzystywane jest połączenie poprzez WIFI. Komunikacja pomiędzy urządzeniem a administratorem przeprowadzana jest poprzez protokół SSH i FTP. Stacja jest zbudowana w taki sposób, żeby użytkownik mógł w łatwy sposób ją obsługiwać. Aby włączyć stację należy podłączyć ją do prądu. Działanie urządzenia zostanie zasygnalizowane świeceniem diody (czerwonej) oraz miganiem diody zielonej, która świeci przy wysyłaniu danych do bazy. Oprócz graficznego przedstawienia danych aplikacja posiada skrypty do obliczenia m. in. wschodu i zachodu słońca w danej lokalizacji. Oprogramowania posiada opcje, w których m. in. możemy ustawić czas pomiaru pogody i zapisu do bazy danych. Jest to ważne ponieważ możemy sami ustalać jak często stacja ma sprawdzać stan pogody Projekt obudowy Obudowa została zaprojektowana w programie FreeCAD. Składa się ona z 9 elementów. Została wydrukowana w drukarce 3D – Anet A8 (moja własna drukarka). Materiał wykorzystany podczas druku to PLA, temperatura druku 205°C. Łączny czas druku wynosi 19 godzin, zużywa 174 gram materiału przy wysokości warstwy 0.2mm. Projekt obudowy został wykonany w taki sposób, aby urządzenie było odporne na deszcz i wiatr. Opływowość stacji pozwala na wykonywania dokładnych pomiarów prędkości wiatru. Na samej górze stacji zamontowany został czujnik opadów deszczu oraz czujnik natężenia światła. Następnie pod nimi umieszczone są ultradźwiękowe czujniki prędkości wiatru. Kolejny element to obudowa chroniąca RB PI i elektronikę. Obudowa posiada specjalne mocowanie na RP PI, które sztywno trzyma urządzenie. Następnym elementem jest rdzeń, do którego przymocowane są pozostałe czujniki. Obudowę zamyka podstawka, w której znajduję się główny przewód zasilający oraz diody sygnalizujące działanie. Czujniki Urządzenie w czasie rzeczywistym pobiera dane z 7 czujników, następnie są one w odpowiedni sposób przekazywane do modułu detektorów i mikrokontrolerów, które zwracają dane do Raspberry PI. Lista czujników: Czujnik opadów Czujnik pomiaru opadów atmosferycznych składa się z dwóch części: sondy pomiarowej „YL-83” oraz modułu detektora „LM393”, który posiada wyjście analogowe umożliwiające pomiar deszczu. Moduł zasilany jest napięciem 5V. Czujnik natężenia światła Czujnik światła bazuje na fotorezystorze „GL5537-1”. Jest to opornik fotoelektryczny, który zmienia swoją rezystancję pod wpływem padającego światła. Prędkość wiatru Pomiar prędkości wiatru bazuje na ultradźwiękowym czujniku odległości „HC-SR04”. Ultradźwiękowy pomiar prędkości polega na zmierzeniu różnicy czasu przejścia impulsów ultradźwiękowych propagujących w kierunku przeciwnym do kierunku przepływu. Temperatura i wilgotność Do wykonywania pomiaru temperatury i wilgotności powietrza został wykorzystany popularny moduł „DHT11”. Moduł składa się z czujnika oraz niezbędnego do poprawnego działania układu: rezystora i kondensatora filtrującego napięcie zasilania. Ciśnienie Moduł z cyfrowym barometrem „BMP180” wykonuje pomiar ciśnienia w zakresie od 200hPa do 1100hPa. Układ komunikuje się przy użyciu interfejsu IC2, co zapewnia wysoką dokładność i stabilność wykonywanych pomiarów. Jakoś powietrza Moduł jakości powietrza „MQ-135” wykrywa w atmosferze: benzen, amoniak (NH3) oraz dwutlenek węgla (CO2). Inne: Przetwornik A/C i C/A 8-bitowy I2C Głównym układem przetwarzania danych w tej pracy jest przetwornik PCF8591. Moduł posiada czterokanałowy przetwornik analogowo-cyfrowy działający w oparciu o 8-bitowy systemem akwizycji danych. Komunikacja opiera się na szeregowej wysyłce danych za pomocą dwukierunkowej magistrali I2C.
  22. 8 punktów
    Zgodnie z nazwą ten robot nie miał powstać. Jego historia zaczyna się w maju 2019 roku. Został mi wtedy przyznany przez Wydział Elektroniki Mikrosystemów i Fotoniki Politechniki Wrocławskiej grant na realizację robota kategorii nanosumo. Postawiłem sobie jako deadline na pierwszy działający prototyp Robocomp 2019. Wtedy myślałem że to dużo czasu. Niestety koła zębate biurokracji wolne są, Digikey nie miało kluczowej części a na mój brak czasu i problemy z nim zarządzaniem już na łamach tego forum narzekałem. Złożyło się to na brak funkcjonalnej wersji czegokolwiek na tydzień przed konkursem. Na szczęście w zamówieniu było trochę modułów a w moich szufladach jeszcze więcej. No i druk 3d uratował dzień jak zawsze: Jako napęd robota posłużyły cztery silniki sub-micro 26:1. Cztery silniki dają sporą mechaniczną przewagę w *sumo ponieważ pozwalają na przeniesienie przez koła praktycznie całej masy robota, a jak wiadomo Td = µN. Do tego zamontowałem opadający pług który dociążyłem około połowy wysokości metalową ośką 1mm co podniosło jego środek ciężkości. Okazał się bardzo skuteczny, szczególnie przeciwko robotowi z stałym pługiem dość daleko od ziemi z którym walczyłem w półfinale. Aby ciężar wyniósł jak najbliżej 100g w robocie umieściłem około 30 nakrętek M5 bo akurat były pod ręką. Początkowo silnikami miał sterować chiński moduł opisywany jako mini-L298N do którego dokumentacja prawdopodobnie nie istnieje ale po tym jak nie zadziałał od ręki wymieniłem go na 2x Pololu DRV8838. Prawdopodobnie źle go podłączyłem (autopsja w planach) przez co na jednym silniku nie działała praca w tył. Zostało to "rozwiązane" programowo. Sercem robota było Arduno Pro Micro (wybrane ze względu na to że leżało pod ręką) zasilane przez przetwornicę step-up do USB poddaną subtelnej modyfikacji kombinerkami z pojedynczej celi LiPo (która zasilała też mostek H bezpośrednio). No i ostatecznie jedyna ciekawa rzecz w tym robocie - VL53L1X-SATEL - breakouty do świetnych czujników odległości TOF od ST. Zdążyłem tylko wstępnie ogarnąć ich możliwości ale wydają sioę bardzo obiecujące. Niestety zupełnie nie widziały robota przeciwnika (wydrukowanego 3D) w finale, przyczyny zostaną zbadane. Kod powstał w autobusie do Krakowa i strefie dla zawodników. Jest napisany w bardzo nieeleganckim Arduino C++. Jego logika składa się z 3 if-ów. Działa. W "najbliższym" czasie powinny się pojawić worklog do nanosumo bo tam planuję sporo ciekawsze rozwiązania jak customowe elastyczne PCB. Pewnie jeszcze tu wrócę i trochę poprawię.
  23. 8 punktów
    Arduino jest taką fajną platformą, że chyba podoba się tylko osobom zainteresowanym. Dlatego też moja żona zawsze skacze z radości, gdy dokupuje sobie nowe przekaźniki, czujniki czy też „cholerne kable walające się po szafkach”. Ja natomiast uwielbiam wykonywania przedmiotów użytkowych, mających zastosowanie w życiu codziennym. Wiem, że system podlewający zioła nie jest niczym nowym i skomplikowanym, jednakże wbrew pozorom dla człowieka nie mającego wiele wspólnego z programowaniem i elektroniką, może być kłopotliwe. Tym bardziej, że informacje ułatwiające złożenie takiego zestawu są rozproszone w sieci i nie znalazłem jeszcze dobrego tutorial -a po polsku. Dlatego postaram się opisać to jak najdokładniej, aby ktoś taki jak ja mógł to zrozumieć Projekt na początku miał kilka założeń: Znalezienie doniczki, która będzie dobrze wyglądała (nie zostanie skazana na banicję przez innego mieszkańca) Doniczka musi mieć zbiorniki na wodę na tyle duże, żebym nie musiał martwić się podlewaniem ziół w kuchni, które umierały mi bardzo szybko – albo za mało wody albo odwrotnie. System podlewania ma być indywidualny dla każdego rodzaju z ziół i działać raz lub dwa dziennie. Wszystko musi być najtańsze jak się da. Projekt musi dobrze wyglądać. Znalezienie odpowiedniej doniczki, która zmieści się na szafce (i będzie dobrze wyglądać) okazał się niemożliwy do zrealizowania. Nic takiego na rynku nie ma, dlatego też musiałem zrobić ją sam. Najkorzystniejszym cenowo materiałem okazało się PCV o grubości 3 mm. Występuje w kolorze białym i czarnym, są sklepy internetowe gdzie możemy zamówić już przycięte formatki. Poniżej pokazuję projekt doniczki i rozpisane wielkości formatek. Formatki z PCV potrzebne do wykonania doniczki ze zbiornikiem wody (wymiary w mm, grubość 3 mm): 183 x 200 mm – 2 szt 400 x 180 mm – 2 szt 400 x 200 mm – 1 szt 392 x 120 mm – 3 szt Zdjęcia i projekt: Elementy kleimy najpierw kropelką/super glue lub innym klejem kontaktowym. Później uszczelniamy połączenia poprzez nakładanie kleju do rur PCV (z atestem dla instalacji wodnej) za pomocą strzykawki. Po prostu nabieramy klej i grubą warstwę wciskamy w szczeliny. Klej schnie 24h, a każdy z elementów trzeba zabezpieczyć oddzielnie, co więcej nie ma możliwości aby zrobić to już po złożeniu doniczki – nie będziemy mieć dostępu do niektórych krawędzi. Dlatego uszczelniać klejem do PCV należy etapami. Na końcu warto zrobić test szczelności, w moim przypadku robiłem 3 poprawki. Również warto przed wklejeniem tylnej ściany doniczki zamontować do niej pompki wody oraz wyprowadzić okablowanie na zewnątrz. W moim przypadku po prostu wywierciłem dziurę, którą uszczelniłem klejem do PCV. Pompki mogą być dowolne, pracujące z odpowiednim napięciem dla naszego źródła zasilania (u mnie 5V) Ponieważ doniczka z PCV nie spełnia przynajmniej dwóch punktów z założeń projektu. Musiałem wykombinować osłonkę, która będzie wodoodporna i umożliwi dolewanie wody od góry pojemnika. Padło na hexagonalną sklejkę, akurat najdroższy z elementów tego projektu. Formatki, osłonka drewniana (wymiary w mm, grubość 15 mm): 220 x 215 – 2 szt 415 x 215 – 1 szt 415 x 40 – 1 szt 415 x 190 – 1 szt 50 x 415 – 1 szt 35 x 415 – 1 szt Wszystko jest połączone na konfirmaty bo tak mi pasowało wizualnie. Można zastosować też wkręty lub po prostu ją skleić na kołki. Elementy elektroniczne: Wemos D1 mini (lub inna płytka oparta na ESP8266, np. NodeMCU) Czujnik wilgotności gleby Przekaźnik z logiką 3,3V Moduł z zasilaniem 5v i 3,3V Pompki akwarystyczne zasilane z 5V Przetwornik ADC - ADS1115 Tylna ściana doniczki specjalnie ma niepełne plecy, aby łatwo było zamontować całą elektronikę. Do sterowania wykorzystałem D1 Mini oparty na ESP8266 ze względu na wbudowane wifi. Ponieważ wykorzystuje 3 sztuki czujników wilgotności gleby, niezbędny był przetwornik ADS1115, który może odczytywać sygnał ADC dla czterech urządzeń. To ma znaczenie bo D1 Mini mógłby obsługiwać tylko jeden z nich, więc musiałbym w projekcie wykorzystać aż 4 takie urządzenia. Dodatkowo wprowadziłem zewnętrzny układ zasilania (pompki nie dawały rady przy natężeniu prądu z D1) oraz przekaźniki uruchamiające pompki w zależności od wilgotności. Sterowanie oparłem na gotowym rozwiązaniu ESP Easy z którego korzystam już przy innych czujnikach i które trochę już znam. Tak jak pisałem wcześniej, nie potrafię programować i nie to mnie kręci w tej platformie. Natomiast ESP Easy posiada web UI i ogólnie jest łatwo konfigurowalne bez znajomości języka programowania. W tym projekcie jedyne czego trzeba się nauczyć to reguły, banalnie proste do zrozumienia. Instalacja i konfiguracja jest dobrze opisana na stronie głównej projektu: https://www.letscontrolit.com/wiki/index.php/ESPEasy Tak wygląda strona z urządzeniami w ESP po odpowiedniej konfiguracji. Pierwsze trzy to czujniki wilgotności, kolejne trzy to przełączniki do sterowania przekaźnikami uruchamiającymi pompki. Jest jedna rzecz, której nie znalazłem w żadnym poradniku dotyczącym przygotowania systemu do podlewania kwiatków. Jest to kalibracja czujników. Powinno to być oczywiste i logiczne, jednak dla mnie nie było. Na początku przyjąłem, że wartości maksymalne przetwornika należy przyjąć jako referencyjne i na podstawie tego określać procent wilgotności gleby. Mój błąd był większy niż moje samozadowolenie. Dopiero po ciemnej stornie anglojęzycznego internetu znalazłem informacje o kalibracji i jej sposobie. Należy sprawdzić jego wskazania w suchej ziemi oraz w mokrej. Więc najlepszym sposobem jest wysuszenie odpowiedniej ilości w piekarniku: Zanotowaniu wskazań dla wysuszonej i ostygniętej ziemi ( przesypałem ją do słoika żeby odpowiednio wsadzić czujnik). A później powtórzeniu pomiarów dla ziemi mokrej ale bez stojącej gleby. Później wystarczy wpisać nasze wskazania w webUI ESP Easy, która ma już dwupunktową kalibrację dla przetwornika ADS1115 i wskazaniu naszych pomiarów. Przy pomiarze gdzie była sucha ziemia wpisujemy 0, przy mokrej 100. Dzięki temu mamy od razu podany wynik w procentach. Ostatnim etapem jest napisanie reguł sterujących podlewaniem. W moim przypadku codziennie o 7.00 rano dokonywany jest pomiar wilgotności gleby, jeśli wilgotność jest niższa niż 55% (lub 45% w przypadku jednego z nich) uruchamiana jest pompka na czas kilku sekund. Później odczekuje 30 sek i znowu dokonuje pomiaru. Jeśli wilgotność jest niższa, znowu dostarczana jest woda. Algorytm powtarza się łącznie 3 razy dla każdego z czujników. Wystarcza to spokojnie aby utrzymać odpowiednią wilgotność gleby. Oczywiście można było się pokusić o wprowadzenie zmiennych zależnych od odchylenia od normy. Nawet nie byłoby to bardzo trudne, jednakże całość miała być prosta. Reguły: On Clock#Time=All,07:00 do // codziennie o 7.00 rano wykoanć taskrun,1 // pomiar z zadania nr 1 if [Z1#Analog]<55 // sprawdzić czy jest niższy niż 55 pulse,14,0,20000 // jeśli jest to uruchomić pompkę na 20 sek, jeśli nie nie podejmuje zadań endif timerSet,1,30 // timer uruchominy na 30 sek aby woda mogła wsiąknąć taskrun,1 // pomiar z zadania nr 1 if [Z1#Analog]<55 // itd... pulse,14,0,10000 endif timerSet,2,30 taskrun,1 if [Z1#Analog]<55 pulse,14,0,8000 endif endon taskrun,2 if [Z2#Analog]<55 pulse,13,0,10000 endif timerSet,3,30 taskrun,2 if [Z2#Analog]<55 pulse,13,0,5000 endif timerSet,4,30 taskrun,2 if [Z2#Analog]<55 pulse,13,0,5000 endif endon taskrun,3 if [Z3#Analog]<45 pulse,12,0,10000 endif timerSet,5,30 taskrun,3 if [Z3#Analog]<45 pulse,12,0,5000 endif timerSet,6,30 taskrun,3 if [Z3#Analog]<45 pulse,12,0,5000 endif endon Pewnie kilka rzeczy zrobiłem w sposób trudniejszy niż można było to zrobić albo niezgodnie ze sztuką ale chyba to w tej całej zabawie jest najfajniejszego.
  24. 8 punktów
    Projekt powstał z racji mojego wrodzonego skąpstwa Żona napaliła się na takie cudo, a ceny gotowych tablic manipulacyjnych zwaliły mnie z nóg, więc konieczne było zrobienie czegoś własnoręcznie. Oprócz całej masy różnych przełączników i przycisków, w projekcie znalazły się również: wyświetlacz matrycowy led 8x8 max7219 dioda ws2812b sterowania potencjometrem Klawiatura numeryczna membranowa samoprzylepna z 16 klawiszami Mały wzmacniacz audio powerbank z 4 ogniwami 18650 do zasilenia całości, co pozwala na bardzo długie cieszenie się zabawą. Aby urozmaicić tablicę postanowiłem wyposażyć ją również w zamykany schowek. Projekt zamka oraz zawiasów pochodzi z thingverse, ciekawostką jest to że zawiasy są drukowane jako jeden element, nie ma konieczności ich składania, drukujemy i można montować. Zamek : https://www.thingiverse.com/thing:1273591 Zawias: https://www.thingiverse.com/thing:2187167 Obudowa została wykonana z sklejki, po wstępnym rozmieszczeniu przycisków i wycięciu dziur, nadszedł czas na sklejenie oraz skręcanie w całość. Na koniec pomalowana lakierem bezbarwnym. Teraz można było przystąpić do tej przyjemniejszej części projektu, czyli do programowania. Jako serce układu zostało wybrane Arduino Nano.Tablica ma możliwość odegrania 5 różnych melodii : dwie melodie Mario Bros, Crazy Frog, oraz motywy z filmów Piraci z Karaibów i James Bond. Pliki dźwiękowe odpowiednio skonwertowane oraz przykład programu można znaleźć tutaj: https://circuitdigest.com/microcontroller-projects/playing-melodies-on-piezo-buzzer-using-arduino-tone-function. Oczywiście nie odbyło się bez przeróbek oryginalnego kodu. Ze względu na małą ilość pamięci RAM, konieczne było przypomnienie sobie jak działa dyrektywa PROGMEM oraz odpowiednie przerobienie programu. W skrócie zmienna z melodią nie jest wczytywana do pamięci ram tylko baj to bajcie czytana prosto z flasha. Szerszy opis z przykładami użycia znajdziecie tutaj: https://www.arduino.cc/en/pmwiki.php?n=Reference/PROGMEM Sterowanie Diodą WS2812 odbywa się na 2 sposoby, albo płynnie podczas kręcenia potencjometrem lub w trybie pozytywki, losowo zmieniany jest kolor. Na matrycy ledowej, wyświetlane są liczby z klawiatury numerycznej, a w trybie pozytywki wyświetlane są minki. Zdjęcia wnętrza pominę bo ze względu na montaż na “pająka” nie ma się czym chwalić. Poniżej przedstawiam jak działa całość. Kod programu: tablica1.zip
  25. 8 punktów
    Artykuł przeznaczony do wszystkich zapaleńców druku 3D. Można nie kupować dość drogi filament do swojej drukarki 3D, a produkować w domu własny filament z zużytych butelek PET od napojów. Przy tym nieważne, jeżeli butelka jest pognieciona, ona również się nadaje do domowej produkcji filamentu. Filament z butelek ma sporo zalet w porównaniu z firmowym filamentem kupowanym – ABS albo PLA. Przede wszystkim, że produkowany filament nic nie kosztuje, jest po prostu darmowy Produkowany pręt filamentu Jest bardzo sztywny i absolutnie nie łamliwy, wytrzymuje sporo ostrych przegięć. Filament własnej produkcji jest sporo mocniejszy i twardszy, jak na rozciąganie tak i o wiele bardziej odporny na uderzenie. Absolutnie nie pochłania wody, czyli nie trzeba go ani suszyć, ani chronić w zamkniętym zabezpieczonym od nawilżania się opakowaniu. Praktycznie nie skurcze się przy ochłodzeniu w trakcie druku. Nie wymaga chłodzenia drukowanej warstwy. Nie wymaga stołu podgrzewanego. Dla przyczepności wystarczy miejsce na stole posmarować cienką warstwą kleju w sztyfcie na przykład typu „Glue Stick” Wydrukowane detal można obklejać od razu po skończeniu wydruku. Taki filament jest bardzo odporny na działanie rozpuszczalników i środków chemicznych. Jak widać filament produkcji własnej ma sporo zalet w porównaniu z filamentami kupowanymi, a najważniejsze – że jest darmowy. Niżej przedstawiono zdjęcia maszynki do produkcji filamentu: Do domowej produkcji filamentu wykorzystywane zużyte butelki od napojów. Ale butelki muszą być czyste, resztki kleju do nalepki powinni być usuwane. Technologia produkcji jest bardzo prosta i składa się z trzech następujących operacji: Poprawa zgniecionych butelek i butelek z ryflowaną powierzchnią tak, żeby ścianka boczna butelki była gładka. Nacinanie butelek na paski o określonej szerokości, od 5mm do 12mm w zależności od grubości ścianki butelki. Produkcja pręta filamentu z nacinanych pasków na specjalnej maszynce z nawijaniem na bębenek odbiorczy. Na tych wideo można obejrzeć prace maszynki i przyrządu do nacinania pasków z butelek: Zębatka drukowanie:
  26. 8 punktów
    Po zbudowaniu robota czworonożnego w układzie ssaka (robot "Garfield") chciałem spróbować zbudować coś w układzie gada. Zdecydowałem wykorzystać jako bazę wyjściową robota MiniKame, którego pliki STL i instrukcja złożenia dostępne są na Thingiverse. Jak łatwo zauważyć wyżej wymienione układy cechuje różnica w płaszczyźnie obrotu drugiego stopnia swobody względem pierwszego - dla ssaka jest to jedna, dwuwymiarowa płaszczyzna XY, dla gada ułożenie tych płaszczyzn jest prostopadłe - aby lepiej je zilustrować, zacznę od zdjęć: Mechanika Jak wspomniałem, wyszedłem z gotowego projektu, ale nie chciałem bezmyślnie wydrukować części, zmontować, wgrać wsadu i cieszyć się gotowym robotem. Zacząłem od zmodyfikowania głównego korpusu, ponieważ oryginał jako zasilania używa dwóch akumulatorów litowo-jonowych 18650 oraz gotowej płytki - kontrolera serwomechanizmów, a całość zamykana jest w dwuczęściowej obudowie. Zachowałem pierwotne wymiary (rozstaw otworów), dodałem szczelinę, do której wsuwany jest akumulator LiPol 2S 800mAh i otwory mocujące tradycyjnie już płytkę uniwersalną 5x7cm. Elementów nóg nie zmieniałem, dodam tylko, że są nieco trudne w drukowaniu, potrzebne są liczne podpory. Drukowane 3D są również pomarańczowe dystanse mocujące płytkę rozpoznającą głos, ale o niej za chwilę. Jeśli chodzi o napędy, ku zaskoczeniu wszystkich są to serwomechanizmy SG-90 Elektronika Jeśli ktoś czytał mój poprzedni wpis, również nie będzie zaskoczony - wykonałem własny sterownik serw, klasycznie Arduino Nano + stabilizator LM7805 - wiem, że nie jest to idealne i najlepsze rozwiązanie, ale działa Akumulator z płytką łączy JST-BEC - z przodu płytki wyprowadziłem złącze UART do płytki-modułu rozpoznającego dźwięk (jeżeli ktoś jest zainteresowany szczegółami, wiele informacji znajdzie pod hasłem "Arduino voice recognition", a sam moduł funkcjonalnością podobny jest do produktu SparkFun'a - identycznego należy szukać na chińskich portalach aukcyjnych ). Podstawową zaletą płytki jest jej prostota obsługi i działanie off-line, wadą zaś konieczność powtórzenia polecenia niekiedy kilkukrotnie. Moduł działa na zasadzie "nagrania" wzorców (maksymalnie 7, wystarczy zrobić to raz) w trybie nauki, a następnie, w trybie czuwania, po wykryciu dźwięku (moduł porównuje to, co otrzymuje mikrofon, z tym, co zapisał w pamięci, zatem rozpoznaje jedynie głos właściciela) przesyła przez UART informację typu "wykryto komunikat 1". Robot obsługuje komendy: naprzód, do tyłu, w lewo, w prawo, skacz, tańcz, pompki. Aha, jeszcze jedna sprawa - skuteczność rozpoznawania spada drastycznie przy nawet minimalnym szumie, stąd konieczność uciszenia widowni i wyłączenia wentylatora czy klimatyzacji. Niemniej moduł, który był głównym elementem mojej modyfikacji, okazał się bardzo udanym i efektownym pomysłem jak na swoją cenę (około 200PLN). Zastosowałem jeszcze jedną sztuczkę - kiedy robot chodzi, niemożliwe jest rozpoznanie komunikatu typu "stop", dlatego z przodu zamontowałem czujnik Sharp'a 4-30cm - kiedy przyłożę rękę, robot zatrzymuje się i mogę wydać kolejne polecenie Oprogramowanie W kwestii oprogramowania ponownie nie ma nic odkrywczego, moja praca polegała głównie na skomunikowaniu Arduino-moduł Voice Recognition przez UART (software'owy), autorzy udostępnili gotowe schematy i bibliotekę, która zawiera gotowe sekwencje chodu i innych ruchów (wspomniane skoki, pompki i taniec) - poniżej film Pozdrawiam, wn2001
  27. 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
  28. 7 punktów
    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
  29. 7 punktów
    Drodzy Technicy! Typowe ramię robota tylko pozornie naśladuje nasze ludzkie. Mobilność naszych kończyn jest daleko większa gdyż mamy bardziej ruchliwe stawy. A co by było gdyby i robot miał możliwość realizowania wielu stopni swobody w ramach tylko jednego przegubu?... ... To pytanie zadałem sobie po raz pierwszy dawno, dawno temu, u schyłku poprzedniego tysiąclecia, kiedy to komórki były w cebuli i na ziemniaki. W owych straszliwych, bezfejsowych czasach, pacholęciem będąc, usłyszałem o bajecznej chirurgii laparoskopowej. Dziurka od klucza te sprawy... Zafascynował mnie ten temat w stopniu większym niźli przysługiwał memu smarkaczowatemu stanowi. Pooglądałem sobie narzędzia jakie są używane i byłem niezmiernie zbulwersowany, że wśród nich brak jest takich, które by działały wystarczająco dobrze... Było dla mnie oczywistym, że przegub, który występuje w tego typu instrumentach, winien pracować podobnie jak nadgarstek, gałka oczna czy staw ramienny lub biodrowy. W takim brzuchu czy innym sercu jest klaustrofobicznie ciasno, więc należy mieć możliwość działania swobodnie w każdym kierunku bez przeszkód. Istniejące konstrukcje dają wolność w wymiarze albo w lewo albo w prawo i dopiero przy następnym przegubie mamy ponownie jedynie słuszną dowolność albo w lewo albo w prawo… No ale tam, w tych trzewiach, przecie nie ma miejsca na taką gburowatość w ruchach! O jakże potężną moc ma dziecięcy gniew!... Zanurkowałem w bezkresnym bałaganie mojego pokoju i cudem odnalazłem w miarę całą i nadal względnie kulistą piłeczkę pingpongową i zacząłem kombinować… O dziwo udało mi się znaleźć rozwiązanie. Działało! Ale niestety nie działało dość dobrze… Konstrukcja żerowała na wytrzymałości bardzo finezyjnych, delikatnych i trudnych w wykonaniu elementach. Wiedziałem już wtedy, że jeżeli coś ma być do medycyny to ma być solidne, niezawodne i nade wszystko tanie!… Początkowy gniew, po którym nastał złudny sukces, ostatecznie przeobraził się z wieloletnią, dojmującą rozpacz bezowocnych poszukiwań… Aż tu nagle, razu pewnego, jadłem na kolację jajecznicę. Lubuję się w takiej technologii spożycia rzeczonej jajecznicy, gdy niezmiennie pozostaje ona na patelni a ta z kolei spoczywa uroczyście na gazecie aby zadość uczynić stołowemu, kuchennemu bhp… Pozwalam sobie o tym wspomnieć tylko dlatego, że wystąpił w tamtym momencie ów decydujący czynnik katalizujący me pragnienia – gazeta! A dokładniej skrawek jej, marginesem zwany, wciąż niedoceniany, który aż się prosi aby przelać nań jakąś ważną myśl. Z takiego zaproszenia skwapliwie skorzystałem bo właśnie wtedy napadła mnie bardzo ważna myśl, jakże utęskniona… Otóż rozwiązanie patowego problemu, który dręczył mnie całe dziecieństwo, że o okresie gołowąsowości nie wspomnę, okazało się bezczelnie proste – stos rurek, rozmieszczonych współosiowo kubek w kubek, uzębionych na końcu, przenosi ruch w oś stawu, na którym nanizana jest adekwatna ilość kół zębatych, a te z kolei, przenoszą napęd dalej... Każda rurka, wraz z przypisanym sobie kołem odpowiada za inną funkcję, której sobie życzymy… Wszelkie ruchliwości mogą być realizowane osobno lub jednocześnie - jednakowoż sterowanie ich jest niezależne, z możliwościami (zaletami i wadami) i precyzją układów zębatych… czyli klasyka mechaniki w nieco odświeżonej, cudacznej formie... Po etapie gazetowym nastąpiła już czysta formalność – zasiadłem do warsztatu, pozbierałem kilka rurek, kilka zębatek wypreparowałem z maszyn, które liczyły czasy słusznie minione… Nad ranem dysponowałem ruchliwą ilustracją mojego pomysłu. Oddam pod łaskawy osąd drogich Forumowiczów, czy to drugie podejście, które nastąpiło po wielu latach rozkmin podprogowych, może osuszyć wcześniejsze łzy… Proszę o słowa możliwie merytoryczne i krytyczne. Oczywistym jest, że rozwiązanie to namieszać może w robotyce jako takiej. Pierwotnie skupiłem się na medycynie bo tam jest najtrudniej i tam też najprędzej nowe rozwiązania powinny trafiać. Nota bene stąd wynikają rozmiary prototypu, który naprędce zmajstrowałem.. Docelowo produkcja tych urządzeń miała być realizowana za pomocą lasera femtosekundowego (popularne dziś stenty, przedmioty o podobnych rozmiarach i klasie dokładności są tak wykonywane). Ja wtedy miałem tylko rękę uzbrojoną w pilnik – stąd żałosna dokładność... Wyzwań, którym trzeba by sprostać jest wiele – opracowanie odpowiedniego modułu zęba (audi ma na koncie niezłą propozycję, co najważniejsze przećwiczoną), wybór materiału (metal, ceramika?), w końcu wybór systemu łożyskowania… Owszem, sporo zabawy! Jednakże śmiem twierdzić, że rozwiązanie jakie pozwoliłem sobie zaproponować pod wieloma względami jest nader atrakcyjne – daje nieznane dotychczas, nowe możliwości. Ochoczo przyjmę wszelkie uargumentowane za i przeciw. Szczególnie będę wdzięczny za wskazanie gdzie takie rozwiązanie w przemyśle już występuje. Przyznam się, że na tamten czas, przeprowadziłem gruntowne poszukiwania przynajmniej śladów podobnych koncepcji, gdyż byłem przekonany, że tak prosty mechanizm musi już gdzieś występować. Dopiero później odnalazłem lakoniczny schemat rysunkowy zamieszczony bodaj na stronie pewnej japońskiej uczelni. Niestety, nie było żadnych zdjęć czy filmu dokumentujących prace badawcze nad tego typu konstrukcją. Życzę owocnych rozmyślań i z góry dziękuję za rozpoczęcie dyskusji! Szczegóły zębatki:
  30. 7 punktów
    W poprzednich częściach zapoznaliśmy się z demonstracyjnym kodem dostarczanym przez producenta wyświetlacza. Wiemy jakie zalety i wady ma to rozwiązanie, nadszedł moment, żeby spróbować napisać własną wersję. Spis treści: Sterowanie wyświetlaczem TFT - część 1 - wstęp, podstawowe informacje Sterowanie wyświetlaczem TFT - część 2 - analiza problemu Sterowanie wyświetlaczem TFT - część 3 - testy prędkości na STM32 Sterowanie wyświetlaczem TFT - część 4 - własny program Sterowanie wyświetlaczem TFT - część 5 - optymalizacja programu Własna biblioteka graficzna Specjalnie wybrałem model mikrokontrolera, w którego pamięci zmieści się cały bufor ekranu. Jak pamiętamy konieczne jest 160 x 128 x 2 = 40960 bajtów pamięci, co nie jest jednak problemem dla układu STM32L476. Kolor każdego piksela jest przechowywany jako 16-bitowa wartość, możemy więc utworzyć bufor pisząc po prostu: uint16_t lcd_framebuffer[LCD_WIDTH * LCD_HEIGHT]; Wybrałem poziomą orientację ekranu, więc LCD_WIDTH ma wartość 160, a LCD_HEIGHT 128. Przykładowa procedura rysowania punktu może wyglądać następująco: void lcd_set_pixel(uint32_t x, uint32_t y, uint16_t color) { if (x < LCD_WIDTH && y < LCD_HEIGHT) lcd_framebuffer[x + y * LCD_WIDTH] = __REV16(color); } Jak widzimy jest to po prostu zapisanie do tablicy przechowywanej w pamięci RAM, działa więc błyskawicznie. Wywołanie __REV16 było mi potrzebne, aby zamienić kolejność bajtów - można byłoby odpowiednio przeliczyć kody kolorów, ale zamiana bajtów to raptem jedna instrukcja asemblera (oczywiście jak ktoś będzie chciał optymalizować kod może, a nawet powinien tego typu błędy wyeliminować). 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. Kasowanie ekranu również odbywa się w pamięci, więc kod również jest prosty: void lcd_clear(uint16_t color) { uint16_t *p = lcd_framebuffer; uint16_t *end = lcd_framebuffer + LCD_WIDTH * LCD_HEIGHT; color = __REV16(color); while (p != end) *p++ = color; } Obecnie całe rysowanie odbywa się w lokalnej pamięci, dopiero gotowy obraz jest kopiowany na ekran. Przy pierwszym podejściu użyję HAL_SPI_Transmit, ale tym razem do przesłania wszystkich danych na raz (zamiast po jednym bajcie jak poprzednio): void lcd_copy(void) { lcd_cmd(ST7735S_RAMWR); HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit(&hspi2, (uint8_t*)lcd_framebuffer, 2 * LCD_WIDTH * LCD_HEIGHT, 500); HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET); } Przed wysłaniem danych wykonywana jest komenda wyświetlacza RAMWR. Jej wykonanie powoduje zapis do zdefiniowanego okna zaczynając od pozycji (0, 0). Natomiast samo okno jest ustawiane raz podczas inicjalizacji sterownika - ma ono rozmiar całego wyświetlacza, czyli 160x128 pikseli. Zmieniłem trochę wyświetlany obraz bo w mojej wersji używam tylko poziomej orientacji wyświetlacza. Program działa następująco: Nie bardzo to widać, ale obraz jest rysowany w pętli - tym razem jednak nie widać kasowania, ani rysowania obrazu: Rysowanie w pamięci RAM zajmuje ok 6ms. Procedury nie są jakoś szczególnie zoptymalizowane, do rysowania linii i okręgów używany jest algorytm Bresenhama, czcionki są identyczne jak w kodzie WaveShare. Kopiowanie danych z bufora do wyświetlacza trwa 33ms, czyli tyle ile powinno. Biblioteka HAL jak widzieliśmy poprzednio nie jest demonem szybkości, gdy wysyłamy za jej pomocą pojedyncze bajty, jednak transmitując duży bufor, jej użycie jest już całkiem sensowne (a na pewno łatwe). Użycie DMA Kopiowanie za pomocą funkcji HAL_SPI_Transmit ma pewną wadę, przez 33ms mikrokontroler jest zajęty tylko kopiowaniem. Do tego celu można jednak wykorzystać mechanizm DMA, dzięki czemu procesor będzie mógł wykonywać inne zadania, a samo kopiowanie będzie odbywało się w pełni sprzętowo (a więc i z maksymalną szybkością). Użycie DMA w środowisku STM32CubeIDE jest dziecinnie proste - prawie wszystko robimy za pomocą graficznych kreatorów. Napisałem prawie, bo jednak trochę programowania jeszcze nam zostanie. Poza tym kod wygenerowany przez CubeMX nie zawsze działa... W przypadku L476 w wersji CubeIDE 1.1.0 kolejność inicjalizacji SPI oraz DMA jest niepoprawna, więc domyślnie tworzony kod po prostu nie działa. Niestety nie udało mi się zmusić CubeMX do generowania kodu włączającego DMA zanim zacznie konfigurację SPI. Obejściem było wyłączenie wywoływania inicjalizacji DMA zupełnie i ręczne dodanie odpowiedniego kodu. Może w kolejnej wersji narzędzia ten błąd zniknie, ale zobaczymy. Warto też pamiętać, że domyślne priorytety przerwań mogą zupełnie zawiesić kod biblioteki HAL. W każdym razie zmiany w kodzie programu związane z użyciem DMA są właściwie kosmetyczne. Pierwsza to nieco inne sterowanie pinem CS - poprzednio ustawialiśmy go w stan wysoki zaraz po powrocie z wywołania HAL_SPI_Transmit. Teraz musimy dodać obsługę przerwania, które będzie wywołane po zakończeniu transmisji. Dopisujemy więc: void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) { lcd_copy_done(); } A w funkcji lcd_copy_done sterujemy odpowiednio pinem CS. Druga zmiana wynika z działania DMA w tle - nie chcemy zmieniać zawartości bufora ekranu, gdy DMA przesyła dane. Potrzebujemy więc flagi, która będzie informowała, że trwa transmisja (moglibyśmy oczywiście użyć podwójnego buforowania, ale 40KB to już i tak ogromny bufor jak na mikrokontroler). Oczywiście zamiast HAL_SPI_Transmit wywołujemy teraz HAL_SPI_Transmit_DMA. Sam program działa jak poprzednio, zyskaliśmy jednak 32ms na wykonywanie czegoś ciekawszego przez procesor. Program demonstracyjny Wiemy już jak sterować wyświetlaczem i uzyskać całkiem sensowne czasy działania. Odbyło się to za cenę dużego użycia pamięci, ale mamy możliwość rysowania prawie 25 klatek na sekundę. Jako przykład użycia kodu, wykorzystałem demo rysujące animowany tunel, które jest dokładnie opisane pod tym adresem. Pierwszy program oblicza kolory tekstury oraz bufory dla współrzędnych - wszystko jest opisane na blogu do którego podałem link, nie będę się więc rozpisywał o zasadzie działania programu. Użycie pamięci wzrosło do prawie 90KB Ale efekt jest chyba dość ładny: Program jest zaskakująco prosty (w pętli głównej wywoływana jest tylko funkcja draw_tunnel uint16_t texture[texHeight][texWidth]; uint8_t distanceTable[LCD_HEIGHT][LCD_WIDTH]; uint8_t angleTable[LCD_HEIGHT][LCD_WIDTH]; static void precalc(void) { for (int y = 0; y < texHeight; y++) { for (int x = 0; x < texWidth; x++) texture[y][x] = (x * BLUE / texWidth) ^ (y * BLUE / texHeight); } for(int y = 0; y < LCD_HEIGHT; y++) for(int x = 0; x < LCD_WIDTH; x++) { float ratio = 16.0; int dx = x - LCD_WIDTH / 2; int dy = y - LCD_HEIGHT / 2; float d = sqrt(dx * dx + dy * dy); int distance = (int)(ratio * texHeight / d) % texHeight; int angle = texWidth / 2 + (int)(texWidth / 2 * atan2(dy, dx) / M_PI); distanceTable[y][x] = distance; angleTable[y][x] = angle; } } static void draw_tunnel(void) { static uint32_t animation = 0; animation++; int shiftX = (int)(texWidth * 0.05 * animation); int shiftY = (int)(texHeight * 0.025 * animation); lcd_wait_ready(); for(int y = 0; y < LCD_HEIGHT; y++) for(int x = 0; x < LCD_WIDTH; x++) { uint16_t color = texture[(unsigned int)(distanceTable[y][x] + shiftX) % texWidth][(unsigned int)(angleTable[y][x] + shiftY) % texHeight]; lcd_set_pixel(x, y, color); } lcd_copy(); } Podobnie jak w opisywanym blogu, użyłem również użyć gotowej tekstury (zapisanej w pamięci Flash). Takie rozwiązanie jest nawet korzystniejsze jak chodzi o użycie pamięci RAM: Efekt działania programu: Możliwe jest również zaoszczędzenie pamięci przez wykorzystanie symetrii tablic wykorzystywanych podczas rysowania "tunelu". Takie program jest nieco bardziej skomplikowany, ale pozwala na uzyskanie efektu "rozglądania się" kamery: Podsumowanie Jak widzimy współczesne mikrokontrolery mają ogromną moc obliczeniową, odpowiednio optymalizując kod mogą całkiem sprawnie poradzić sobie ze sterowaniem niewielkiego wyświetlacza TFT. Odbywa się to za cenę użycia pamięci RAM, ale odpowiednio optymalizując kod można to zapotrzebowanie nieco zmniejszyć. W kolejnej części opiszę jak użyć trybu z mniejszą liczbą kolorów, dzięki czemu zapotrzebowanie na pamięć bardzo spadnie. Spis treści: Sterowanie wyświetlaczem TFT - część 1 - wstęp, podstawowe informacje Sterowanie wyświetlaczem TFT - część 2 - analiza problemu Sterowanie wyświetlaczem TFT - część 3 - testy prędkości na STM32 Sterowanie wyświetlaczem TFT - część 4 - własny program Sterowanie wyświetlaczem TFT - część 5 - optymalizacja programu
  31. 7 punktów
    Od dawna interesowały mnie pomiary warunków meteorologicznych w mojej miejscowości, pierwsza stacja meteorologiczna, którą zbudowałem około roku 2010, wykonana była na mikrokontrolerze Atmega32. Do komunikacji z światem wykorzystywała moduł LAN Wiznet 7010a. Stacja ta była oprogramowana w języku BASCOM. Projekt który chcę zaprezentować dzisiaj działa już od roku 2018 i został oprogramowany w środowisku Arduino. Stacja została podzielona na 2 moduły, pierwszy pomiarowy oparty jest na klonie Arduino Nano oraz drugi odbiorczy którego sercem jest ESP8266 NodeMCU v3, służy on również do wyświetlania aktualnych pomiarów na wyświetlaczu LED dot matrix o wymiarach 8x56 punktów. Na pracach stolarskich się nie będziemy skupiać napiszę tylko że klatka meteorologiczna została wykonana z drewna sosnowego i umieszczona na wysokości 2 m. Moduł Pomiarowy Czujniki jakie zastosowałem to dwie sztuki DS18B20 pierwszy zajmuje się pomiarem temperatury przy gruncie na wysokości 5cm, drugi pełni rolę zapasowego czujnika temperatury na wypadek uszkodzenia się głównego czujnika BME280. Do pomiaru prędkości wiatru wykorzystuję wiatromierz firmy Maplin na jeden obrót wiatromierza przypadają 2 impulsy z kontaktronu który jest w nim zamontowany, producent dostarcza również odpowiedni wzór według którego można obliczyć rpm oraz prędkość wiatru w km/h. Dane mierzone przez wiatromierz możemy podzielić na dwie wartości, pierwsza to chwilowa prędkość, druga prędkość w porywach, aby uśrednić wartości mierzone program zlicza impulsy z 5s a następnie dokonuje odpowiednich obliczeń. Zebrane dane przesyłane są do drugiego urządzenia poprzez moduły radiowe które działają na częstotliwości 433,92 MHz. W tym celu zastosowana została biblioteka RCSwitch. Każda mierzona wartość jest wysyłana jako osobna transmisja. aby rozróżnić pomiary z konkretnych czujników mierzona wartość mnożona jest przez 100 a następnie dodawana jest liczba 100 000 dla pierwszego czujnika, 200 000 dla drugiego itd. Przykład kodu który realizuje tę funkcję poniżej: // temperatura sensor BME codetosend = temp * 100 + (1 * 100000); mySwitch.send(codetosend, 24); // wilgotnosc sensor BME codetosend = hum * 100 + (2 * 100000); mySwitch.send(codetosend, 24); Moduł Wewnętrzny Obudowa, która idealnie nadawała się do implementacji wewnętrznego modułu pochodzi z tunera IPTV Motorola VIP1910-9. Przedni panel został wykonany z ciemnego półprzepuszczalnego plastiku który idealnie nadaje się do umieszczenia w nim wyświetlacza. Sercem urządzenia jest układ ESP8266. "Moduł wewnętrzny" został również wyposażony w czujnik temperatury oraz wilgotności DHT22, dodatkowo w celu prezentacji zmierzonych wartości dołączone zostało 7 szt. modułów wyświetlacza LED dot matrix z układem MAX7219. Do obsługi tej matrycy zastosowałem bibliotekę Max72xxPanel.h która współpracuje z biblioteką Adafruit_GFX.h w ten sposób nie byłem zmuszony implementować do rozwiązania własnych czcionek. Matryca ta oprócz modułowej konstrukcji umożliwia również sterowaniem jasnością podświetlania, w tym celu aby uprzyjemnić użytkowanie w porach nocnych odbiornik został wyposażony w fotorezystor dzięki któremu potrafi określić natężenie oświetlenia otoczenia i odpowiednie ustawienie podświetlenia. Na wyświetlaczu w pierwszej kolejności wyświetlam aktualną godzinę oraz temperaturę wewnątrz pomieszczenia oraz wilgotność, po około jednej minucie wyświetlane są informacje odczytane z stacji meteo czyli temperatura wilgotność i ciśnienie, postanowiłem nie wyświetlać tutaj informacji dotyczących prędkości wiatru oraz temperatury przy gruncie. Decyzję tą podjąłem na podstawie użytkowania innego podobnego rozwiązania, akurat jak chcemy odczytać godzinę to wyświetlane są inne informacje. Dodatkowo w godzinach nocnych, które zostały ustawione w sztywnych ramach czasowych między 21:00 a 7:00 informacje odczytane z stacji meteo zostały okrojone tylko do temperatury. W projekcie zostały zastosowane 2 rodzaje animacji pierwsza z nich, przesuwa tekst z prawej strony wyświetlacza na lewą, z możliwością zatrzymania w interesujących momentach. Drugi rodzaj to pionowa animacja. Mikrokontroler również poprzez protokół NTP i bibliotekę time.h pobiera aktualną godzinę i datę. Za odbiór danych z pierwszego układu odpowiedzialny jest moduł radiowy którego obsługą tak jak w poprzednim module zajmuje się biblioteka RCswitch. Poniżej fragment programu który demonstruje w jaki sposób odbierane i dekodowane są dane: rc = mySwitch.getReceivedValue(); // czujnik temperatury powietrza BME280 if (abs(rc)>=50000&& abs(rc)<150000) { rc=(rc-100000)/100; if (rc > -50 and rc < 60) { temp1 = rc; Serial.print("Czujnik BME280 - temperatura: \t"); Serial.println(rc); matrix.drawPixel(55,0,1); matrix.write(); } } // czujnik wilgotności BME280 if (abs(rc)>=150000 && abs(rc)<250000) { rc=(rc-200000)/100; if (rc > 5 and rc <= 100) { hum = rc; Serial.print("Czujnik BME280 - wilgotnowsc: \t"); Serial.println(rc); matrix.drawPixel(55,1,1); matrix.write(); } } Dzięki zastosowaniu zewnętrznej anteny oraz odbiornika opartego na superheterodynie, zasięg w otwartym terenie to około 250 m. Po odebraniu danych z pierwszego układu poprzez moduł radiowy następuje przekazanie ich do serwera z systemem Domoticz. Domoticz to bardzo lekki system automatyki domowej, który pozwala monitorować i konfigurować różne urządzenia, przełączniki, czujniki takie jak temperatura, opady deszczu, wiatr, promieniowanie ultrafioletowe (UV), zużycie energii elektrycznej, zużycie gazu, zużycie wody i wiele więcej. Wykresy dostępne są również na stronie www http://meteo.palowice.net Poniżej film z działania odbiornika, smużenie animacji które występuje na filmiku ludzie oko nie rejestruje. Gdyby kogoś interesował kod to również zamieszczam: meteo.zip
  32. 7 punktów
    Dziś wydrukowałem kilka ostatnich części i zmontowałem mechaniczną część konstrukcji robota czworonożnego. Dalej muszę: - zoptymalizować 'nóżki' - sensory kolizji, żeby zapewniały także dobrą przyczepność. W tej chwili widać zakończenia na 'kwadrat', żebym mógł robić szybkie testy i wymieniać na nowe wersje bez drukowania całej dalszej części kończyny. - zamontować zasilanie - przetwornicę step-down, ogniwa li-ion z bms i zabezpieczenia dla serw i logiki - zacząć programować schematy ruchowe (odwrotną kinematykę napisałem wcześniej i przetestowałem).
  33. 7 punktów
    1. Wprowadzenie Od dawna zafascynowany jestem kaligrafią, nie tylko w kontekście naszego języka ojczystego, ale także innych bardziej egzotycznych jeżyków. Mimo wielokrotnych prób zmierzenia się z tym tematem od strony praktycznej doszedłem do wniosku, że forma biernego obserwatora daje mi więcej satysfakcji niż sam udziel w procesie kaligrafowania. Może to niektórym wydawać się dziwne, może nie, no cóż, tak mam. Po pewnym czasie w mojej głowie narodziła się myśl, a raczej pytanie. Czy jestem w stanie zbudować maszynę do kaligrafii? Uprzedzając nieco fakty - odpowiedź brzmi: tak. Więc zapraszam do zapoznania się, z krótką historią mojego plotera. Po przejrzeniu zasobów sieci zdecydowałem się na ploter CNC, jako urządzenie, które spełni moje wymagania. W przyszłości może będzie dodatkowo rozbudowane i zyska funkcję na przykład laserowego grawera. Na początek, krótko o samym urządzeniu. Jak podaje Wikipedia ploter to komputerowe urządzenie peryferyjne, służące do pracy z dużymi płaskimi powierzchniami, mogące nanosić obrazy, wycinać wzory, grawerować itp. Plotery są również używane do kreślenia map. Są to urządzenia z powiedzeniem używane od lat w różnych gałęziach przemysłu. Pod naszymi strzechami, czyli w domu przeciętnego Kowalskiego się ich jednak nie widuje ponieważ funkcję tego typu urządzenia pełni drukarka, gdyż raczej na co dzień nie ma konieczności "generowania" pisma ręcznego na dużym formacie. Jest to pewien skrót myślowy, ale mam nadzieje, że większość osób rozumie o co mi chodzi. Założenia są takie, że urządzenia ma być stosunkowo tanie, łatwe w budowie i obsłudze. 2. Projekt i Thingiverse Thingiverse to serwis, w którym ludzie dzielą się swoimi projektami różnych maszyn, urządzeń, czy gadżetów, które można następnie wydrukować we własnym zakresie na drukarce 3D. Tak też postanowiłem zrobić, wykorzystać jeden z bardzo wielu istniejących już projektów na którego bazie zbuduję swój ploter. Konkretnie z projektu "Drawing Robot - Arduino Uno + CNC Shield + GRBL", który sam bazuje na innym tego typu projekcie. 2.1. Oryginalny projekt, źródło: thingiverse.com/thing:2349232 Do projektu zostały wykorzystane części stworzone na drukarce 3D, w przypadku gdy ktoś nie ma dostępu do tego typu urządzenia polecam skorzystać z ogłoszeń społeczności związanej z drukiem 3D. Takie usługi powinny być dostępne na miejscu we wszystkich większych miastach, a koszt to w tym przypadku to do kilkudziesięciu złotych. Nie zapominajmy to też, o naszej forumowej braci. 3. Części i narzędzia Po za wydrukowanymi częściami będą potrzebne jeszcze: Elementy elektroniczne, elektryczne Silnik krokowy NEMA 17 - 2 sztuki. Stalowy pręt o średnicy 8mm (oś X, Y) - 4 sztuki, w moim przypadku jest to 4x po 400mm. Stalowy pręt o średnicy 3mm (oś Z) - 2 sztuki, 2x 8mm. Stalowy pręt gwintowany o średnicy 8mm - 1 sztuka. Łożysko liniowe LM8UU - 8 sztuk. Serwomechanizm SG-90 - 1 sztuka. Sprężynka od długopisu lub gumka recepturka - od jednej do kilku sztuk w zależności od użytego uchwytu. Koło zębate GT2, 16 zębów - 2 sztuki. Łożysko 624zz - 5 sztuk. Pasek zębaty GT2 - w zależności od długości zastosowanych prętów dla osi X i Y, w moim przypadku wystarczyło 2000mm. Arduino Uno - 1 sztuka. Sterownik CNC Shield dla Arduino Uno - 1 sztuka. Zworki - 6 sztuk. Sterownik silnika krokowego A4988 - 2 sztuki. Zasilacz 12V minimum 2A - 1 sztuka. Przyciski krańcowe - 4 sztuki, opcjonalnie, zabezpieczenie osi X i Y z obu stron. Gniazdo DC - 1 sztuka. Nakrętki M8 - 4 sztuki. M4 - 5 sztuk. M3 - 7 sztuk. Śruby M4 x 35mm - 5 sztuk. M3 x 20mm - 1 sztuka. M3 x 16mm - 13 sztuk. M3 x 6mm - 4 sztuki. Podkładki M8 - 4 sztuki. M3 - 4 sztuki. Inne Przewody do silników krokowych, kupne czy też zrobione własnoręcznie. Wszelkie narzędzia, które mogą się przydać pod czas budowy, jak na przykład piła do przycięcia stalowych prętów, cążki do metalu, czy stacja lutownicza, ale to raczej oczywiste. 4. Elektronika i mechanika Sercem urządzenia jest Arduino Uno z płytką rozszerzeń CNC, w której osadzone są 2 sterowniki A4988. Płytkę rozszerzeń wpinamy zgodnie z opisem pinów, powinny być one opisane na obu płytkach. Tak samo postępujemy z A4988 dla osi X i Y. 4.1. Diagram połączeń elektrycznych z CNC Shield. 4.2. Zmontowany układ, gotowy do pracy. Ploter wykorzystuje system CoreXY do poruszania karetką. Według opinii krążących w internecie taki system jest bardziej precyzyjny i prostszy do wykonania w porównaniu do starowania osiami X i Y niezależnie. Nie jestem ekspertem i ciężko mi stwierdzić czy tak rzeczywiście jest. Bez względu na to czy ma to odzwierciedlenie w rzeczywistości, to dość spora część tego typu maszyn budowanych przez hobbystów wykorzystuje właśnie CoreXY, jeśli nie mamy do czynienia z dużymi obciążeniami karetki. 4.3. Wizualizacja układu CoreXY. 4.4. Schemat ruchu układu CoreXY. 5. Oprogramowanie Oprogramowanie Arduino zostało wykonane zgodnie z poniższym poradnikiem: https://electricdiylab.com/grbl-cnc-shield-z-axis-servo-migrbl/ Należy jedynie pamiętać, że przez samym zaprogramowaniem mikro kontrolera należy w pliku config.h zmienić linie: // #define COREXY // Default disabled. Uncomment to enable. na #define COREXY // Default disabled. Uncomment to enable. czyli usunąć komentarz, przez co zostanie włączona opcję COREXY ponieważ właśnie takiego starowania używa ploter. W przeciwnym wypadku nasza maszyna będzie działać, ale wszystko co stworzy będzie obrócone o 45 stopni. 6. Efekt końcowy Tak oto prezentuje się efekt końcowy, wymaga jeszcze kilku końcowych szlifów, jak dobór odpowiednich sprężyn, przetestowanie różnych podkładek do pisania, piór, długopisów, wykonanie nowego "trzymaka", dodaniu kilku trytytek, ect. Mimo tych drobnych aspektów maszyna działa bardzo dobrze i jestem zaskoczony jej dokładnością. 6.1. Wybrane ujęcia gotowego plotera. 6.2. Film obrazujący pracę plotera. Film ukazuje również problemy z dociskiem długopisu spowodowane nierównym podłożem.
  34. 7 punktów
    Przeglądając kurs Techniki Cyfrowej na Forbocie, wpadłem na pomysł przygotowania płytek PCB do każdej z lekcji. Po wytrawieniu wszystkich płytek, naniesieniu opisów i zabezpieczeniu ich, zdałem sobie sprawę że korzystanie z (jak i przechowywanie) takich małych PCB będzie nieporęczne. Stąd zrodził się pomysł na płytkę testową do tego kursu, wszystkie układy na jednym, większym laminacie. Przechowywanie i korzystanie z takiej wersji okazało się o wiele lepsze. W dolnym prawym rogu znalazło się też miejsce na wszystkie układy (CD4026, CD4069, CD4071, CD4081), których potrzebujemy do każdej z lekcji. Do publikacji niniejszego wpisu namówił mnie Treker (i tu ukłon w jego stronę za to, że zgodził się na publikację). Kiedyś spytałem czy nie myśleli o tym, by wypuścić zestaw takich płytek do lutowania, odpisał że specjalnie tego nie zrobili z racji tego, iż składanie układów samemu jest bardziej edukacyjne. I tu się z nim zgadzam. Jednak dla osób, które nie mają za dużo czasu, albo przechodzą kolejne etapy kursu siedząc w pracy i ucząc się pod biurkiem (jak np. ja), takie rozwiązanie jest rewelacyjne. Teraz co nieco o budowie (wszystkie schematy ideowe dostępne są w kursie Technika Cyfrowa, więc myślę, że nie ma potrzeby ich tu dodawać). Na płycie znalazły się dwa wejścia zasilania. Jedno na wtyk DC 2.1/5.5 oraz wejście śrubowe ARK2. Uruchomienie poszczególnych układów odbywa się za pomocą zworki, którą należy umieścić na pinach 1 i 2 oznaczonych jako ON. Wyłączenie analogicznie na pinach 2 i 3. W miejsce docelowych układów zostały zamontowane podstawki precyzyjne. Wybór padł właśnie na nie z jednego prostego względu, żywotność takich podstawek jest o wiele dłuższa niż przy zwykłych podstawkach, z uwagi na to, że tu układy będą dość często z nich wyjmowane. Problemem jaki napotkałem był zły wyświetlacz, który otrzymałem do układu licznika gości, niestety po zamontowaniu i uruchomieniu układu wyświetlacz nie reaguje (pora zamówić wyświetlacz docelowy). Projekt PCB jak i budowa przebiegła bez większych problemów. Wymiary wynoszą zaledwie 12x17,5cm. Można by pokusić się o zmniejszenie PCB jeśli komuś taki wymiar wydaje się za duży. Projekt w całości przygotowany w programie Eagle.
  35. 7 punktów
    Pomysł na stacje lutowniczą postał w 2010 roku. Wtedy też powstała moja kopia stacji lutowniczej. W 2014 roku przebudowałem stacje dodając funkcje lutownicy na gorące powietrze. Wadą tej przebudowy był brak możliwości korzystania z lutownicy oporowej i HA jednocześnie. Po kolejnych 4 latach pojawiły się problemy z przekaźnikami w przystawce do HA i gnieździe kolby oporowej. W 2018 postanowiłem stworzyć nową stacje lutowniczą całkowicie od podstaw. Prace rozpocząłem od skompletowania potrzebnych elementów: Kolby pochodzą od stacji: Oporowa: Pensol SL20 HA: Zhaoxin 858 Transformator 2x12V 50VA Do tego: Wzmacniacze termopar MAX6675 Wyświetlacz 20x4 i2c Czujnik temperatury DS10B20 Klon Arduino proMini Drobnica elektroniczna taka jak tranzystory, stabilizatory, kondensatory, potencjometry. Większość tych elementów miałem z demontażu poprzedniej stacji lub znalazłem w „przydasiach” co znacznie zminimalizowało koszt wykonania. Kolba oporowa została wybrana bez jakiś większego zastanowienia, porostu polecana. Natomiast kolba hot air została wybrana z powodu wbudowanego wentylatora i braku konieczności stosowania kompresora. Dodatkowym atutem jest wbudowany kontaktron. Gdy miałem potrzebne elementy, złożyłem układ na płytce stykowej i zabrałem się za pisanie programu. Regulacja temperatury kolb i przepływu powietrza odbywa się poprzez odczyt napięcia ustawionego na potencjometrze. Dodatkowo potencjometry mają wbudowany włącznik (ma minimum zwiera dodatkowy styk do masy) Po rozwarciu styku program sprawdza napięcie na potencjometrze i mapuje je na stopnie Celsjusza w zakresie 0-5V -> 100-500°C. Do obu kolb przygotowana jest funkcja uśpienia. Dla kolby oporowej aktywowana jest poprzez przełączenie włącznika i ustawia ona temperaturę grota na 150°C niezależnie od nastawy potencjometru. Uśpienie kolby HA odbywa się poprzez umieszczenie jej w uchwycie. Wewnątrz, którego ukryte są dwa magnesy. Aktywują one kontaktron umieszczony w rączce kolby. Podobnie jak w przypadku kolby oporowej aktywowanie uśpienia HA blokuje regulacje potencjometru oraz ustawia temperaturę na 0°C i PWM wentylatora na 50% aby wystudzić grzałkę. Odczyt temperatury chciałem żeby odbywał się po magistrali i2c jednak cena takich wzmacniaczy była zaporowa. Wypadło więc na zwykłe wzmacniacze które komunikują się z arduino po magistrali SPI. Aby zminimalizować potrzebną ilość portów sygnał zegarowy CLK oraz przesył danych DO są podpięte równolegle. Jedynie wybór wzmacniacza (CS) podpięty jest do osobnych portów w Arduino. Dzięki temu wykorzystanych jest 5 a nie 9 pinów. Wewnątrz obudowy umieszczony jest czujnik temperatury DS18B20. Gdy wzrośnie ona powyżej ustawionego progu uruchamiane są dwa wentylatory odśrodkowe chłodzące wnętrze. /* PIN_IO * * WZMACZNIACZE MAX6675 * * CLK- 2 wspolne dla termopar * DO- 4 wspolne dla termopar * CS_1- 3 cs dla pierwszej termopary * CS_2- 5 cs dla drugiej termopary * CS_3- 6 cs dla trzeciej termopary * * DS18B20_DATA- 7 * * WŁĄCNIK_HA- 8 * WŁĄCZNIK_KOLBA- 9 * PODSTAWA HA- 14(A0) * USPIENIE KOLBY- 16(A2) * * POTENCJOMETR_KOLBA- A6 * POTENCJOMETR_HA- A7 * POTENCJOMETR_DMUCHAWY_HA- A1 * * WENTYLATOR WNETRZE- 13 * PWM DMUCHAWA_HA- 10 * * STEROWANIE_GRZAŁKI_KOLBY- 11 * STEROWANIE_GRZAŁKI_HA- 12 */ #include "max6675.h" #include <LiquidCrystal_I2C.h> #include <OneWire.h> #include <Wire.h> #include <DS18B20.h> byte stopienC[8] = { B01100, B10010, B10010, B01100, B00000, B00000, B00000, B00000, }; byte strzalka[8] = { B00000, B00100, B00110, B11111, B11111, B00110, B00100, B00000, }; byte strzalka1[8] = { B00100, B01010, B10001, B00100, B01010, B10001, B00100, B01010, }; byte strzalka2[8] = { B00100, B01010, B10001, B00100, B01010, B10001, B00100, B01010, }; int thermoCLK = 2; //SCK wspolne dla termopar int thermoDO = 4; //SO wspolne dla termopar //TERMOPARA1 int thermoCS_1 = 3; //CS cs pierwszej termopary //TERMOPARA2 int thermoCS_2 = 5; //CS cs drugiej termopary //TERMOPARA3 int thermoCS_3 = 6; //CS cs trzeciej termopary MAX6675 thermocouple_1(thermoCLK, thermoCS_1, thermoDO); //termopara 1 MAX6675 thermocouple_2(thermoCLK, thermoCS_2, thermoDO); //termopara 2 MAX6675 thermocouple_3(thermoCLK, thermoCS_3, thermoDO); //termopara 3 LiquidCrystal_I2C lcd(0x3F, 20,4); //inicjalizacja wyswietlacza 20x4 #define ONEWIRE_PIN 7 //inicjalizacja dallasa byte address[8] = {0x28, 0x4E, 0xE3, 0x58, 0x8, 0x0, 0x0, 0xD}; OneWire onewire(ONEWIRE_PIN); DS18B20 sensors(&onewire); #define przycisk_wl_HA 8 #define przycisk_wl_KOLBA 9 #define podstawaHA 14 #define uspienieKOLBA 16 int stopnie_1; int stopnie_2; int stopnie_3; int odczytanaWartosc_1; int odczytanaWartosc_2; int odczytanaWartosc_3; //int odczytanaWartosc_4 = 510; int ADC_KOLBA; int ADC_HA; int ADC_WENTYLAROT_HA; int kolba = 0; int ha = 0; int podstawa = 0; int uspienie = 0; int wnetrze =40; int PWM; void setup() { Wire.begin(); lcd.init(); //lcd.noBacklight(); // inicjalizacja ekranu, podswietlenia i lcd.backlight(); // znakow urzytkownika lcd.clear(); lcd.createChar(1,stopienC); lcd.createChar(2,strzalka); lcd.createChar(3,strzalka1); lcd.createChar(4,strzalka2); sensors.begin(9); // start dallasa sensors.request(address); pinMode(przycisk_wl_HA, INPUT_PULLUP); pinMode(przycisk_wl_KOLBA, INPUT_PULLUP); pinMode(podstawaHA, INPUT_PULLUP); pinMode(uspienieKOLBA, INPUT_PULLUP); pinMode(13, OUTPUT); //WENTYLTOR pinMode(11, OUTPUT); //KOLBA pinMode(12, OUTPUT); //HA } void loop() { /***************************ODCZYT_TEMP***********************************/ stopnie_1 = thermocouple_1.readCelsius(); stopnie_2 = thermocouple_2.readCelsius(); stopnie_3 = thermocouple_3.readCelsius(); lcd.setCursor(0, 0); lcd.print("KOLBA:"); jesli3cyfry(stopnie_1); lcd.write(1); lcd.print("C"); lcd.setCursor(0, 1); lcd.print(" HA:"); jesli3cyfry(stopnie_2); lcd.write(1); lcd.print("C"); lcd.setCursor(0, 2); lcd.print("SONDA:"); jesli3cyfry(stopnie_3); lcd.write(1); lcd.print("C"); /**************************WNETRZE_CHLODZENIE**************************************/ float temperatureDALLAS = sensors.readTemperature(address); lcd.setCursor(0, 3); lcd.print("Temp.:"); jesli3cyfry(temperatureDALLAS); lcd.write(1); lcd.print("C"); lcd.setCursor(12,3); lcd.print("WENT:"); if( wnetrze >= temperatureDALLAS ) { digitalWrite(13, LOW); lcd.setCursor(17,3); lcd.print("OFF"); } if( wnetrze + 2 <= temperatureDALLAS ) { digitalWrite(13, HIGH); lcd.setCursor(17,3); lcd.print(" ON"); } sensors.request(address); /**************************PRZYCISKI******************************/ if (digitalRead(uspienieKOLBA) == LOW) { uspienie = 2; } else { uspienie = 0; } if (digitalRead(podstawaHA) == LOW) { podstawa = 2; } else { podstawa = 0; } if (digitalRead(przycisk_wl_HA) == LOW) { ha = 0; } else { ha = 2; } if (digitalRead(przycisk_wl_KOLBA) == LOW) { kolba = 0; } else { kolba = 2; } /***************************KOLBA**********************************/ if(kolba > 1) { ADC_KOLBA = 0; lcd.setCursor(13, 0); lcd.print(" "); lcd.setCursor(19, 0); lcd.print(" "); } else { if(uspienie > 1) { ADC_KOLBA = 150; lcd.setCursor(13, 0); lcd.print("X"); } if(uspienie < 1) { odczytanaWartosc_1 = analogRead(A6);//Odczytanie wartości z ADC ADC_KOLBA = map(odczytanaWartosc_1, 5, 1005, 100, 500);//Przeskalowanie wartości lcd.setCursor(13, 0); lcd.print(" "); } } lcd.setCursor(14, 0); lcd.write(2); jesli3cyfry(ADC_KOLBA); lcd.write(1); lcd.print("C"); if( ADC_KOLBA >= stopnie_1 ) //TERMOSTAT KOLBY { digitalWrite(11, HIGH); lcd.setCursor(12,0); lcd.write(3); } if( ADC_KOLBA + 1 <= stopnie_1 ) /////////////////////////////////////////////////////////////bylo + 2 { digitalWrite(11, LOW); lcd.setCursor(12,0); lcd.print(" "); } /****************************HA************************************/ if(ha > 1) { ADC_HA = 0; analogWrite(10, 0); PWM = 0; lcd.setCursor(13,1); lcd.print(" "); lcd.setCursor(19,1); lcd.print(" "); lcd.setCursor(19,2); lcd.print(" "); } else { if(podstawa > 1) { lcd.setCursor(13,1); lcd.print("X"); ADC_HA = 0; PWM = 50; analogWrite(10, 126); //PWM na 50 % digitalWrite(12, LOW); lcd.setCursor(19, 1); lcd.print(" "); } if(podstawa < 1) { lcd.setCursor(13,1); lcd.print(" "); odczytanaWartosc_2 = analogRead(A7);//Odczytanie wartości z ADC ADC_HA = map(odczytanaWartosc_2, 5, 1005, 100, 500);//Przeskalowanie wartości odczytanaWartosc_3 = analogRead(A1);//Odczytanie wartości z ADC ADC_WENTYLAROT_HA = map(odczytanaWartosc_3, 5, 1005, 127, 253);//Przeskalowanie wartości analogWrite(10, ADC_WENTYLAROT_HA); PWM = map(odczytanaWartosc_3, 5, 1005, 50, 100);//Przeskalowanie wartości 1020 } } lcd.setCursor(14, 1); lcd.write(2); jesli3cyfry(ADC_HA); lcd.write(1); lcd.print("C"); lcd.setCursor(12, 2); lcd.print("PWM"); lcd.write(2); jesli3cyfry(PWM); lcd.print("%"); if(ADC_HA >= stopnie_2) //TERMOSTAT HA { digitalWrite(12, HIGH); lcd.setCursor(12,1); lcd.write(4); } if( ADC_HA + 1 <= stopnie_2 ) ////////////////////////////////////////////////////////////////bylo + 2 { digitalWrite(12, LOW); lcd.setCursor(12,1); lcd.print(" "); } /***************************KONIEC*********************************/ } void jesli3cyfry(int liczba) //Funkcja odpowiedzialna za wyswietlanie spacji na pozycji poprzedzającej gdy 3 cyfry zmieniaja się na dwie { if (liczba >= 0 && liczba < 100) { lcd.write(' '); } lcd.print(liczba); } Gdy miałem gotowy kod zabrałem się za stworzenie schematu oraz PCB. Niestety popełniłem błąd i nie wychwyciłem go przed wykonaniem płytki. 7805 wypuścił magiczny dym. W kolejnej wersji został poprawiony jednak powstał błąd na schemacie w magistrali SPI (co ciekawe w pierwszej wersji schematu go nie było) i został przeniesiony na PCB. Trzecia wersja działa poprawnie. Płytka wykonana termo-transferem. Po skończeniu sterownika pora na zmontowanie wszystkiego w całość. Na przednim panelu znalazło się miejsce na wyświetlacz, 3 potencjometry od regulacji temperatury i PWM wentylatora, włącznik uśpienia kolby oporowej, gniazda kolb wraz z gniazdem dodatkowej termopary i na koniec włącznik odsysacza elektrycznego. Wewnątrz obudowy oprócz sterownika oraz transformatora 2x12V znajduję się dodatkowy transformator oraz zasilana przez niego pompka centralnego zamka z jakiegoś niemieckiego auta. Rzeczy związane z sterowaniem oraz danami połączone są odpowiednią ilością żył z taśmy komputerowej, natomiast przewody związane z zasilaniem stacji, kolbami oraz wszystkim co wymaga większego prądu/napięcia to lgy1mm2 Mocowanie toroida, pompki centralnego zamka oraz uchwyt kolby HA wydrukowałem z ABS na drukarce. Uchwyt jest wydrukowany jako skorupa i jak już wspomniałem ma schowane wewnątrz dwa magnesy neodymowe do uruchomienia kontaktronu a następnie zalałem jego wnętrze żywicą epoksydową aby zwiększyć jego sztywność i wytrzymałość. Mocowanie pompki próżniowej ma przygotowane miejsce na gumowe piankowe uszczelki, które skutecznie tłumią drgania mechanizmu. Na dzień dzisiejszy do dokończenia została mi końcówka odsysacza. W planach mam przepisanie kodu z wykorzystaniem regulatora PID zamiast tradycyjnej histerezy, zmiana sterowania chłodzenia (po raz kolejny PID) oraz wymiana obu kolb spowodowana ich zużyciem. ArduLutownica.rar
  36. 7 punktów
    Cześć, na wszystkich większych forach, mimo szczerych chęci użytkowników, zdarzają się różne niemiłe sytuacje. Większe lub mniejsze zgryźliwości, ogólne odsyłanie do wyszukiwarki itd. Z punktu widzenia nowych czytelników są to sytuacje, które zniechęcają do udzielania się na forum. Często zdarza się też tak, że jakaś osoba chętnie udziela pomocy w 100 tematach, a w jednym miejscu napisze coś mniej miłego i od razu robi się "afera", która psuje atmosferę i reputację danej osoby. Wydaje mi się, że atmosfera panująca na Forbocie jest całkiem dobra, ale chciałbym jednak wprowadzić pewne rozwiązania, które jeszcze ją poprawią. Roboczo nazwałem tę kwestię PPF (Polityką Przyjaznego Forum). Chciałbym wprowadzić kilka zasad, które będą dotyczyły zarówno osób zadających pytania, jak i tych, które odpowiadają. Mam już swój szkic zasad tego typu, ale nie chcę jeszcze go tutaj publikować, bo może ktoś z Was podpowie coś ciekawego (nie chcę niczego sugerować). Dobrym przykładem mogą być wytyczne dostępne na StackExchange (polecam lekturę szczególnie pierwszego linka): https://electronics.stackexchange.com/conduct https://electronics.stackexchange.com/help/how-to-ask Co sądzicie o wprowadzeniu tego typu zasad? Macie jakieś propozycje wytycznych, które warto byłoby wprowadzić? Podkreślam, że chodzi o zasady, które będą dotyczy obu stron: pytających oraz odpowiadających.
  37. 7 punktów
    Chciałem zaprezentować mój pierwszy edukacyjny projekt o nazwie kodowej KLOSZARD, który stanowi połączenie pojazdu sterowanego i autonomicznego. Swoją nazwę zawdzięcza temu, że zbudowałem go z najtańszych części ze sklepów wysyłkowych, a jego głównym zadaniem jest pałętanie się po mieszkaniu bez większego celu Początki Pierwsza wersja powstała na gotowym podwoziu robota 2WD. Posiadała jeden moduł mikrokontrolera (Blue Pro Micro) i czujnik odległości (wpięte na breadboardzie). Była zasilana z czterech baterii AA i uwzględniała sterowanie radiowe (moduł + pilot z czterema przyciskami). Niestety wszystkie te elementy potwornie mnie zawiodły, a przede wszystkim to, że pojazd nie potrafił utrzymać prostego kierunku jazdy. Postanowiłem więc podejść do sprawy bardziej profesjonalnie Konstrukcja Obecne czterokołowe podwozie oparte jest na płytach plexi (dociętych na wymiar z portalu aukcyjnego) połączonych kołkami dystansowymi. Posiada cztery silniki z podwójnym wałem (DC 6V z przekładnią 1:48). Na każdym wale umieściłem tarcze z otworami oraz czujniki szczelinowe stanowiące enkodery optyczne. Moduły czujników FC-03 mają wlutowane kondensatory między pin D0 i GND w celu eliminowania błędnych/fałszywych impulsów (rozwiązanie znalezione w sieci). Pierwsze piętro robota wyposażyłem w pięć czujników odległości, koszyk na akumulatory i przełącznik zasilania. Cała konstrukcja zwieńczona jest gustownym zadaszeniem z plexi Zasilanie Maszynę zasilają dwa ogniwa litowo-jonowe (2S 7.4V). Część logiczna otrzymuje napięcie 5V dzięki miniaturowej przetwornicy step-up/step-down (Pololu S7V8A). Silniki sterowane są przez dwa masywne moduły z radiatorami oparte na dwukanałowym układzie L298N. Moduł jezdny Na parterze konstrukcji umieściłem Arduino Pro Micro, które nieustannie oblicza prędkość obrotową każdego wału oraz reguluje napięcia silników w celu uzyskania prędkości zadanej przez moduł główny. Dzięki temu koła mogą niezależnie poruszać się z jednakową prędkością. Komunikacja z modułem głównym odbywa się po magistrali I2C. Komenda sterująca zawiera trzy liczby: 1. prędkość lewej strony, 2. prędkość prawej strony (koło przednie i tylne otrzymują tę samą wartość w przedziale od -100 do +100 RPM) oraz 3. długość zadania mierzona w impulsach enkodera (0 = zadanie ciągłe, nieskończone). [DriveModule.ino] #include <Wire.h> #include <PID_v1.h> /**************************************************************************************************/ class MotorController { static const int8_t SMPL_COUNT = 4; // speed calculation every 4 sensor events static const double RPM_CONST = (60000000 * (SMPL_COUNT / 40.0)); // 20 holes, 40 events bool lastState; byte encPin, fwdPin, bckPin, pwmPin; int8_t signedRpm = 0; double desiredRpm = 0, pwmVal, currRpm = 0; PID *Pid; public: bool setpointReached; uint32_t counter = 0, lastMeasurement = 0, setpoint = 0; MotorController(byte encPin, byte fwdPin, byte bckPin, byte pwmPin) { pinMode(this->encPin = encPin, INPUT); pinMode(this->fwdPin = fwdPin, OUTPUT); pinMode(this->bckPin = bckPin, OUTPUT); pinMode(this->pwmPin = pwmPin, OUTPUT); lastState = !digitalRead(encPin); Pid = new PID(&currRpm, &pwmVal, &desiredRpm, 0.4, 1.5, 0, DIRECT); Pid->SetSampleTime(50); // PID calculation every 50 milliseconds setRpm(0); } int8_t speedMeasurement() { // ISR bool state = digitalRead(encPin); if (state != lastState) { lastState = state; counter++; setpointReached = (setpoint && (counter >= setpoint)); if ((counter % SMPL_COUNT) == 0) { uint32_t currTime = micros(); currRpm = RPM_CONST / (currTime - lastMeasurement); lastMeasurement = currTime; return 2; // RPM calculated } return 1; // sensor event occurred } return 0; // nothing happened } int8_t voltageAdjustment() { if (setpointReached) return 2; // task completed if (!Pid->Compute()) return 0; // nothing happened, no adjustment or PID turned off noInterrupts(); uint32_t eventDuration = micros() - lastMeasurement; if (eventDuration > 400000) currRpm = 0; // no event for 400ms, engine stopped interrupts(); analogWrite(pwmPin, pwmVal); return (eventDuration < 3000000) ? // no event for 3 seconds means danger of power overload 1 /* voltage adjustment */ : 3 /* power overload */; } void setRpm(int8_t rpm, byte initPwm = 0) { if (((rpm ^ signedRpm) < 0) || (rpm && !signedRpm)) { // if new RPM has opposite sign or ... noInterrupts(); currRpm = 0; interrupts(); pwmVal = initPwm; } signedRpm = rpm; desiredRpm = abs(rpm); digitalWrite(fwdPin, rpm > 0 ? HIGH : LOW); // setting direction depending on sign of RPM digitalWrite(bckPin, rpm < 0 ? HIGH : LOW); Pid->SetMode(rpm ? AUTOMATIC : MANUAL); // PID switch ON-OFF } }; /**************************************************************************************************/ class VehicleController { static const byte MAX_PWM = 255; static const byte MID_PWM = 100; uint32_t lastTime = 0; static int ascComp(const void *a, const void *b) { return (*(uint32_t *)a - *(uint32_t *)b); } public: bool powerOverload = false, isRunning = false; int8_t taskCompleted = 0; static const int8_t M_LEN = 4; MotorController motor[M_LEN] = { {/*ENC1*/ 12, /*FIN1*/ 4, /*FIN2*/ 2, /*FENA*/ 3}, {/*ENC2*/ 11, /*FIN3*/ 8, /*FIN4*/ 7, /*FENB*/ 9}, {/*ENC3*/ 10, /*BIN1*/ A1, /*BIN2*/ A0, /*BENA*/ 6}, {/*ENC4*/ 13, /*BIN3*/ A3, /*BIN4*/ A2, /*BENB*/ 5} }; void interruptRoutine() { // ISR for (byte i = 0; i < M_LEN; i++) motor[i].speedMeasurement(); } void loopRoutine() { for (int8_t i = 0; i < M_LEN; i++) { switch (motor[i].voltageAdjustment()) { case 0: // nothing happened break; case 1: // voltage adjustment break; case 2: // task completed taskCompleted++; noInterrupts(); motor[i].setpointReached = motor[i].setpoint = 0; interrupts(); break; case 3: // power overload powerOverload = true; setSpeed(0, 0); return; } } if (taskCompleted >= M_LEN) setSpeed(0, 0); } void setSpeed(int8_t left, int8_t right, uint32_t taskLen = 0) { uint32_t now = micros(); isRunning = left || right; noInterrupts(); if (isRunning) { powerOverload = taskCompleted = 0; for (byte i = 0; i < M_LEN; i++) { motor[i].lastMeasurement = now; motor[i].setpointReached = motor[i].counter = 0; motor[i].setpoint = taskLen; } } else { for (byte i = 0; i < M_LEN; i++) motor[i].setpointReached = motor[i].setpoint = 0; } interrupts(); motor[0].setRpm(left, MID_PWM); motor[1].setRpm(right, MID_PWM); motor[3].setRpm(left, MID_PWM); motor[2].setRpm(right, MID_PWM); } } vehicle; /**************************************************************************************************/ void setChangeInterrupt(byte pin) { pinMode(pin, INPUT); *digitalPinToPCMSK(pin) |= bit(digitalPinToPCMSKbit(pin)); PCIFR |= bit(digitalPinToPCICRbit(pin)); PCICR |= bit(digitalPinToPCICRbit(pin)); } volatile int8_t gblLeftSpeed = 0, gblRightSpeed = 0; volatile uint8_t gblTaskLen = 0; void setup() { Wire.begin(44); Wire.onReceive(i2cReceiveEvent); Wire.onRequest(i2cRequestEvent); for (byte i = 10; i <= 13; i++) setChangeInterrupt(i); // PCINT0 int8_t tmpLSpeed = 0, tmpRSpeed = 0; uint8_t tmpTaskLen = 0; while (true) { vehicle.loopRoutine(); if (gblLeftSpeed != tmpLSpeed || gblRightSpeed != tmpRSpeed || gblTaskLen != tmpTaskLen) { tmpLSpeed = gblLeftSpeed; tmpRSpeed = gblRightSpeed; tmpTaskLen = gblTaskLen; vehicle.setSpeed(tmpLSpeed, tmpRSpeed, tmpTaskLen); } } } void i2cReceiveEvent(int howMany) { if (howMany >= 3) { gblLeftSpeed = Wire.read(); gblRightSpeed = Wire.read(); gblTaskLen = Wire.read(); howMany =- 3; } while (howMany--) Wire.read(); } void i2cRequestEvent() { // task ended Wire.write(gblTaskLen ? vehicle.taskCompleted >= vehicle.M_LEN : 1); } ISR(PCINT0_vect) { vehicle.interruptRoutine(); } Moduł zbliżeniowy Pięcioma czujnikami odległości (4x HC-SR04 i 1x US-015) steruje samotna Atmega328P na płytce uniwersalnej (wgrany bootloader wykorzystuje wewnętrzny oscylator 8MHz). Każde żądanie danych (wysłane po I2C) do tego modułu jest sygnałem do wykonania kolejnego pomiaru ze wszystkich czujników (po kolei). [ProximityModule.ino] #include <Wire.h> #include <NewPing.h> NewPing sensor[] = { NewPing(12, 12, 200), NewPing(5, 5, 200), NewPing(13, 13, 200), NewPing(4, 4, 200), NewPing(A3, A3, 200) }; const byte ORDER[] = {0, 2, 4, 1, 3}; const byte CNT = sizeof(ORDER); volatile byte distance[CNT] = {}; volatile bool takeNext = false; void setup() { Wire.begin(40); Wire.onRequest(requestEvent); while (true) { if (!takeNext) continue; takeNext = false; for (byte i = 0; i < CNT; i++) { distance[ORDER[i]] = sensor[ORDER[i]].ping_cm(); delay(8); } } } void requestEvent() { for (byte i = 0; i < CNT; i++) Wire.write(distance[i]); takeNext = true; } Sterowanie Pojazd może być sterowany za pomocą smartfonu z Androidem. Aplikacja napisana w Java’ie (Android Studio) przełącza się na WiFi rozgłaszane przez robota i przesyła drogą sieciową (na ustalony adres IP i port) krótkie instrukcje sterujące. Przy tej okazji po raz pierwszy w życiu doceniłem prostotę protokołu UDP – każda komenda znajduje się w osobnym pakiecie danych Nie zamieszczam źródeł aplikacji ponieważ zawierają one mnóstwo nadmiarowego kodu związanego bardziej z interfejsem użytkownika. Moduł główny Przypadkowo wpadłem w posiadanie modułu WiFi NodeMCU v3 z rodziny układów ESP8266. Podczas pierwszych testów w Arduino IDE zauważyłem, że najprostsze szkice wygrywają się do niego potwornie długo. Do tej pory nie znalazłem rozwiązania tej niedogodności, ale w trakcie poszukiwań natrafiłem na firmware obsługujący prosty system plików oraz interpreter języka skryptowego Lua. http://nodemcu.readthedocs.io Koncepcja asynchronicznych callbacków wywoływanych przez zdarzenia i timery (znana mi z JavaScriptu) wydała się genialnym rozwiązaniem symulującym pracę wielozadaniową w środowisku jednowątkowym, dlatego postanowiłem przyjrzeć się temu środowisku. NodeMCU pracuje w standardzie 3.3V. Konwerter poziomów logicznych został ukryty na płytce uniwersalnej bezpośrednio pod modułem Pojazd posiada możliwość autonomicznej jazdy aktywowanej z poziomu aplikacji lub wbudowanego w moduł przycisku FLASH. W tym trybie robot ma za zadanie objechać całe mieszkanie tak, aby po jego prawej stronie w odległości do 40 cm zawsze znajdowała się jakaś przeszkoda. W przypadku braku przeszkody robot obraca się w prawo mniej-więcej o 90 stopni i jedzie prosto. Przeszkoda od strony frontowej uruchamia poszukiwanie otwartej przestrzeni przy jednoczesnym obracaniu w lewo. [main.lua] print("Starting Kloszard") local klrd = require "kloszard" local snsr = require "sensor" local autoDrive = { timer = tmr.create() } function autoDrive:onSetup() self.timer:register(100, tmr.ALARM_AUTO, function() local s = klrd.prxRead() for i = 1, 5 do snsr[i]:add(s:byte(i)) end local act = self:action() if act then self.action = act if act == self.main then -- drive forward klrd.drvSend(45, 45) end end end) self:onStop() end function autoDrive:onStart() self.action = function() return self.main end klrd.prxRead() self.timer:start() snsr.sideCheckLock = true end function autoDrive:onStop() self.timer:stop() klrd.drvSend(0, 0) end function autoDrive:onFinish() self:onStop() self.timer:unregister() end function autoDrive:main() -- obstacle ahead if math.min(snsr[2].val, snsr[3].val, snsr[4].val) < 20 then klrd.drvSend(-45, 45) return function() -- finding escape if snsr[2]:gt(20) and snsr[3]:gt(20, 3) and snsr[4].val > 20 then snsr.sideCheckLock = true return self.main end end end -- open space on right side if snsr:rightSideOpen(40) then klrd.drvSend(45, -45, 30) -- turn right return function() if klrd.drvTaskEnded() then return self.main end end end -- right side collision risk if snsr[5]:lt(10) and snsr[5]:get(5) > snsr[5].val then klrd.drvSend(0, 45) -- course correction return function() if snsr[5]:get(5) < snsr[5].val then return self.main end end end end klrd.setup(autoDrive) [sensor.lua] local Queue = { 100, 100, 100, 100, 100, pos = 1, LEN = 5, val = 100 } Queue.__index = Queue Queue.new = function() return setmetatable({}, Queue) end function Queue:add(value) if value == 0 then value = self.val end self[self.pos], self.val = value, value self.pos = self.pos + 1 if self.pos > self.LEN then self.pos = 1 end end function Queue:get(index) if index > self.LEN then index = self.LEN elseif index < 1 then index = 1 end local shift = self.pos - index if shift < 1 then shift = shift + self.LEN end return self[shift] end function Queue:gt(value, len) -- values greater than if not len then len = self.LEN end for i = 1, len do if self:get(i) <= value then return false end end return true end function Queue:lt(value, len) -- values less than if not len then len = self.LEN end for i = 1, len do if self:get(i) >= value then return false end end return true end local M = { sideCheckLock = true } for i = 1, 5 do M[i] = Queue:new() end function M:rightSideOpen(distance) local cnt = 0 for i = 1, self[5].LEN do if self[5][i] > distance then cnt = cnt + 1 end end if self.sideCheckLock then self.sideCheckLock = cnt > 0 return false else self.sideCheckLock = cnt == self[5].LEN return self.sideCheckLock end end return M [kloszard.lua] local M = {} local AUTOBTN_PIN = 3 local CMD_PING = 121 local DRVM_ADDR = 44 -- DriveModule Address local CMD_DRIVE = 122 local PRXM_ADDR = 40 -- ProximityModule Address local CMD_AUTO = 123 local UDP_PORT = 50000 function M.setup(autoDrive) M.autoDrive = autoDrive wifi.setmode(wifi.SOFTAP) wifi.ap.setip({ ip = "192.168.1.1", netmask = "255.255.255.0", gateway = "192.168.1.1" }) wifi.ap.config({ ssid = "KloszardWiFi", pwd = "abcd1234" }) i2c.setup(0, 5, 6, i2c.SLOW) -- SDA pin 5, SCL pin 6 autoDrive:onSetup() M.udpSock = net.createUDPSocket() M.udpSock:listen(UDP_PORT) M.udpSock:on("receive", function(sock, data, port, ip) if data:byte(1) == CMD_PING then sock:send(port, ip, "PONG") elseif data:byte(1) == CMD_DRIVE then M.drvSend(data:byte(2), data:byte(3), data:byte(4)) elseif data:byte(1) == CMD_AUTO then if data:byte(2) ~= 0 then autoDrive:onStart() else autoDrive:onStop() end end end) gpio.trig(AUTOBTN_PIN, "up", function() if autoDrive.timer:state() then autoDrive:onStop() else autoDrive:onStart() end end) M.drvSend(0, 0) end function M.finish() M.autoDrive:onFinish() M.udpSock:close() wifi.setmode(wifi.NULLMODE) end local lastDrvmLeft, lastDrvmRight, lastTaskLen = 0, 0, 0 function M.drvSend(left, right, taskLen) if not taskLen then taskLen = 0 end if lastDrvmLeft == left and lastDrvmRight == right and lastTaskLen == taskLen -- ignore function call if nothing changed then return end lastDrvmLeft, lastDrvmRight, lastTaskLen = left, right, taskLen left = bit.band(left, 0xFF) right = bit.band(right, 0xFF) i2c.start(0) i2c.address(0, DRVM_ADDR, i2c.TRANSMITTER) i2c.write(0, left, right, taskLen) i2c.stop(0) end function M.drvTaskEnded() i2c.start(0) i2c.address(0, DRVM_ADDR, i2c.RECEIVER) local res = i2c.read(0, 1) i2c.stop(0) return res:byte(1) ~= 0 end function M.prxRead() i2c.start(0) i2c.address(0, PRXM_ADDR, i2c.RECEIVER) local res = i2c.read(0, 5) i2c.stop(0) return res end return M WebEspDitor Konieczność ciągłego podłączania kabla USB w celu modyfikacji najdrobniejszych parametrów kodu zmotywowała mnie do napisania dodatkowego skryptu o nazwie WebEspDitor. Jest to prosty serwer/serwis HTTP, który umożliwia edycję plików znajdujących się w pamięci nodeMCU z poziomu przeglądarki internetowej. Skrypt wyświetla również wyniki funkcji print i ostatni błąd aplikacji, w związku z czym powinien być uruchamiany na wstępie jako init.lua (kod aplikacji należy przenieść do pliku main.lua). [init.lua] gpio.mode(3, gpio.INPUT, gpio.PULLUP) -- FLASH BUTTON if gpio.read(3) == 0 then return end -- boot loop protection print("Starting WebEspDitor...") net.createServer():listen(80, function(socket) local htmlTmpl = { -- 1 -- [===[HTTP/1.0 200 OK Content-Type: text/html; charset=UTF-8 Connection: close <!DOCTYPE html><html><head><title>WebEspDitor</title><meta name="viewport" content="width=device-width"><link rel="icon" href="data:;base64,="><style> a{text-decoration:none;color:navy}a:hover{text-decoration:underline}body{ line-height:1.5em;font-family:monospace}</style></head><body> ]===], -- 2 -- [===[<p><a href="/">WebEspDitor</a> [ <a href="/reset">reset</a> ]</p>]===], -- 3 -- [===[<form action="/save/<!FNAME>" method="post" enctype="text/plain"> <textarea name="s" spellcheck="false" style="position:absolute;width:100%; height:100%;margin:0;border:0;padding:4px;resize:none;white-space:pre; box-sizing:border-box">]===], -- 4 -- [===[</textarea><input style="position:absolute;bottom:0;right:0" type="submit" value="Save"></form><style>body{margin:0;overflow:hidden}</style>]===], -- 5 -- [===[<input id="name" type="text"><input type="button" value="create" onclick="location.href='/edit/'+document.getElementById('name').value">]===], -- 6 -- [===[<script>setTimeout(function(){location.href='/'},2000)</script>]===], -- 7 -- [===[<li><a href="/edit/<!FNAME>"><!FNAME></a> (<!FSIZE>, <a href="/delete/<!FNAME>" onclick="return confirm('Are you sure to delete'+ ' file \'<!FNAME>\'?')">del</a>)</li>]===] } local rqst, rspn = { length = 0, totalLen = 0, fname = nil }, { } local function getLine(str, bgnPos) local line, endPos = "", str:find("\r\n", bgnPos, true) if endPos then line = str:sub(bgnPos, endPos - 1) bgnPos = endPos + 2 end return line, bgnPos end local function sendRspn(sck) -- send response table to client if #rspn > 0 then sck:send(table.remove(rspn, 1), sendRspn) else sck:close() end end local function saveRqst(sck, data) -- save incoming content to file if #data > 0 then rqst[#rqst+1] = data end rqst.length = rqst.length + #data if rqst.length >= rqst.totalLen then -- last network frame data = nil rqst[1] = rqst[1]:sub(3) -- enctype text/plain: rqst[#rqst] = rqst[#rqst]:sub(1, -3) -- s=[data]CRLF local fd = file.open(rqst.fname, "w+") if fd then while #rqst > 0 do fd:write(table.remove(rqst, 1)) end fd:close() end collectgarbage("collect") sendRspn(sck) end end socket:on("receive", function(sck, data) rspn[1] = htmlTmpl[1] local line, dataPos = getLine(data, 1) -- 1 2 3 4 5 local parts = {} -- GET /cmd/prm HTTP/1.1 for elm in line:gmatch("[^/ ]+") do parts[#parts+1] = elm end if #parts == 5 then parts[3] = parts[3]:gsub("[^a-zA-Z0-9_.-]", "") end if parts[1] == "POST" then repeat line, dataPos = getLine(data, dataPos) local len = line:match("Content%-Length: (%d+)") if len then rqst.totalLen = tonumber(len) end until #line == 0 end -- EDIT if parts[2] == "edit" and #parts == 5 then local tmp = htmlTmpl[3]:gsub("<!FNAME>", parts[3], 1) rspn[#rspn+1] = tmp tmp = #rspn + 1 local fd = file.open(parts[3], "r") if fd then while true do local chunk = fd:read(512) if chunk then rspn[#rspn+1] = chunk else break end end fd:close() end for i = tmp, #rspn do rspn[i] = rspn[i]:gsub("&", "&amp;"):gsub("<", "&lt;") end rspn[#rspn+1] = htmlTmpl[4] else rspn[#rspn+1] = htmlTmpl[2] -- SAVE if parts[2] == "save" and #parts == 5 then rqst.fname = parts[3] rspn[#rspn+1] = '<p>File "'..parts[3]..'" has been saved.</p>'..htmlTmpl[6] -- DELETE elseif parts[2] == "delete" and #parts == 5 then file.remove(parts[3]) rspn[#rspn+1] = '<p>File "'..parts[3]..'" has been deleted.</p>'..htmlTmpl[6] -- RESET elseif parts[2] == "reset" then local timer = tmr.create() timer:register(500, tmr.ALARM_SINGLE, function() node.restart() end) timer:start() rspn[#rspn+1] = '<p>Waiting for device...</p>'..htmlTmpl[6] -- INDEX else local res = {'<ul>'} for n, s in pairs(file.list()) do if not n:find("init.", 1, true) then res[#res+1] = htmlTmpl[7]:gsub("<!FNAME>", n):gsub("<!FSIZE>", s) end end res[#res+1] = '</ul>' rspn[#rspn+1] = table.concat(res)..htmlTmpl[5] local res = file.getcontents("init.out") if res then rspn[#rspn+1] = '<p>Lua interpreter output:<pre>'..res..'</pre></p>' end end end rspn[#rspn+1] = '</body></html>' socket, htmlTmpl, line, parts = nil collectgarbage("collect") -- FILE UPLOAD if rqst.fname and rqst.totalLen > 0 then sck:on("receive", saveRqst) saveRqst(sck, data:sub(dataPos)) -- NORMAL REQUEST else sendRspn(sck) end end) end) print("Starting main...") do local res, err file.remove("init.out") node.output(function(s) local fd = file.open("init.out", "a+") if fd then fd:write(s) fd:close() end end, 1) if file.exists("main.lc") then res, err = pcall(function() dofile("main.lc") end) else res, err = pcall(function() dofile("main.lua") end) end if not res then print(err) end end Dalszy rozwój Najsłabszymi elementami obecnej wersji KLOSZARDa są plastikowe przekładnie i wały silniczków, które coraz bardziej uginają się pod ciężarem konstrukcji (1,06 kg). Na horyzoncie rozbudowy pojawiła się jednak nadzieja w postaci identycznych silników z przekładnią metalową. Mam nadzieję, że uda się je bezkarnie podmienić Drugie miejsce niedoskonałości zajmuje sam w sobie NodeMCU, który cierpi z powodu niedostatku pamięci RAM oraz tendencji do resetowania w przypadku wszelkich problemów zarówno softwarowych jak i hardwarowych. W tym przypadku rozwiązaniem wydaje się przejście na Raspberry Pi Zero (WiFi+BT), czego dodatkowym atutem byłoby całkowite uwolnienie projektowania i debugowania od kabla USB Dalsze prace będą ukierunkowane na rozwój oprogramowania (bardziej zaawansowane przetwarzanie danych z modułu zbliżeniowego i jezdnego), a może nawet uda się zaimplementować jakąś skromną formę lokalizowania w przestrzeni.
  38. 7 punktów
    Cześć. Chciałem wam pokazać projekt nad którym pracuję: PENETRATOR Trochę konkretów: aluminiowy kadłub napęd: dwa silniki elektryczne szczotkowe 9V zasilanie 2 x 18650 + 2 x 18650 ukryte pod kadłubem kontroler: Raspberry Pi 3 A+ wizja: moduł kamery 1080p emitery IR umożliwiające kamerze pracę w ciemnościach łączność: WiFi (access point i serwer HTTP) język programowania : Python 3 sterowanie i obraz: autorska aplikacja webowa (w przeglądarce) umożliwiająca wyświetlanie obrazu z kamery i sterowanie robotem W PLANACH: mechaniczne ramię 7DOF oparte na serwomechanizmach MG996R obracanie kamery w 2 płaszczyznach przy pomocy serwomechanizmów mam też ochotę pobawić się sztuczną inteligencją, stąd wybór raspberry a nie np. esp8266 Przy okazji, podziękowania dla Forbot.pl za podstawy które wprowadziły mnie do świata robotyki
  39. 7 punktów
    Witam, jestem początkujący oraz jest to mój pierwszy projekt. Dałem sobie za zadanie zrobienie przez wakacje maszyny, która policzy mi ile jest w moim zbiorze groszy, ale chciałem zrobić to trochę inaczej niż projekty, które widziałem. Wpadłem na pomysł że skoro grosze (1, 2, 5) są różnych średnic to jeżeli ułoży się monetę miedzy fotorezystorem a diodą led to będzie można przypisać jej daną wartość. Tak też zrobiłem i po chwili powstał mały cylinder na bazie Arduino Uno potrafiący rozpoznać monetę. Następnie zacząłem pracę nad tym aby moja maszyna brała monety z podajnika i liczyła je automatycznie. Wyszło że cylinder z ustawionym centralnie serwomechanizmem to najlepsza opcja. Projektowałem na programie Design Spark Mechanical, drukowałem Anet A8. Po wielu nieudanych wydrukach, wkońcu wyszedł ten właściwy i mogłem wszystko odczepić od Arduino Uno... zaczeło się lutowanie do Arduino nano, jak mozna się spodziewać po początkującym długie i mało umiejętne. Po wydrukowaniu całego mechanizmu zaprojektowałem obudowę, a następnie zrobiłem adaptacje do warunków oświetleniowych panujących w jej wnętrzu. Z uwagi na małą precyzję mojego czujnika maszynka czasem się myli, o jakiś grosz/dwa na złotówkę, jednakże jestem zadowolony z mojego pierwszego projektu :)) A oto krótki i prosty kod : #include <Servo.h> #include <Wire.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); Servo servo; int odczytanaWartosc = 0; int tlo = 0; float wartosc = 0; float kwota = 0; void setup() { lcd.begin(16,2); lcd.backlight(); lcd.setCursor(0,0); pinMode(6, OUTPUT); Serial.begin(9600); digitalWrite(6,HIGH); servo.attach(9); } void loop() { kwota = wartosc / 100; lcd.setCursor(0,0); lcd.print("KWOTA"); lcd.setCursor(7,0); lcd.print(kwota); servo.write(180); delay(400); servo.write(80); delay(300); odczytanaWartosc = analogRead(A1); if(odczytanaWartosc > 30 && odczytanaWartosc < 50){wartosc = wartosc + 1;} if(odczytanaWartosc > 22 && odczytanaWartosc < 30){wartosc = wartosc + 2;} if(odczytanaWartosc < 22 && odczytanaWartosc > 3){wartosc = wartosc + 5;} servo.write(0); delay(400);}
  40. 6 punktów
    Witam wszystkich czytelników. Zainspirowałem się w wakacje postem użytkownika @Krzysiek97, który przedstawił swoją kierownice do gier na bazie arduino leonardo: Kierownica PC - wersja 2. Z racji tego że lubię grać w gry wyścigowe i symulatory postanowiłem zbudować własną kierownicę z dodatkowymi akcesoriami. Nie chciałem przerabiać starej gotowej kierownicy do komputera, więc wpadłem na pomysł aby zbudować całe stanowisko. Materiał o tym stanowisku/ projekcie znajduję się na moim kanale na YT do którego was zapraszam Kierownica do komputera na bazie arduino Chciałem aby w tym stanowisko znajdowała się kierownica o kącie obrotu 900 stopni, sprzęgło, gaz, hamulec, 8 biegów + wsteczny (8 biegów ponieważ tyle mają niektóre samochody np. w Forza Horizon 4), hamulec ręczny, 2 joystiki (do sterowania maszynami w Farming Simualtor), button matrix 4x5 = 20 przycisków (przypisanych do rożnych akcji w grach) i zegary do wyświetlania prędkości i obrotów. Tak więc gdzieś w połowie lipca zacząłem szukać potrzebne części. Pierwszym problemem z którym się spotkałem była niewystarczająca ilość wejść w arduino leonardo. Ktoś na tym forum podsunął mi płytki Nucleo 64 na STM32, obawiałem się jednak czy programy które wcześniej znalazłem w internecie będą z nimi współpracować. Bawiłem się naco wcześniej arduino lecz nucleo nie stąd moja niepewność ponieważ zaczynałem dopiero wtedy zabawę z tym wszystkim. Zdecydowałem się jednak zostać przy arduino i zakupiłem 3 płytki, po jednej dla każdego programu który obsługuję inną część stanowiska, ponieważ i tak nie ma prgoramu który ogarnie wszystkie moje rzeczy na raz. A więc tak: Arduino Leonardo - program EMC Utility Lite (z początku korzystałem z RFR Whell Configuration elcz sprawiał on problemy) - obsługuję kierownicę, pedały, hamulec ręczny - Link do programu EMC, Jak zainstalować program Pierwsze Arduino Pro Micro - program MMJoy2 - obsługuję button matrix i 2 joysticki - Link do programu MMJoy2, Jak zainstalować program Drugie Arduino Pro Micro - program SimHub - obsługuję zegary/wyświetlacze - Link do programu SimHub Zamówiłem też 20 guzików (push button), 10 styczników krańcowych, 2 joysticki, 2 wyświetlacze Tm1638, 1 wyświetlacz Max7219 (zamówiłem też sterownik silnika BTS7960 lecz na razie nie zakładałem FFB). Rzeczy które miałem w domu to: 2 potencjometry 10k Ohm, stycznik krańcowy ls-11s, kable kawałki plastiku, materiału i gumy. Za postawę stanowiska posłużyła mi deska rozdzielcza i fotel od mazdy mx-5 i kierownica od mazdy 626. Całość jest przyspawana do rurki i przykręcona do euro palety. Z racji tego że deska pochodzi z anglika to nie mogłem zamontować zwykłych zegarów w miejscu poduszki pasażera. Zamieszczam tutaj scheamty podłączeń danych elementów: Drugim problemem który chce tu opisać, było przeniesienie/ zczytanie obrotu z kierownicy do arduino. Na początku chciałem wykorzystać enkoder optyczny z swojej starej drukarki, lecz gubił się on często i nie działał dokładnie, więc kupiłem enkoder inkrementalny 600ppr. Nie będę się już tak rozpisywał co jak i gdzie jest skręcone dlatego wszystko pokazane i omówione jest w filmiku do którego link jest na początku posta. Więc to jest dodatkowy materiał dla ciekawych. Podsumowując: koszt budowy stanowiska zamknął się dla mnie w kwocie 300zl, czas realizacji od pierwszego pomysłu do zbudowania całości i upewnienia się że wszystko jest sprawne to 6 miesięcy. Tak oto prezentuję się kierownica i jej działanie w grze Forza Horizon 4 Na koniec pytanie głownie do administratora, czy i kiedy będzie znowu dostępny konkurs Opisz elektroniczne DIY i odbierz 50 zł rabatu do Botland?
  41. 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
  42. 6 punktów
    Witam,to mój pierwszy samodzielny projekt mam 14 lat. Na początku przyszedł mi pomysł na zrobienie 4 kołowego pojazdu zdalnie sterowanego .Nie ukrywam że część wiedzy wziąłem z kursu budowy robotów na Forbocie. Wyglądało to następująco: Później zdecydowałem się na rozbudowę projektu o ramię z patyczków i 4 serw: W najbliższym czasie planuje zmienić sterowanie na najtańszy moduł radiowy.Za około 4-7zł.Sterowany przez Arduino nano joystickami. Do pojazdu na razie użyłem: arduino uno motor control shield 4 serwa(tower pro 3×Mg995,1×sg90) 4silniki dc 2 akumulatorki 18650 4.2v czujnik ir , pilot do telewizora stabilizator kilka przewodów troche patyczków i kartonu.
  43. 6 punktów
    MQTT jest popularnym, bo prostym w obsłudze protokołem komunikacji. Najłatwiej jest porównać to do systemu YouTube: są subskrybenci i nadawcy. Nadawca może mieć wielu subskrybentów ale też jedno urządzenie może słuchać wielu nadawców (co - trochę jak w prawdziwym życiu - nie zawsze kończy się dobrze). Cała architektura wygląda w ogólnym przypadku w następujący sposób: W tym artykule zajmiemy się przygotowaniem środowiska oraz wysłaniem “hello world”. Zakładam, że na Raspberry jest zainstalowany raspbian. Jeśli nie to koniecznie sięgnij do odpowiedniego poradnika na Forbocie. Na Raspberry możesz pracować zdalnie lub lokalnie, to nijak nie wpływa na działanie systemu. 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. Konfiguracja serwera W charakterze serwera posłuży nam Raspberry Pi. W zasadzie każdy model powinien się sprawdzić, ale zalecane jest użycie przynajmniej drugiej wersji. Aby wszystko działało jak trzeba potrzebujemy pakietu Mosquitto. Wydajemy następujące komendy: aktualizacja systemu: sudo apt-get update && sudo apt-get upgrade -y instalacja mosquitto: sudo apt-get install mosquitto -y automatyczne uruchomienie przy starcie: sudo systemctl enable mosquitto.service I… to już. Serwer został zainstalowany. Teraz jeszcze tylko restart i możemy przejść dalej. Potrzebujemy jeszcze dwa urządzenia, które będziemy ze sobą komunikować. Pierwszym z nich będzie Raspberry Pi. Będzie na nim uruchomiona usługa Node-Red dzięki której będziemy mogli w łatwy sposób odczytywać dane i sterować urządzeniami wykonawczymi. Drugie urządzenie to ESP32. Przy jego pomocy będziemy sterować diodą, która potwierdzi poprawne przejście przez instalację. Node-Red Do instalacji potrzebujemy następującej komendy: sudo apt-get install nodered -y Po skończeniu instalacji możemy dodać automatyczne uruchamianie przy starcie: sudo systemctl enable nodered.service Aby wyłączyć automatyczny start wpisujemy: sudo systemctl disable nodered.service Uruchamiamy działanie serwisu poprzez komendę: sudo node-red-start Potrzebujemy teraz poznać IP maliny (jeśli pracujemy lokalnie). Wykonujemy to poprzez: ifconfig Jeśli przechodzisz przez ten artykuł tylko “dla zajawki” to możesz poprzestać na tej komendzie. Jeśli jednak stawiasz to “na stałe” to musisz zrobić jeszcze jedną rzecz, z którą nie bardzo mogę pomóc. Należy ustawić statyczne IP dla maliny. Robi się to na routerze, w panelu administracyjnym. Ze względu na mnogość rozwiązań różnych producentów routerów musisz poszukać odpowiedni poradnik w internecie. Jak wspomniałem, jest to tylko dla osób, które stawiają serwer na stałe. Po instalacji Node-Red przechodzimy do przeglądarki. W pasku na adres url wpisujemy adres IP z portem 1880. Czyli pracując lokalnie na Raspberry wpiszemy: 127.0.0.1:1880 natomiast pracując zdalnie wpiszemy ip:1880. W moim przypadku jest to 192.168.100.194:1880. Powinniśmy dostać taki obraz: Klikamy na trzy paski w prawym górnym rogu i wybieramy opcję “Manage palette”: W oknie dialogowym przechodzimy do zakładki install i wpisujemy dashboard. Wybieramy drugą opcję z góry: Po zatwierdzeniu instalacji czekamy, aż proces się skończy. Ta wtyczka umożliwia nam utworzenie graficznego interfejsu użytkownika. Po zakończeniu instalacji zamykamy okno dialogowe i z listy po lewej stronie wybieramy opcje oraz je łączymy. Wybieramy opcje MQTT (obie) z sekcji network oraz z sekcji dashboard wybieramy switch oraz show notification. Łączymy ze sobą te punkty wg następującego schematu: Następnie dwa razy klikamy w pierwszy obiekt mqtt. Klikamy w ikonkę z ołówkiem która otworzy nam dodatkowy panel. W otrzymanym polu edycji wpisujemy tylko localhost, klikamy Add. Dalej, w polu Topic wpisujemy esp32/message i ustawiamy QoS jako 1. Podobne kroki wykonujemy dla drugiego punktu z mqtt przy czym serwer powinien zostać uzupełniony automatycznie, w polu Topic wpisujemy esp32/gpio, ustawiamy QoS jako 1 i retain jako false. Ostatni punkt w konfiguracji Node-Red to ustawienie przełącznika. Wchodzimy w jego okno dialogowe, dodajemy nową grupę (Klikamy w ołówek koło pola “Group”) znowu klikamy ołówek dalej Add i znowu Add. W ustawieniach schodzimy trochę niżej i ustawiamy On Payload jako typ number (pierwsza rozwijana ikonka) i wpisujemy 1 oraz ustawiamy Off Payload jako number i wpisujemy 0. Klikamy Deploy w prawym górnym rogu i trzymamy kciuki. Po zapisaniu zmian otwieramy nowe okno przeglądarki i wpisujemy ip:1880/ui. Pozostała część adresu zostanie uzupełniona automatycznie. Mając tak przygotowane środowisko przechodzimy do ostatniego punktu czyli modułu ESP32. ESP32 Programować będziemy w Arduino IDE. Aby jednak móc to zrobić musimy przygotować środowisko. Nie będę się zagłębiał w ten temat bo jest wiele dobrych poradników o tym w internecie (np.: tutaj) Dodatkowo w managerze bibliotek instalujemy bibliotekę PubSubClient oraz ESPMQTTClient. W fizycznym podłączeniu warto jest sprawdzić najpierw pinout naszej płytki w internecie oraz ewentualnie zmienić numer pinu w kodzie. Następnie wybieramy odpowiednią płytkę, wgrywamy przykładowy szkic i… nie działa. Po pierwsze dlatego, że należy zmienić ssid (czyli nazwy sieci, do której jest podłączone też raspberry pi), hasło oraz adres IP serwera na adres maliny. Po drugie dlatego, że często są problemy z tymi płytkami (o tym w kolejnym akapicie). Jeśli jednak udało się wszystko wgrać powinniśmy dostać wiadomość w panelu sterowania oraz możemy sterować diodą przez przełącznik. Kod testowy prezentuje się następująco: #include <WiFi.h> #include <PubSubClient.h> const char* ssid = "Nazwa wifi"; //ZMIENIC na swoje const char* password = "haslo do wifi"; //ZMIENIC na swoje const char* mqtt_server = "IP Raspberry Pi"; //ZMIENIC na swoje const char* deviceName = "ESP32"; //poki co nie trzeba zmieniac //ale przy wiekszej ilosci urzaden kazde musi miec swoja nazwe const char* startMessageTopic = "esp32/message"; //temat do wyslania wiadomosci const char* pinTopic = "esp32/gpio"; //temat do odbioru wiadomosci const int ledPin = 27; //numer pinu diody, ZMIENIC JESLI TRZEBA WiFiClient espClient; PubSubClient client(espClient); void reconnect() { bool ctd = false; //funkcja jest wywolywana jesli utracono polaczenie z serwerem Serial.println("Rozlaczono!"); while(!ctd) { Serial.print("Laczenie z serwerem..."); if(client.connect(deviceName)) { ctd = true; Serial.println("Polaczono!"); } else { Serial.print("."); delay(1000); } } } void odbiorWiadomosci(String temat, byte* zawartosc, unsigned int dlugosc) { String pomoc; Serial.println("Odebrano wiadomosc:"); Serial.print("\tTemat: "); Serial.println(temat); Serial.print("\tTresc: \""); for(int i=0; i<dlugosc; i++) { Serial.print((char)zawartosc[i]); pomoc += (char)zawartosc[i]; } Serial.println("\""); if(temat == pinTopic) { if(pomoc == "1") { digitalWrite(ledPin, HIGH); Serial.println("LED1: ON"); } else if(pomoc == "0") { digitalWrite(ledPin, LOW); Serial.println("LED1: OFF"); } else Serial.println("Nieznana komenda, nie wiem co mam z tym zrobic"); } } void ustawienieWifi() { delay(10); Serial.println(); Serial.print("Laczenie z "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.print("Polaczona z wifi.\nESP otrzymalo adres IP: "); Serial.println(WiFi.localIP()); } void setup() { Serial.begin(115200); pinMode(ledPin,OUTPUT); ustawienieWifi(); //polaczenie z wifi delay(1000); client.setServer(mqtt_server, 1883); //ustawienie serwera mqtt client.connect(deviceName); //polaczenie z podana nazwa client.subscribe(pinTopic); //ustawienie nasluchiwania w podanym temacie client.setCallback(odbiorWiadomosci); //ustawienie funkcji do odbioru wiadomosci client.publish(startMessageTopic, "Hello from ESP32"); //wyslanie pierwszej wiadomosci } void loop() { if (!client.connected()) //jesli klient zostal rozlaczony { reconnect(); //polacz ponownie client.publish(startMessageTopic, "Hello from ESP32"); //wysliij wiadomoc powitalna } if(!client.loop()) client.connect(deviceName); //upewnienie sie, ze klient jest stale podlaczony } Jako, że jest to pierwszy kontakt z tym protokołem i programem pozwolę sobie nie zagłębiać się w szczegóły. Starałem się wszystko opisać w komentarzach kodu. Dodatkowo dużo rzeczy jest wyświetlanych w konsoli, więc warto tam zajrzeć. Problemy, problemy, problemy Często zdarza się tak, że płytki z rodziny ESP nie współpracują ze wszystkimi komputerami. W moim przypadku na 4 komputery bez problemu mogę podłączyć się z dwóch. Z jednego muszę używać programatora FTDI a jeden (najnowszy) działa trochę jak ślepy koń: nie widzi przeszkód (a w zasadzie nie widzi płytek). Jeśli natomiast są problemy przy wygrywaniu można spróbować wybrać inną płytkę w ustawieniach IDE lub pomajstrować z ustawieniami wybranej płytki (znowu odsyłam do internetu, źródeł trochę jest). Osobiście pracowałem na płytce Esp32 Wroom, a wgrywanie działało przy ustawieniach dla płytki LOLIN D32.
  44. 6 punktów
    To i ja się dołączę z moim projektem, który powolutku dłubię - detektorem promieniowania kosmicznego. Działa to tak, że sprawdza się sygnały z macierzy tub Geigera i na ich podstawie odrzuca się promieniowanie tła, a liczy się tylko promieniowanie kosmiczne (a konkretnie muony). Z tego, co na razie ogarnąłem, jeśli wystąpi sygnał w tubach leżących na linii prostej, to jest to zliczane jako muon, reszta jest ignorowana. Na renderingu jest macierz 4x4 tub, ale one są drogie, więc kupiłem tylko tyle, by starczyło na 3x3. By zwiększyć rozdzielczość (zakładając, że ilość muonów nie zmienia się szybko w czasie), pomyślałem, że detektor może się obracać. Stąd ten statyw, na rotorze znajdować się będzie cała elektronika, akumulator litowy i silnik krokowy by pozycjonować detektor. Sygnały będą obrabiane przez FPGA, pewnie nie ma takiej potrzeby, ale to IMHO w miarę fajny projekt by się nauczyć trochę FPGA
  45. 6 punktów
    Faktycznie, kod źródłowy sporo pomaga. Problem okazał się dużo ciekawszy niż w pierwszej chwili wyglądało, więc w końcu odnalazłem zakurzone blue pill, podłączyłem programator i postanowiłem sprawdzić o co w tym chodzi. Najpierw wersja skrócona - lepiej na razie nie używać CubeIDE... to nowe środowisko, powiedziałbym że wersja beta. Więc jest w nim dużo więcej błędów niż w starych i sprawdzonych. A teraz pełna wersja: Na początek faktycznie - błąd się pojawia, występuje w dość przypadkowych momentach, nie ma najmniejszego sensu... Chociażby podczas inicjalizacji DMA, HardFault jest wynikiem dereferencji wskaźnika NULL, który pojawia się magicznie. Więc typowe debugowanie niewiele daje oraz wskazuje na problemy ze stosem. I tutaj niestety musimy zejść do poziomu samego procesora. Po uruchomieniu mamy: Jak widzimy po prawej stronie wskaźnik stosu, czyli rejestr sp ma wartość 0x200001fc, czyli od początku pamięci SRAM dzieli go 0x1fc = 508 bajtów. To za mało na skomplikowany program działający na liczbach zmiennopozycyjnych. Teoretycznie wielkość stosu ustawiamy w pliku linkera, czyli STM32F103C8TX_FLASH.ld: Tutaj znajdziemy dwie ważne wartości. _estack to adres "końca" stosu, czyli najwyższego adresu zajmowanego przez stos. Widzimy tutaj pierwszy błąd, bo adresy powinny być podzielne przez 4, a często i przez 8. W każdym razie powinniśmy zmienić deklarację na: /* Highest address of the user mode stack */ _estack = 0x20005000; /* end of "RAM" Ram type memory */ Teraz to czego szukamy, czyli wielkość stosu: _Min_Stack_Size = 0x400 ; /* required amount of stack */ Domyślna wartość 0x400 oznacza 1024 bajty. Mikrokontroler STM32F103C8 ma 20 KiB pamięci SRAM, więc nie musimy być aż tak oszczędni - można ustawić stos na nieco większy. Okazuje się jednak, że zmiana _Min_Stack_Size nie wpływa na wartość rejestru SP, czyli faktyczny wskaźnik stosu... Mamy 508 bajtów bo coś się komuś pozajączkowało. Nie jestem pewien, czy to najlepsze rozwiązanie, ale można poprawić problem zmieniając plik startup_stm32f103c8tx.s, który znajdziemy w katalogu Startup. Teoretycznie rdzeń Cortex-M3 ustawia wartość rejestru SP podczas uruchamiania programu - ale to jak widać nie działa poprawnie. Możliwe że wynika to ze sposobu działania debuggera i po wgraniu do pamięci flash byłoby poprawnie, ale możemy zastosować "obejście" i sami ten rejestr ustawić. Do wspomnianego pliku wystarczy dodać jedną instrukcję: ldr sp, =_estack Program wygląda wówczas tak: I u mnie działa...
  46. 6 punktów
    Tak to jest, jak się najpierw ustala nazwę, a potem dopiero traktuje jako akronim i szuka jakiegoś sensownego określenia Ale jeśli ktoś czytał "Cieplarnię" Aldissa powinien już w tym momencie domyślać się, do czego służy SMARDZ (a jeśli nie to warto przeczytać). Kilka słów wyjaśnienia: bardzo lubię Arduino Pro Mini (choćby ze względu na cenę klonów i brak jakichkolwiek niepotrzebnych wodotrysków - ot, po prostu atmega z kwarcem i pinami na zgrabnej płytce). Niestety - o ile programowanie różnych mniej lub bardziej stacjonarnych urządzeń typu automatyka domowa jest całkiem wygodne (o ile się zadba o odpowiedni konwerter) - o tyle przy robotach, które jeżdżą, szwendają się po podłodze i ogólnie starają się usilnie zerwać wszystkie możliwe kable, sprawa staje się bardziej skomplikowana. Próbowałem jakichś wariackich rozwiązań na ESP8266 (coś w stylu "Serial over IP"), ale rezultaty były raczej mało interesujące. Postanowiłem więc przestać bawić się w oszczędzanie i użyć Raspberry Pi Zero W. Rozwiązanie (oprócz tego że działa bezproblemowo) ma jeszcze jedną zaletę: wszystkie pliki trzymane są na karcie pamięci malinki, układ katalogów zgodny jest z oryginalnym układem Arduino, a do komunikacji z malinką możemy użyć dowolnego komputera z zainstalowanym jakimś klientem SSH (np. PuTTY). I tu uwaga: mimo że opisuję dość dokładnie jak takie coś można sobie zrobić - zakładam u potencjalnego użytkownika podstawową wiedzę na temat Linuksa, Arduino i ogólnie elektroniki. Tak że proszę - bez pytań typu "a jak się przegrywa pliki na RPi" i "po co tam Arduino IDE jeśli go nie używam" Jak zwykle postanowiłem użyć elementów "wyciągniętych z szuflady". Co prawda jest mało prawdopodobne, aby wiele osób miało w szufladzie jakieś niepotrzebne malinki, ale ponieważ wymagania SMARDZa są bardzo małe, można po prostu na chwilę wyjąć ją z innego urządzenia. Nawet zakup RPi specjalnie do tego celu nie będzie niepotrzebnym wydatkiem - albo posłuży do programowania, albo znajdzie zastosowanie w innym urządzeniu (np. kamerze, serwerze octoprinta co tam sobie wymyślimy). Co do wymagań - potrzeba niewiele. Wystarczy stały adres IP w domowej sieci WiFi, dostępny interfejs Serial na ttyS0 i wolny pin BCM17. Co do malinki - można kupić wersję z wlutowanymi pinami, ale to chyba szczyt lenistwa, SMARDZ jest przeznaczony dla osób, które potrafią się lutownicą posługiwać i wlutowanie czterdziestopinowego gniazda nie powinno stanowić problemu. Zaoszczędzić też możemy na karcie SD, wcale nie musimy kupować najdroższej, wystarczy taka za kilkanaście złotych. Potrzebny będzie jeszcze konwerter napięć i jakieś drobne elementy montażowe. Tak więc mamy już malinkę (z pinami). Zakładam, że większość z tych, którzy chcieliby sobie coś takiego zrobić ma jakiś zasilacz (wystarczy mocniejsza ładowarka od komórki) - ja trafiłem w Tesco ostatnio ładowarkę 2.5A za niecałą dychę przy okazji kupowania jakichś bułeczek Instalacja jest prosta, chociaż trochę potrwa. Należy zacząć od skonfigurowania systemu tak, aby otrzymywał stały adres IP w sieci wewnętrznej. Ponieważ sposobów na to jest wiele i wybór ściśle zależy od warunków technicznych - odsyłam do oficjalnych instrukcji konfiguracji RPi. Potrzeba również, aby malinka miała dostęp do Internetu. Również pamiętajmy, że będzie nam potrzebny UART, czyli konfigurujemy malinkę tak, aby UART był aktywny, ale konsola serial wyłączona. W tym celu łączymy się z malinką przez SSH, wydajemy polecenie: sudo raspi-config i kolejno: Wybieramy punkt "Interfacing options" Z nastpnego menu wybieramy "Serial" Na pytanie "Would you like a login shell to be accessible over serial?" odpowiadamy "Nie" Na pytanie "Would you like the serial port hardware to be enabled?" odpowiadamy "Tak" Jako że najprawdopodobniej konfigurować to będziemy w trybie headless (nie bardzo sobie wyobrażam po co komu graficzne środowisko na komputerze bez podłączonego monitora - ale może ktoś ma takie upodobania) - na komputerze z którego przeprowadzamy instalację potrzebny będzie jeszcze vncviewer. I tu znów odsyłam do materiałów dotyczących używanego systemu operacyjnego - w przypadku Ubuntu czy Debiana najprostszy będzie xtightvncviewer. Oczywiście, jeśli mamy graficzne środowisko i podłączony monitor możemy z tego skorzystać! Zaczynamy od instalacji najpotrzebniejszych programów. Jeśli używamy otoczenia graficznego, wystarczy WiringPi i pyserial: sudo apt install wiringpi python-serial Jeśli instalujemy w trybie headless, potrzebne będą dodatkowe programy: sudo apt install wiringpi python-serial x11vnc xserver-xorg-video-dummy Teraz musimy zainstalować Arduino IDE. Tu znów w przypadku graficznego środowiska postępujemy zgodnie z oficjalnymi instrukcjami - Arduino IDE należy co najmniej raz uruchomić! Zajmijmy się więc wersją headless. Należy do katalogu domowego wgrać najnowszą wersję Arduiono IDE dla Linuxa ARM 32-bity (w chwili pisania jest to wersja 1.8.9) i rozpakować poleceniem: tar -xJf arduino-1.8.9-linuxarm.tar.xz Utworzony zostanie katalog arduino-1.8.9 zawierający wszystko, co potrzebne do pracy. Teraz należałoby uruchomić Arduino IDE - niestety, bez grafiki się nie da... trzeba więc nieco oszukać Javę żeby myślała, że wszystko jest w porządku. Najprostszym sposobem jest uruchomienie IDE w symulowanym środowisku graficznym. W tym celu wydajemy polecenie: xvfb-run arduinio-1.8.9/arduino i po odczekaniu dłuższej chwili (IDE musi wystartować, a tego nie widać) zatrzymanie programu przez ctrl-c. Powinny zostać utworzone katalogi ~/Arduino zawierający katalog libraries oraz ~/.arduino15 z (między innymi) plikiem preferences.txt. Jednak lepiej jest widzieć co się dzieje, poza tym nie wszyscy lubią zabawy z zipami i ściąganie bibliotek z githuba, w większości przypadków użycie okienkowego interfejsu jest po prostu wygodniejsze. A więc aby ujrzeć (teraz i następnym razem) interfejs Arduino IDE należy postąpić nieco inaczej. Wydajemy kolejno polecenia: Xvfb -display :20 & x11vnc -display :20 & DISPLAY=:20 arduino-1.8.9/arduino a na komputerze na którym pracujemy uruchamiamy przeglądarkę VNC i łączymy się z naszą malinką. Należy pamiętać, że x11vnc uruchomiony w ten sposób pozwala tylko na jedną sesję! Teraz po prostu po pokazaniu się właściwego interfejsu IDE możemy go zamknąć, a na konsoli malinki wydać polecenie: killall Xvfb Spowoduje to wyłączenie graficznego środowiska oraz serwera VNC. Tak więc nasza malinka jest już gotowa, możemy przystąpić do instalacji plików SMARDZa. Tu już jest to wręcz trywialne. Z załącznika do posta (smardz.zip) trzeba wyciągnąć plik smardz.tgz, przegrać go na naszą malinkę do katalogu domowego i rozpakować poleceniem: tar -xzf smardz.tgz Powinien zostać utworzony katalog bin, a w nim pliki wykonywalne: ardureset arducomp.py ardupload.py arduserial.py Możemy teraz sprawdzić, czy wersja Arduino IDE w pliku ardupload.py zgadza się z wersją zainstalowaną, ew. poprawić linijkę: aversion='1.8.9' zgodnie z zainstalowaną wersją Arduino IDE. Należy pamiętać, że w przypadku wgrania nowej wersji IDE trzeba będzie tę linijkę poprawić ponownie! Jeśli nie stosujemy malinki do innych celów lub żaden zainstalowany program nie używa pinu BCM17 możemy do /etc/rc.local dopisać: /usr/bin/gpio -g mode 17 output /usr/bin/gpio -g write 17 1 Jeśli tego nie zrobimy, może okazać się konieczne wydanie polecenia ardureset zaraz po uruchomieniu systemu. Mamy więc wszystko zainstalowane... ale jakoś to trzeba z naszym Arduino połączyć! Tu już wszystko zależy od inwencji użytkownika. Schemat połączeń będzie oczywiście podobny w każdym przypadku, zworka w obwodzie zasilania jest opcjonalna i służy do rozdzielenia zasilanie, jeśli chcemy oddzielnie zasilać Arduino i SMARDZa. Połączenia między Arduino a RPi powinny przechodzić poprzez konwerter napięć (wykorzystane trzy z czterech par), a więc sprowadzą się do: DTR/1 - GPIO17/11 TxD/2 - RxD/10 RxD/3 - TxD/8 Ja w swoim rozwiązaniu użyłem kawałka dwustronnej płytki uniwersalnej, gniazda zostały wycięte z jednej 40-pinowej pojedynczej listwy żeńskiej, ale to już zależy od własnych potrzeb i możliwości, każdy ma inne. Pozostaje jeszcze kwestia obudowy. Początkowo użyłem wersji z Thingiverse (nota bene bardzo wygodnej), postanowiłem jednak nieco ją zmodyfikować. Pliki STL oraz SCAD w załączniku! No i pora na testy. Potrzebne jest Arduino Pro Mini z wlutowanymi kątowymi pinami do programowania (ja stosuję zawsze pięć). Podłączamy Arduino do SMARDZa, podłączamy zasilanie do RPi, powinna zaświecić się dioda zasilania w Arduino. Teraz łączymy się przez SSH z naszą malinką. Potrzebny jest jakiś dowolny szkic (np. blink) umieszczony w katalogu Arduino (możemy go przegrać choćby z ~/arduino-1.8.9/examples/01.Basics/Blink). Wchodzimy do katalogu Arduino/Blink: cd ~/Arduino/Blink i wydajemy polecenie arducomp.py Program powinien się skompilować, zostać przesłany na Arduino i po chwili powinniśmy zobaczyć migającą diodę. Kolej na użycie monitora serial. W tym celu otwieramy drugie połączenie z malinką i wpisujemy polecenie arduserial.py Powinniśmy zobaczyć ekran monitora. Obsługa jest intuicyjna, wspomnę tylko że wpisanie czegokolwiek jest możliwe tylko przy aktywnym monitorze (przełączanym klawiszem F2), a prędkości przełącza się klawiszami kursora po wciśnięciu F3 przy nieaktywnym monitorze. Na pierwszej konsoli musimy skompilować jakiś program używający seriala. Jeśli nie macie nic takiego pod ręką, przygotowałem taki bardzo krótki test pozwalający sprawdzić zarówno nadawanie, jak i odbiór: void setup(void) { Serial.begin(9600); Serial.println("Program wystartował"); } void loop() { if (Serial.available()) { Serial.println((int)Serial.read()); } } Jeśli wszystko będzie w porządku - możemy już zająć się prawdziwą pracą z naszym robotem. Należy tylko zadbać o to, aby piny interfejsu Arduino zwrócone były w stronę umożliwiającą podłączenie SMARDZa bez konieczności użycia dodatkowych kabli: ...a następnie przystąpić do programowania i monitorowania naszego robota (robot na zdjęciu jest wersją próbną w trakcie powstawania, zrzut ekranu monitora pochodzi z tego właśnie robota). Wszystkie pliki potrzebne do odtworzenia SMARDZa w załączniku: smardz.zip Jak zwykle w przypadku pytań pozostaję do dyspozycji.
  47. 6 punktów
    Jak by to kogoś interesowało, to wszystkie części dotarły, złożyłem i działa. Teraz uczę się na tym pisać. Gerbery, BOM i firmware autor udostępnił tutaj: https://github.com/larrbo/odd-rocket/tree/master/5plit -kailh Domyślny układ jest taki: (Swój trochę zmieniłem, żeby wyłączyć auto-shift i mieć prawy alt do polskich literek.)
  48. 6 punktów
    To tak w przerwie od elektroniki coś z "mechaniki", albo z zimnego kowalstwa jak kto woli sąsiedzi zza wschodniej granicy mają fantazję, więc czemu trochę nie zgapić: Jak na prezent bardzo tanie, 10zł na materiały lub nic jak ktoś ma miedziany złom np z odzysku instalacji CO. Palnik, szczypce, młotek kowadło i kwiatek zrobiony
  49. 6 punktów
    1. Wprowadzenie Pewnego dnia zapragnąłem stać się właścicielem lampki nocnej RGB, którą mógłbym sterować smartfonem przy wykorzystaniu przeglądarki internetowej. Z możliwością sterowania w przyszłości takimi systemami, jak Domoticz, czy też asystentem takim jak Google Home Mini, czy Alexa Amazon. Nie potrzebuje dużej mocy, aby na przykład móc czytać przy jej świetle literaturę piękną, a raczej preferuję podejście proekologiczne w zużyciu energii. Lampka ma pełnić funkcję głównie dekoracyjną tworząc przyjemny nastrój w pomieszczeniu. Przeglądając ofertę dostępnych na rynku rozwiązań doszedłem do wniosku, że ceny tego typu urządzeń są w moim mniemaniu bardzo wysokie, więc postanowiłem zbudować lampkę własnoręcznie. Moją uwagę przykuł jeden z modeli marki Xiaomi, a mianowicie Mi Bedside Lamp (cena to około 250 pln). Na tym projekcie będę bazował podczas budowy. Całość planuję wykonać z materiałów ekologicznych, oczywiście w miarę moich skromnych możliwości. 1.1. Lampka nocna Mi Bedside Lamp Silver. 1.2. Lampka nocna Mi Bedside Lamp Silver – schemat budowy wewnętrznej. 1.3. Lampka nocna Mi Bedside Lamp Silver – prezentacja wyglądu podświetlenia w różnych kolorach. 2. WS2812 Według producenta WS2812 jest on inteligentnym sterownikiem LED z wbudowanym źródłem światła. Bliższe rzeczywistości jest jednak stwierdzenie, że układ ten składa się zazwyczaj z diody RGB 5050 z wbudowanym w nią sterownikiem WS2811. Należy zaznaczyć, że rozwiązanie to produkowane jest wielu typach obudów. Ogromną zaletą takiego rozwiązania w przeciwieństwie do konwencjonalnych diod RGB jest możliwość szeregowego łączenia i sterowania niemalże dowolną liczbą diod za pomocą pojedynczego przewodu sygnałowego z cyfrowego pinu mikrokontrolera. 2.1. Schemat szeregowego połączenia WS2812. Na rynku dostępne jest wiele wersji WS2812 w różnych obudowach. Dość popularną wersją, która została wykorzystana tym w projekcie jest wersja WS2812B w obudowie 5050. Wersja WS2812 posiada sześć nóżek, natomiast wersja z dopiskiem B tylko cztery. Największą różnicą między modelami bez dopisku B i z dopiskiem jest zabezpieczenie przed odwrotnym podłączeniem zasilania diody dla wersji B, co w przypadku standardowej wersji skutkuje zniszczeniem elementu. 2.2. Pojedyncza dioda WS2812 w wersji obudowy 5050. 2.3. Pojedyncza dioda WS2812B w wersji obudowy 5050. 2.4. Schemat szeregowego łączenia WS2812B. Podczas pracy z WS2812 niezależnie od wersji należy pamiętać, aby: Nie przekraczać napięcia zasilania powyżej 5V, ponieważ uszkodzimy układ. Dodać kondensator elektrolityczny o pojemności od 100µF do 1000µF (na 6.3V lub wyższy) przy zasilaniu pierwszej diody. Dodać rezystor o wartości od 300Ω do 1kΩ pomiędzy mikrokontrolerem, a pierwszym pinem DIN i aby umieść go jak najbliżej diody. W miarę możliwości jak najbardziej skrócić odległość pomiędzy mikrokontrolerem, a pierwszą diodą. Nie podłączać diod przy włączonym zasilaniu. Jeśli nie mamy innego wyboru, to należy zachować kolejność: masa, zasilanie, linia sterująca, a odłączamy w odwrotnej kolejności. Jeżeli diody zasilane są z oddzielnego źródła zasilania, to najpierw należy doprowadzić zasilanie do diod, a następnie do mikrokontrolera. 3. Projekt i narzędzia Główną część lampy stanowi szklany słoik, wewnątrz są warstwy rozpraszające światło w postaci arkusza kalki technicznej i białego papieru. Całość została osadzona w kartonowej podstawie. Całość będzie składała się z: Szklany słoik, najlepiej wypiaskowany aby dobrze rozpraszał światło. Ja użyłem słoika Ikea Dropper, ale nada się każdy inny podobny. Ten, którego użyłem nie jest idealny, ponieważ nie jest w całości wypiaskowany. Kalka techniczna do rozpraszania światła. Pomaga uzyskać lepsze rozproszenie, zwłaszcza gdy piaskowanie nie jest całkowite. Kartka białego papieru A4. Również pomocna przy rozpraszaniu, warto wąski pasek umieścić przy tuż przy diodach. Karton, tektura. Do do budowy podstawy, jeśli nie umiemy sobie takiej podstawy wyrzeźbić na przykład z drewna lub nie mamy drukarki 3D. Klej biurowy, przeźroczysta taśma klejąca. Ładowarka z wyjściem USB 5V 1A+. Taka, jakiej używa się się do ładowania większości smartfonów. Kabel USB – micro USB o długości dostosowanej do własnych potrzeb. W projekcie zastosowałem taki o długość 100 cm. Kondensator elektrolityczny w przedziale od 100µF do 1000µF na 6.3V. W projekcie został użyty 1000µF 6.3V. Rezystor o wartości od 300Ω do 1kΩ. W projekcie został użyty 470Ω ¼W. Gniazdo wejściowe microUSB do zasilania układu. Z racji braku gniazda użyłem moduły TP4056, co daje dodatkową możliwość zastosowania w przyszłości zasilania bateryjnego z ogniw 18650. Koło / pierścień diod WS2812B + moduł ESP8266 (ESP-01 / ESP-01S) + łącznik ESP-01S/ESP-01 RGB LED. W tym konkretnym projekcie zostało użyte koło z 16 diodami. Lutownica lub stacja lutownicza, stop lutowniczy i topnik. Przewody, kilka cm. Cążki do metalu. Śrubokręt, nożyczki, czy inne narzędzia potrzebne do obróbki wybranego materiału. 3.1. Zestaw składający się z WS2812B, ESP-01 i łącznika. 4. Schemat połączeń Jeśli korzystamy z gotowego zestawu, to schemat połączeń jest bardzo prosty. 4.1. Schemat połączeń wszystkich podzespołów. 5. Kod i programowanie #include <adafruit_neopixel.h> #include <esp8266wifi.h> const char* ssid = "<nazwa_sieci_wifi>"; const char* password = "<haslo_do_sieci_wifi>"; Adafruit_NeoPixel strip = Adafruit_NeoPixel(16, 2, NEO_GRB + NEO_KHZ800); WiFiServer server(80); long aniMillis = 0; byte aniFlag = 0; void setup() { Serial.begin(115200); delay(100); strip.begin(); for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, 0); strip.show(); } IPAddress ip(192, 168, 0, 100); IPAddress gateway(192, 168, 0, 1); IPAddress subnet(255, 255, 255, 0); WiFi.config(ip, gateway, subnet); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } server.begin(); } void loop() { while (WiFi.status() == WL_CONNECTED) { WiFiClient client = server.available(); if (!client) { return; } while (!client.available()) { delay(10); } String request = client.readStringUntil('\r'); client.flush(); if (request.indexOf("/off") > 0) { for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, 0); strip.show(); } } // jasnosc if (request.indexOf("/b0") > 0) { for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setBrightness(30); strip.show(); } } if (request.indexOf("/b1") > 0) { for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setBrightness(128); strip.show(); } } if (request.indexOf("/b2") > 0) { for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setBrightness(255); strip.show(); } } // jasnosc end // kolory // bialy if (request.indexOf("/c0") > 0) { for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, 255, 255, 255); strip.show(); } } // zoly if (request.indexOf("/c1") > 0) { for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, 255, 255, 0); strip.show(); } } // pomaranczowy if (request.indexOf("/c2") > 0) { for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, 255, 127, 0); strip.show(); } } // czerwony if (request.indexOf("/c3") > 0) { for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, 255, 0, 0); strip.show(); } } // rozany if (request.indexOf("/c4") > 0) { for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, 255, 0, 127); strip.show(); } } // magenta if (request.indexOf("/c5") > 0) { for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, 255, 0, 255); strip.show(); } } // fioletowy if (request.indexOf("/c6") > 0) { for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, 127, 0, 255); strip.show(); } } // niebieski if (request.indexOf("/c7") > 0) { for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, 0, 0, 255); strip.show(); } } // blekitny if (request.indexOf("/c8") > 0) { for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, 0, 127, 255); strip.show(); } } // cyjan if (request.indexOf("/c9") > 0) { for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, 0, 255, 255); strip.show(); } } // wiosenna zielen if (request.indexOf("/c10") > 0) { for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, 0, 255, 127); strip.show(); } } // zielony if (request.indexOf("/c11") > 0) { for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, 0, 255, 0); strip.show(); } } // zielen chartreuse if (request.indexOf("/c12") > 0) { for (uint16_t i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, 127, 255, 0); strip.show(); } } // kolory end client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println(""); client.println(""); client.println(""); client.println(""); client.println("<title>Lampka</title>"); client.println("<meta charset="\"utf-8\"">"); client.println("<meta name="\"apple-mobile-web-app-capable\"" content="\"yes\"">"); client.println("<meta name="\"apple-mobile-web-app-status-bar-style\"" content="\"black\"">"); client.println("<meta name="\"robots\"" content="\"none\"">"); client.println("<meta name="\"viewport\"" content="\"width=device-width," initial-scale="1\"">"); client.println("<style>"); client.println("body {background: #313236; text-align: center; padding: 0; margin: 50px 0;}"); client.println("button {border: 0; font: bold 12px Verdana, sans-serif; color: #000000; width: 85px; height: 85px; border-radius: 50%;}"); client.println(".b0 {background: #000000; color: #ffffff;}"); client.println(".b1 {background: #949494;}"); client.println(".b2 {background: #bdbebd;}"); client.println(".b3 {background: #e9e9e9;}"); client.println(".c0 {background: rgb(255, 255, 255); margin-right: 266px;}"); // bialy client.println(".c1 {background: rgb(255, 255, 0);}"); // zolty client.println(".c2 {background: rgb(255, 127, 0);}"); // pomaranczowy client.println(".c3 {background: rgb(255, 0, 0);}"); //czerwony client.println(".c4 {background: rgb(255, 0, 127);}"); // rozany client.println(".c5 {background: rgb(255, 0, 255);}"); // magenta client.println(".c6 {background: rgb(127, 0, 255);}"); // fioletowy client.println(".c7 {background: rgb(0, 0, 255);}"); // niebieski client.println(".c8 {background: rgb(0, 127, 255);}"); // blekitny client.println(".c9 {background: rgb(0, 255, 255);}"); // cyjan client.println(".c10 {background: rgb(0, 255, 127);}"); // wiosenna zielen client.println(".c11 {background: rgb(0, 255, 0);}"); // zielony client.println(".c12 {background: rgb(127, 255, 0);}"); // zielen chartreuse client.println(".mgb4 {margin-bottom: 4px;}"); client.println(".mgb50 {margin-bottom: 50px;}"); client.println("</style>"); client.println(""); client.println(""); client.println("<a href="\"/b0\""><button class="\"b1" mgb50\"="">MIN</button></a>"); client.println("<a href="\"/b1\""><button class="\"b2" mgb50\"="">50%</button></a>"); client.println("<a href="\"/b2\""><button class="\"b3" mgb50\"="">MAX</button></a>"); client.println("<a href="\"/off\""><button class="\"b0" mgb50\"="">OFF</button></a><br>"); client.println("<a href="\"/c0\""><button class="\"c0" mgb4\"=""></button></a><br>"); client.println("<a href="\"/c1\""><button class="\"c1" mgb4\"=""></button></a>"); client.println("<a href="\"/c2\""><button class="\"c2" mgb4\"=""></button></a>"); client.println("<a href="\"/c3\""><button class="\"c3" mgb4\"=""></button></a>"); client.println("<a href="\"/c4\""><button class="\"c4" mgb4\"=""></button></a><br>"); client.println("<a href="\"/c5\""><button class="\"c5" mgb4\"=""></button></a>"); client.println("<a href="\"/c6\""><button class="\"c6" mgb4\"=""></button></a>"); client.println("<a href="\"/c7\""><button class="\"c7" mgb4\"=""></button></a>"); client.println("<a href="\"/c8\""><button class="\"c8" mgb4\"=""></button></a><br>"); client.println("<a href="\"/c9\""><button class="\"c9\""></button></a>"); client.println("<a href="\"/c10\""><button class="\"c10\""></button></a>"); client.println("<a href="\"/c11\""><button class="\"c11\""></button></a>"); client.println("<a href="\"/c12\""><button class="\"c12\""></button></a>"); client.println(""); client.println(""); delay(10); } setup(); } 6. Efekt końcowy Efekt końcowy prezentuje się tak, jak to widać na poniższych zdjęciach. Należy zaznaczyć, że w rzeczywistości podświetlenie jest bardziej jednolite, niż widać to na zdjęciach. Prawdopodobnie ekspozycja podczas robienia zdjęcia byłą zbyt długa, ale ciężko zrobić w inny sposób zdjęcia w nocy bez dobrego sprzętu. Obsługa lampki odbywa się przez interfejs sieciowy pod adresem 192.168.0.100. 6.1. Interfejs sieciowy pod adresem 192.168.0.100. 6.2. Wnętrze podstawy lampy. 6.3. Warstwy rozpraszające światło bez i z koszem. 6.4. Wybrane kolory.
  50. 6 punktów
    Testowo uruchomiona została opcja ignorowania poszczególnych użytkowników W prawym górnym rogu klikamy na swój nick i z menu wybieramy "Ignorowani użytkownicy": Następnie konfigurujemy kogo chcemy blokować i w jaki sposób. Dla przykładu zablokowałem sobie @ethanak (bez urazy, to tylko test ). Od tej pory w widoku tematach posty @ethanak wyglądają dla mnie tak: Dzięki temu nie widzę treści posta, ale wiem, że tam jest. W każdej chwili za pomocą linku Opcje mogę podejrzeć konkretną wiadomość od zignorowanego użytkownika lub go odblokować. Zachęcam do testów. Nie obiecuję, że funkcja ta zostanie na forum na zawsze, ale teraz działa, więc można korzystać PS Jest jednak pewien wyjątek - nie można zablokować moderatorów, mnie oraz treści publikowanych automatycznie.
Tablica liderów jest ustawiona na Warszawa/GMT+02:00
×
×
  • Utwórz nowe...