Skocz do zawartości

Tablica liderów


Popularna zawartość

Pokazuje zawartość z najwyższą reputacją od 31.03.2019 w Posty

  1. 15 punktów
    Ten wpis bierze udział w konkursie na najlepszy artykuł o elektronice lub programowaniu, którego partnerem jest firma PCBWay (popularny producent prototypów PCB). W puli nagród karty podarunkowe Allegro o wartości 2300 zł. Sprawdź więcej informacji na temat konkursu » 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. 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
    Ten wpis bierze udział w konkursie na najlepszy artykuł o elektronice lub programowaniu, którego partnerem jest firma PCBWay (popularny producent prototypów PCB). W puli nagród karty podarunkowe Allegro o wartości 2300 zł. Sprawdź więcej informacji na temat konkursu » 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 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. 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 Ten wpis bierze udział w konkursie na najlepszy artykuł o elektronice lub programowaniu, którego partnerem jest firma PCBWay (popularny producent prototypów PCB). W puli nagród karty podarunkowe Allegro o wartości 2300 zł. Sprawdź więcej informacji na temat konkursu »
  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. 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 Ten wpis bierze udział w konkursie na najlepszy artykuł o elektronice lub programowaniu, którego partnerem jest firma PCBWay (popularny producent prototypów PCB). W puli nagród karty podarunkowe Allegro o wartości 2300 zł. Sprawdź więcej informacji na temat konkursu »
  7. 11 punktów
    Już słyszę głosy: O, staremu ethanakowi coś się chyba pokręciło... Post jest o jakimś Kedrigernie w dziale DIY - a na zdjęciu mamy coś przypominającego robota z dumnym napisem "Ciapek"... Spieszę z wyjaśnieniami. To nie pomyłka. Post jest na temat serwera mowy Kedrigern, a Ciapek to żaden robot. Po prostu dość trudno zrobić zdjęcie programu, poza tym wypadałoby raczej pokazać ów program w działaniu - a ten wymaga jednak czegoś co będzie gadać (czyli jakiejś bardziej fikuśnej obudowy na głośnik). A ponieważ ostatnio zapatrzyłem się w rysunki Daniela Mroza... cóż, Ciapek z ludzkimi dłońmi, stopami i uszkami wyszedł tak jak wyszedł Zacznę możę od opisu elektroniki (jako że jest to najprostsze, a użytkownik może sobie ją skomponować z zupełnie innych elementów). Sercem całego układu jest Raspberry Pi. Można zastosować dowolny model (chociaż ze starymi, krótkimi GPIO mogą być lekkie problemy), ja użyłem w swoim egzemplarzu Raspberry Pi Zero W. Oprócz tego potzebny jest jakikolwiek układ odtwarzania dźwięku. "Duże" (pełnpowymiarowe) malinki jak popularny 3B+ mają już wyprowadzone wyjście audio, potrzebny jest tylko jakiś niewielki wzmacniacz. Dla wersji Zero potrzebny jest jednak układ zewnętrzny. Prawdopodobnie doskonale spisałby się opisywany ostatnio moduł z wyjściem audio do Raspberry Pi Zero, ja zastosowałem jednak moduł i2s. Jest on dość wygodny w użyciu jako że zawiera w sobie wzmacniacz mono o całkiem sporej (do 3 W) mocy, poza tym początkujący elektronicy nie muszą się martwić o jakieś dziwne masy audio, filtrowane zasilania i tym podobne dziwactwa interesujące tylko "analogowców" - moduł podłącza się pięcioma przewodami do malinki, z drugiej strony dwoma do głośnika i już gra Ponieważ nasz "robocik" musi potrafić poruszać ustami, zastosowałem najprostsze (i najprawdopodobniej najtańsze) rozwiązanie - czyli matrycę LED 8x8 ze sterownikiem, na której rysowane będą kształty ust. Od razu uprzedzę pytanie: owszem, próbowałem zrobić bardziej skomplikowany mechanizm używający serw. Niestety - oprócz trudności mechanicznych (te są do przezwyciężenia) natrafiłem na rzecz, której nie da się przeskoczyć: prędkość serwa. Typowe serwo potrzebuje 0.1 sekundy na przekręcenie orczyka o 60° - a nawet zakładając, że owe 60° wystarczy (w co osobiście wątpię), jest to co najmniej dwa razy za wolno (przy czym owe "dwa" mogłoby się w rzeczywistych układach rozrosnąć do trzech czy czterech). Będę jeszcze próbować rozwiązania z solenoidami - jeśli mi się uda to opublikuję wyniki. Ale może w międzyczasie ktoś inny napisze moduł "solenoid"? Zresztą - chciałem, aby każdy mógł sobie w domu wypróbować Kedrigerna ponosząc jak najmniejsze koszty, a opisywany układ można (mając RPi z wyjściem audio) zmontować kosztem klikunastu złotych (matryca) bez żadnych płytek - po prostu łącząc matrycę przewodami z GPIO malinki. Jako głośnika użyłem leżącego gdzieś w szufladzie zakurzonego głośniczka od starego telefonu z sekretarką, pasować jednak będzie dowolny pod warunkiem dopasowania mocy głośnika do posiadanego wzmacniacza. I to cała wielce skomplikowana elektronika. Jak widać na zdjęciu - nie ma tam nic skomplikowanego. Przejdę więc do opisu programu. Kilka lat temu udało mi się zmusić Mbrolę do w miarę prawidłowego gadania po polsku (do tego stopnia, że można ją było wykorzystać np. do tworzenia audiobooków). System TTS Milena (tak się nazywa ten "zmuszacz" do Mbroli - czyli bardziej fachowo NLP) bardzo dobrze sprawdził się na pecetowym Linuksie, wersja na Windows była raczej ciekawostką ale również działała - postanowiłem więc przystosować ją do malinki. Po przezwyciężeniu pewnych trudności z kompilacją (np. "char" dla architerktury Intel to w GCC domyślnie signed, w ARM z jakichś przyczyn unsigned) okazało się, że co prawda Milena działa, ale "rozruch" ma straszliwie powolny. Nic dziwnego - pliki tekstowe słowników wymowy i translacji fonetycznej muszą być kompilowane przy załadowaniu programu, a malinka ze swoją wolniutką kartą pamięci i nieszczególnie silnym procesorkiem potrzebuje zbyt dużo czasu, zanim wydobędzie z siebie jakieś zdanie. Postanowiłem więc zrobić inaczej: serwer wczytuje wszystkie pliki raz przy starcie systemu, a prosty program klienta przekazuje mu tylko treść komunikatów. Takie rozwiązanie ma również inne zalety: uruchomiony na sockecie TCP serwer może być na zupełnie innej fizycznej maszynie niż klient. I w ten sposób powstał program Kedrigern (nazwany na cześć pewnego czarodzieja, który postanowił odczarować księżniczkę z zaklęcia odbierającego głos). Jak mi to wyszło - oceńcie sami. Oto filmik ukazujący Kedrigerna w działaniu: Nie będę tu rozpisywał się o wszystkich zasadach działania i możliwościach Kedrigerna i Mileny (to w końcu ma byc post na forum a nie książka z dokumentacją), zacznę więc od instalacji Kedrigerna na malince. Wszystkie konieczne komponenty (z wyjątkiem głosu pl1) są w załączonym pliku tgz. Rozpakujmy go w dowolnym katalogu (np. w głównym katalogu użytkownika malinki) i instalujemy mbrolę wraz z polskim głosem: sudo dpkg -i mbrola3.0.1h_armhf.deb sudo apt install mbrola-pl1 Teraz możemy zainstalować Milenę. I tu uwaga: jeśli ktoś miał starszą wersję Mileny niż 0.2.92 musi ją przeinstalować na tę właśnie wersję - inaczej nie będzie działać moduł ruchu ust Kedrigerna! sudo dpkg -i milena*.deb Po zainstalowaniu wszystkiego powinno działać już polecenie milena_say, czyli musimy wypróbować: milena_say dzień dobry kolego I znów uwaga: jeśli wyjściem audio jest HDMI, polecenie może nie działać prawidłowo! Należy spróbować dodać parametr -d opóżniający generację mowy do czasu "załapania" HDMI, czyli najprościej: milena_say -d 2000 dzień dobry kolego Niestety, przy użyciu wyjścia HDMI mogą pojawić się problemy z późniejszym działaniem Kedrigerna, ale po pierwsze nie jest to miejsce na omawianie problemów z audio, po drugie wcale nie czuję się mistrzem w tym zakresie i prawdopodobnie ktoś tu wie lepiej ode mnie jak tym problemom zaradzić. W każdym razie mając uruchomiona Milenę i Mbrolę możemy przystąpić do instalacji Kedrigerna. Zaczynamy od instalacji serwera, czyli sudo dpkg -i kedrigern_0.2.0-1_armhf.deb Jeśli mamy podłąćzoną matrycę LED (8x8, MAX7219) możemy użyć jej jako wyświetlacz ust: sudo dpkg -i kedrigern-matrix_0.2.0-1_armhf.deb No i oczywiście coś co pozwoli nam korzystać z serwera, czyli: sudo dpkg -i libkedrigern_0.1.2-1_armhf.deb Teraz możemy sprawdzić, co potrafi nasz serwer: kedrigern -h lub (jeśli mamy zainstalowany moduł matrix) kedrigern -M matrix -h W odpowiedzi dostaniemy wykaz opcji, z którymi możemy uruchomić serwer. Aby je przetestować, należy otworzyć drugi terminal; w pierwszym uruchamiamy serwer poleceniem "kedrigern" z różnymi opcjami, w drugim testujemy poleceniem kdr-say. Po ustaleniu opcji należy zedytować plik /etc/default/kedrigern i w nim ustawić domyślne parametry. Po przetestowaniu poleceniem kedrigern -C /etc/default/kedrigern możemy już uruchomić nasz serwer w tle: sudo systemctl start kedrigern Jeśli chcemy, aby serwer startował od razu przy starcie systemu, należy wydać polecenie: sudo systemctl enable kedrigern Do komunikacji z serwerem służą polecenia kdr-say, kdr-stop i kdr-speaking. Moduł matrix pozwala na wyświetlanie ust zsynchronizowane z głosem syntezatora. Oto przykładowe obrazy ust dla różnych fonemów: Fonem A Fonem I Fonem O Fonem U Fonemy M, P i B Wybrałem do pokazania kilka najbardziej charakterystycznych kształtów. Jeśli komuś nie odpowiadają stworzone przeze mnie kształty może w prosty sposób dorobić własne lub poprawić moje na bazie pliku /usr/share/kedrigern/demo.shape i podłączyć go do Kedrigerna, np. za pomocą opcji "-m matrix:shape" lub wprowadzając odpowiednie zmiany w pliku konfiguracyjnym. Protokół komunikacyjny jest bardzo prosty. Nie wnikając w szczegóły - załączony moduł w Pythonie (działa w wersji 2 i 3 Pythona) pozwala na sterowanie serwerem, a jednocześnie stanowi przykład sposobu komunikacji. I to wszystko... A nie, nie wszystko. Bo zaraz ktoś powie że to tylko zabawka, że komu potrzebne gadające roboty... Przede wszystkim: Kedrigern może generować komunikaty diagnostyczne w czasie testowania/programowania robota. Jakie to ma znacze nie nie muszę chyba mówić nikomu, kto choć raz ustawiał np. regulatory silników czy zakres ruchu serw w warunkach polowych. Poza robotyką może być bardzo dobrym rozwiązaniem do urządzeń typu headless - ja np. stosuję podobne (poprzednika Kedrigerna) rozwiązanie do obsługi sterownika pieca CO (podanie np. godzin nieobecności w mieszkaniu) czy jako wygodnego interfejsu do wpisywania wyników pomiaru ciśnienia krwi i poziomu cukru z małej przenośnej klawiaturki. A to już nie są zabawki A w ogóle przypominam, że Roomba też gada (tyle że Ciapek ładniej) Zdaję sobie sprawę z tego, że opis jest bardzo pobieżny. Z chęcią odpowiem na wszystkie pytania - o ile kogoś to będzie interesować... W każdym razie wypróbowanie Kedrigerna nawet bez modułu ust posiadacza RPi z wyjściem audio nic nie kosztuje, a może się przyda? Kody źródłowe Mileny i Kedrigerna są dostępne na stronie http://milena.polip.com A, i ostatnie wyjaśnienie: Ciapek to mały troll, wychowanek czarodzieja Kedrigerna w książce Johna Morressy'ego "Głos dla księżniczki". Nawet trochę podobny Przy okazji: @Treker, czemu pliki zip są cacy a tgz są be? kedrigern.zip pykedrigern.zip
  8. 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"
  9. 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...
  10. 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®
  11. 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ń.
  12. 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.
  13. 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
  14. 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
  15. 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.
  16. 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.
  17. 8 punktów
    Ten wpis bierze udział w konkursie na najlepszy artykuł o elektronice lub programowaniu, którego partnerem jest firma PCBWay (popularny producent prototypów PCB). W puli nagród karty podarunkowe Allegro o wartości 2300 zł. Sprawdź więcej informacji na temat konkursu » 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ę. 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
    Ten wpis bierze udział w konkursie na najlepszy artykuł o elektronice lub programowaniu, którego partnerem jest firma PCBWay (popularny producent prototypów PCB). W puli nagród karty podarunkowe Allegro o wartości 2300 zł. Sprawdź więcej informacji na temat konkursu » 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. 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. 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 Ten wpis bierze udział w konkursie na najlepszy artykuł o elektronice lub programowaniu, którego partnerem jest firma PCBWay (popularny producent prototypów PCB). W puli nagród karty podarunkowe Allegro o wartości 2300 zł. Sprawdź więcej informacji na temat konkursu »
  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
    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
  28. 7 punktów
    Ten wpis bierze udział w konkursie na najlepszy artykuł o elektronice lub programowaniu, którego partnerem jest firma PCBWay (popularny producent prototypów PCB). W puli nagród karty podarunkowe Allegro o wartości 2300 zł. Sprawdź więcej informacji na temat konkursu » 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. 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ć). 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 Ten wpis bierze udział w konkursie na najlepszy artykuł o elektronice lub programowaniu, którego partnerem jest firma PCBWay (popularny producent prototypów PCB). W puli nagród karty podarunkowe Allegro o wartości 2300 zł. Sprawdź więcej informacji na temat konkursu »
  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 chciałem wam przedstawić wykonaną przeze mnie kierownicę do komputera PC. Kierownica nie jest jeszcze do końca wykonana tak jak bym chciał , ale jest skończona działa można ją będzie jeszcze rozbudować Budowa od strony elektronicznej: Do budowy kierownicy użyłem jako podstawę Arduino Leonardo. Opierałem się o podobny projekt który znalazłem na yt . Lecz ja zrobiłem to trochę ładniej niż tamten chociaż nie ma rewelacji mogło być lepiej , ale w przyszłości myślę zrobić jeszcze jedną lepszą Oprócz Arduino Leonardo , zamiast jakiś potencjometrów i kombinowania z przekładniami użyłem enkoder z drukarki Canon IP2700. Ogólnie to oprócz enkodera to jeszcze parę przycisków potencjometry do pedałów i jakieś diody na podświetlenie. Poniżej schemat jak to podłączone. Oprogramowanie gotowe znalazłem pod tym filmem z którego się wzorowałem, wystarczyło wgrać XLoaderem i to wszystko podłączyć jak na schemacie. Od strony mechanicznej wygląda to tak: Odbijanie i blokada zostały zrobione na sprężynie niestety nie mam zdjęć . Dodam link do filmu na którym jest pokazane jak to działa. Ręczny przerobiony ze starej kierownicy . Elementy skrzyni biegów wydrukował mi kolega w 3D. i wygląda to tak: Pedały ze starej kierownicy , dorobiony jeden sprzęgłowy z innej kierownicy: Ostatecznie wygląda to tak: Link do filmu jak to działa i jak zrobione niektóre rzeczy: To by było na tyle w planach zrobienie Force Feed Back , na silniku DC 12v i przekładniach Pozdrawiam Krzysiek
  41. 6 punktów
    Witajcie w drugiej części kursu ESP8266! Dzisiaj omówimy sobie, co przynosi zmiana środowiska na VS Code, zobaczymy sprzętowe możliwości ESP, a także połączymy się z siecią WIFI. Do kursu będziecie potrzebowali: ESP8266 (najlepiej NodeMCU v3) Dioda LED (z rezystorem) Wyświetlacz HD44780 z konwerterem I2C (lub inny, ale będziesz musiał modyfikować kod) Fotorezystor lub potencjometr Płytkę stykową Przewody połączeniowe oraz oczywiście dobre chęci Zacznijmy od tego, co nam dała przeprowadzka na VS Code. Poza samym wyglądem (kwestia gustu), wsparciem wielu wtyczek (pluginów) czy po prostu większą wygodą pisania, VS Code oferuje wiele zwykłych, aczkolwiek przydatnych funkcji których brakuje w Arduino IDE. 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 Praca na wielu plikach jednocześnie Nawet z jednym monitorem, wszystko wygląda dość czytelnie i elegancko. Po lewej stronie mamy przeglądarkę plików, gdzie możemy wczytać przykład z Arduino IDE czy otworzyć cały folder z projektem. Dzięki temu, będziemy mieli od razu łatwy dostęp do wszystkich plików projektu, jak pliki nagłówkowe, pliki tekstowe, czy nawet zdjęcia. Na dole zaś znajdziemy konsolę. Tam mamy dostęp do windowsowego PowerShella, arduinowego Serial Monitora, czy po prostu informacje o wgrywaniu/kompilowaniu programu. Na samym środku okna znajdziemy zakładki z otwartymi plikami. Możemy je jeszcze podzielić, co umożliwia nam pracę na wielu plikach jednocześnie. Typowe okno programu VS Code. Wygoda pisania VS Code o wiele bardziej potrafi się do nas przystosować. Poza zmianą ogólnego schematu kolorów, mamy możliwość zmiany kolorowania składni. Dostajemy także podpowiadanie (skrót CTRL+SPACJA), co często jest bardzo przydatną funkcją, oraz autouzupełnianie - czyli po zapisaniu pierwszego nawiasu, od razu piszemy drugi (magia IntelliSense). Po prawej stronie, obok suwaka, mamy widok na kod "z daleka". Umożliwia to wyszukiwanie konkretnych bloków kodu, np. obszernych tablic zmiennych, długich instrukcji switch-case czy innych dobrze widocznych rzeczy. Podpowiedź do własnej funkcji blink() VS Code oferuje oczywiście jeszcze wiele, wiele funkcji - lecz są one dla bardziej zaawansowanych programistów, i najzwyczajniej nam się nie przydadzą. Przejdźmy zatem do części praktycznej. Analog Read oraz Serial Sprawdźmy, czy ESP może pracować tak jak zwykłe Arduino. Ale czym by było Arduino bez przetwornika ADC i komunikacji z komputerem? Sprawdźmy, czy przykład "AnalogReadSerial" zadziała na NodeMCU. Wgrywamy poniższy kod: #define PIN_ANALOG A0 //pin analogowy. NodeMCU ma taki tylko jeden! int odczyt; //zmienna, w której zapisujemy odczyt void setup() { Serial.begin(115200); //uruchamiamy komunikację UART z prędkością 115200 bodów pinMode(PIN_ANALOG, INPUT); //ustawiamy pin analogowy jako wejście } void loop() { odczyt = analogRead(PIN_ANALOG); //odczytujemy wartość Serial.println(odczyt); //piszemy wartość delay(1); //czekamy chwilę } Oraz podłączamy NodeMCU zgodnie z poniższym schematem: Gdzie: pin pierwszy fotorezystora łączymy z pinem A0 pin drugi fotorezystora łączymy z zasilaniem 3.3V Po wgraniu przykładu i otworzeniu portu, powinniśmy zobaczyć ciągle wypisywane wartości. U mnie, przy zwykłym oświetleniu odczyt wskazywał 1024 (czyli maks. wartość), po przysłonięciu ręką ta wartość malała aż do 0 (kiedy fotorezystor włożyłem do pudełka). Oznacza to, że zarówno mierzenie wartości analogowych, jak i komunikacja przez port szeregowy działa. Przejdźmy zatem do następnego przykładu. Komunikacja w drugą stronę, przycisk, PWM Kolejny, prosty przykład. Chcemy, aby po odebraniu z komputera po Serialu liczby dioda zaświeciła się dzięki PWM, a po naciśnięciu przycisku zgasła. W tym celu utworzymy nowy projekt. Ja to robię trochę manualne, mianowicie tworzę nowy folder, a w nim "plik.ino". Aby taki plik utworzyć, po nazwie dajemy ".ino" - więc nadajemy rozszerzenie pliku Arduino. Musimy także wybrać typ pliku jako wszystkie pliki. Nazwa folderu i pliku są takie same. Teraz możemy przejść do kodu. #define PIN_BTN D1 #define PIN_LED BUILTIN_LED char znak = ' '; void setup() { Serial.begin(115200); pinMode(PIN_BTN, INPUT_PULLUP); } void loop() { if(Serial.available() > 0) znak = Serial.read(); //jak odbieramy coś, zapisujemy 1 znak switch (znak) { case '1': analogWrite(PIN_LED, 0); //maks break; case '2': analogWrite(PIN_LED, 400); //ok. 80% break; case '3': analogWrite(PIN_LED, 800); //ok. 30% break; case '4': analogWrite(PIN_LED, 1023); //0% break; } if(digitalRead(PIN_BTN) == LOW) znak = '4'; } Na początku definiujemy piny. W ESP używamy oznaczeń "Dx" - jeżeli chcielibyśmy podać samą liczbę, musielibyśmy podać numer GPIO zgodny z rozpiską. Następnie definiujemy zmienną "znak" typu char. Przechowuje ona jeden (!) znak. W funkcji setup uruchamiamy komunikację UART, oraz podpinamy rezystor podciągający do pinu przycisku. Zapobiegnie to "pływaniu" napięcia - nie wiadomo, jakie napięcie by wtedy było na tym pinie. W funkcji loop() na początku sprawdzamy, czy odebraliśmy dane - jeżeli tak, zapisujemy je do zmiennej. Następnie analizujemy zmienną - oraz ustawiamy PWM na pin z daną wartością. Tutaj warto zaznaczyć, że wartość podajemy w zakresie 0-1023 (w Arduino 0-255), oraz że wypełnienie też jest "odwrócone". Czyli "maksymalne wypełnienie" zgasi diodę, natomiast podanie "zera" po prostu ją zapali. Następnie, jeżeli przycisk jest zwarty z masą, gasimy diodę. Tak wygląda schemat: Pin D1 łączymy z pierwszym pinem przycisku, drugi pin łączymy z masą (G). Teraz spróbuj połączyć się Serial Monitor i wpisać kolejno 1,2,3,4, oraz przycisnąć przycisk gaszący diodę. Wszystko powinno sprawnie działać. Pierwsze kroki z WIFI - łączenie się z siecią Czas, aby nasze ESP mogło się połączyć z Wifi. Oczywiście, możemy także ustawić ESP w trybie "routera", lecz zajmiemy się tym później - najlepiej, gdybyś miał gotową domową sieć Wifi do której bezpiecznie można się podpiąć. Nawet jeżeli takiej nie masz, możesz uruchomić w telefonie "hotspota". Jeżeli zapewniłeś sobie już dostęp do Wifi, zapisz gdzieś SSID (nazwę) i hasło. Spróbujemy połączyć się z Wifi, a następnie wyświetlić to na wyświetlaczu sterowanym przez I2C. Tak jak mówiłem, I2C możemy skonfigurować na dowolnych pinach; domyślnie pin D1 to SCL, a D2 to SDA. Tak też zostawiamy, następnie uruchamiamy skaner adresów I2C, aby znaleźć jaki adres ma nasz wyświetlacz. Podłączenie nie powinno być problemem: I teraz prosty programik uruchamiający wyświetlacz: #include <Wire.h> //biblioteka od I2C #include <LiquidCrystal_I2C.h> //biblioteka od HD44780 po I2C LiquidCrystal_I2C lcd(0x3F, 20, 4); //adres, szerokość, wysokość void setup() { lcd.begin(); lcd.print("Hello, world!"); } void loop() {} Powinniśmy zobaczyć takie coś: Teraz spróbujemy połączyć się z Wifi. Do poprzedniego kodu dodajemy bibliotekę, odpowiadającą za obsługę Wifi. #include <ESP8266WiFi.h> Następnie tworzymy tablice charów zawierające naszą nazwę i hasło. Do tego tworzymy obiekt klienta. const char* ssid = "ssid"; const char* pass = "hasło"; WiFiClient client; Przechodzimy do funkcji setup. Tam dodajemy funkcję załączającą Wifi. Przyjmuje ona argument nazwy, a potem hasła. void setup() { lcd.begin(); WiFi.begin(ssid, pass); //połącz z Wifi lcd.setCursor(0, 0); lcd.print("Laczenie "); Potem możemy dodać animację, żeby zobaczyć czy nic się nie zacięło. while(WiFi.status() != WL_CONNECTED) //wyświetlaj animacje dopóki się nie połączyliśmy { lcd.setCursor(10,0); lcd.print("."); delay(400); lcd.setCursor(10,0); lcd.print(" "); delay(400); } Na końcu dodamy wiadomość, że już się połączyliśmy. Wyświetlimy nawet nasz adres IP! lcd.setCursor(0, 0); //napisz, że połączono i wyświetl IP lcd.print("Polaczono! "); lcd.setCursor(0, 1); lcd.print("IP: "); lcd.print(WiFi.localIP()); Całość zatem będzie wyglądać tak: #include <Wire.h> //biblioteka od I2C #include <LiquidCrystal_I2C.h> //biblioteka od HD44780 po I2C #include <ESP8266WiFi.h> LiquidCrystal_I2C lcd(0x3F, 20, 4); //adres, szerokość, wysokość const char* ssid = "nazwa"; //nazwa const char* pass = "haslo"; //hasło WiFiClient client; //obiekt wifi void setup() { lcd.begin(); WiFi.begin(ssid, pass); //połącz z Wifi lcd.setCursor(0, 0); lcd.print("Laczenie "); while(WiFi.status() != WL_CONNECTED) //wyświetlaj animacje dopóki się nie połączyliśmy { lcd.setCursor(10,0); lcd.print("."); delay(400); lcd.setCursor(10,0); lcd.print(" "); delay(400); } lcd.setCursor(0, 0); //napisz, że połączono i wyświetl IP lcd.print("Polaczono! "); lcd.setCursor(0, 1); lcd.print("IP: "); lcd.print(WiFi.localIP()); } void loop() //nie robimy już nic { } Pamiętaj, aby przed wgraniem programu wprowadzić swoje hasło i swoją nazwę sieci (a także adres LCD). Jeżeli nam się nie uda połączyć, lub coś pójdzie nie tak, zamiast adresu IP wyświetli nam się "IP unset". Spróbuj teraz w przeglądarkę wpisać adres IP, który odczytasz na wyświetlaczu. Oczywiście, teraz nic się nie wyświetli - ESP nie ma instrukcji, co zrobić w przypadku wejścia na jego adres IP. Zmieńmy to! ESP8266 jako prosty serwer WWW Nasze zadanie będzie proste: jeżeli wejdziemy na stronę główną (ip), wyświetla nam się tekst "witaj". Po wejściu w podstronę "on" (ip/on) zapalamy diodę, i podobnie w przypadku "off". Będziemy sterować wbudowaną diodą. Dodajemy zatem bibliotekę odpowiedzialną za serwer WWW: #include <ESP8266WebServer.h> Oraz jej obiekt. Liczba podana w nawiasie to port. Jeżeli chcesz wiedzieć więcej o HTTP i serwerach, odsyłam Ciebie do artykułu ethanaka. ESP8266WebServer server(80); Przechodzimy do funkcji setup. Na samym dole dodajemy kilka linijek - pierwsze 3 odpowiadają za funkcje, które się wykonają po wywołaniu strony. Ostatnia uruchamia serwer. server.on("/", handleRoot); server.on("/on", handleOn); server.on("/off", handleOff); server.begin(); Teraz jedynie w funkcji loop() zostało dodać obsługę całego serwera. server.handleClient(); Ostatnim krokiem zostało dodanie trzech funkcji, podanych wcześniej. Funkcja handleRoot wykona nam się za każdym razem, po wywołaniu strony głównej. Stwórzmy ją w takim razie: void handleRoot() { server.send ( 200, "text/html", "<p>witaj</p>"); } Wykonuje nam się tylko polecenie send. Wysyła ono informacje do klienta - pierwszą z nich jest kod 200 (wszystko jest w porządku), następnie tekst. Chwilowo po prostu przepisz ostatni argument, później omówimy HTMLa bardziej szczegółowo. Tak będzie wyglądać funkcja zapalająca oraz gasząca LED: void handleOn() { digitalWrite(PIN_LED, LOW); server.send(200, "text/html", "<p>dioda jest wlaczona</p>"); } void handleOff() { digitalWrite(PIN_LED, HIGH); server.send(200, "text/html", "<p>dioda jest wylaczona</p>"); } I, po dodaniu wszystkiego, całość będzie wyglądać tak: #define PIN_LED BUILTIN_LED #include <Wire.h> //biblioteka od I2C #include <LiquidCrystal_I2C.h> //biblioteka od HD44780 po I2C #include <ESP8266WiFi.h> #include <ESP8266WebServer.h> LiquidCrystal_I2C lcd(0x3F, 20, 4); //adres, szerokość, wysokość const char* ssid = ""; //nazwa const char* pass = ""; //hasło WiFiClient client; //obiekt wifi ESP8266WebServer server(80); void setup() { lcd.begin(); WiFi.begin(ssid, pass); //połącz z Wifi lcd.setCursor(0, 0); lcd.print("Laczenie "); pinMode(PIN_LED, OUTPUT); while(WiFi.status() != WL_CONNECTED) //wyświetlaj animacje dopóki się nie połączyliśmy { lcd.setCursor(10,0); lcd.print("."); delay(400); lcd.setCursor(10,0); lcd.print(" "); delay(400); } lcd.setCursor(0, 0); //napisz, że połączono i wyświetl IP lcd.print("Polaczono! "); lcd.setCursor(0, 1); lcd.print("IP: "); lcd.print(WiFi.localIP()); server.on("/", handleRoot); server.on("/on", handleOn); server.on("/off", handleOff); server.begin(); } void handleRoot() { server.send ( 200, "text/html", "<p>witaj</p>"); } void handleOn() { digitalWrite(PIN_LED, LOW); server.send(200, "text/html", "<p>dioda jest wlaczona</p>"); } void handleOff() { digitalWrite(PIN_LED, HIGH); server.send(200, "text/html", "<p>dioda jest wylaczona</p>"); } void loop() { server.handleClient(); //obsługa serwera } Teraz wpisz swoją nazwę i hasło, wgraj program, i wpisz w przeglądarkę adres IP. Powinien ci się ukazać tekst "witaj". Spróbuj teraz po adresie wpisać ukośnik, oraz "on". Dioda powinna się zapalić, oraz wyświetlić stosowny tekst. Tak samo będzie z podstroną off. Właśnie udało Ci się postawić serwer WWW na mikrokontrolerze za 20zł! W następnej części nauczymy się podstaw HTMLa, wczytamy stronę WWW z karty SD, a nawet zrobimy mini termometr IoT. W międzyczasie spróbuj zrobić program obsługujący dwie diody. Tak więc, do dzieła! Spis treści serii artykułów: 1. Omówienie, i przygotowanie środowiska 2. Zapoznanie z nowym środowiskiem, praca jako Arduino, prosty serwer WWW 3. Przyspieszony kurs na webmastera 4. Wykresy, zapis do SPIFFS, mini smart-dom 5. Odbiór danych z przeglądarki, stałe IP, łączenie modułów ESP
  42. 6 punktów
    Jakoś mi się udało ściągnąć od siostrzyczki
  43. 6 punktów
    Jak chodzi o pomysł na artykuł, to ja chętnie poczytałbym o bezpieczeństwie IoT. Mamy teraz coraz więcej projektów urządzeń podłączonych przez WiFi, czy inne łącza do internetu, a popularność układów ESP ma tutaj pewnie istotne znaczenie. Jednak nie zawsze takie beztroskie udostępnianie urządzeń w internecie jest całkiem bezpieczne. Wydaje mi się, że artykuł o związanych z tym ryzykach oraz chociaż podstawowych środkach zaradczych byłby bardzo interesujący.
  44. 6 punktów
    @SOYER Dobrze W wolnym czasie postaram się przygotować coś ciekawego z dokładnym opisem i kodem źródłowym. W internecie można znaleźć kilka ciekawych jak nie lepszych rozwiązań np. ten.
  45. 6 punktów
    @badrabbit96 ten sposób pomiaru prędkości wiatru zdecydowanie kwalifikuje się do osobnego artykułu na Forbocie ;), kto za?
  46. 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.
  47. 6 punktów
    Podczas ostatniej akcji kuponowo-rabatowej stałem się posiadaczem RaspberryPi 3B+. Od początku w planach miałem wykorzystanie tego mini komputerka jako serce konsoli retro. W pierwszej chwili chciałem stworzyć duży, stacjonarny automat Arcade. W porę doszedłem jednak do wniosku, iż nie mam za bardzo miejsca na taki "mebel". Dlatego też wizja się zmieniła. Postanowiłem zbudować przenośne urządzenie, podpinane do dowolnego Tv za pomocą kabla HDMI o długości 5m. Tak oto zrodziły się pierwsze plany. Zrobiłem jak zwykle przegląd rynku, sprawdziłem ceny poszczególnych elementów, analizowałem swoje możliwości wykonania obudowy. Ten etap trwał dość długo, ponieważ nie ukrywam, iż mam mało czasu na takie zabawy. Jednak w maju w końcu zabrałem się do budowy. Na początek musiała powstać przemyślana obudowa. Zdecydowałem się na zestaw przycisków i joystików z Aliexpress (nie spieszyło mi się, a za zaoszczędzone pieniądze można kupić coś dodatkowo). I tak oto w czerwcu stałem się posiadaczem zestawu jak poniżej: Oczywiście na tym etapie wybrałem już sposób rozmieszczenia przycisków i dokupiłem dodatkowo przyciski COIN oraz 1GRACZ i 2GRACZY (dolny rząd): Obudowa została wykonana ze starych paneli, narożniki wzmocnione kantówką 2x3cm i 2x2cm (wszystko z posiadanych ścinków). Rozplanowałem też umieszczenie wentylatora nawiewowego oraz kratki wentylacyjnej, co by zapewnić naszej konsoli dobre warunki pracy (wentylator z PC na 12V). Panele są różne, więc po wykonaniu pudła całość została przeciągnięta szpachlą samochodową z włóknami wzmacniającymi oraz przeszlifowana. Tak wykonana obudowa została oklejona folią carbon zakupioną na allegro. Skoro przyjąłem, że konsola będzie przenośna to trzeba było wzmocnić narożniki. Zrobiłem to z wykorzystaniem kątowników aluminiowych oraz narożników. Pozostało wykonanie połączeń elektrycznych. Przyciski są podświetlane, więc szkoda było tego nie wykorzystać. Stąd mamy po 4 przewody do każdego przycisku. Wykorzystałem posiadany zasilacz 5V 4A i zasiliłem z niego całość: poprzez przejściówki adapter gniazda 5.5/2.1-microUSB RaspPi, podświetlenie przycisków, wentylator. Na potrzeby wentylatora od PC dołożyłem małą przetwornicę StepUp ustawioną na 12V. Trzeba było rozplanować wyprowadzenie HDMI, USB i gniazdo zasilania z obudowy. W tym celu wyfrezowałem sobie płytkę z aluminium i umieściłem w niej takie oto przedłużki: Oczywiście serce naszej konsoli również znalazło się w środku wraz z oprogramowaniem RetroPie 4.5 i kartą SD 64GB zapełniona grami. Przy pierwszej konfiguracji korzystałem ze standardowej klawiatury PC ale w późniejszym etapie dla wygody wykorzystuję klawiaturę MT08 oraz dorywczo pad Tracer Recon PC. Nie opisuję procesu instalacji i konfiguracji RetroPie, ponieważ najlepiej w tym celu korzystać z internetu i strony projektu; https://retropie.org.uk/ Całość działa od kilku dni. Miałem sporo problemów z konfiguracją joysticków. Początkowo dobrze działały tylko w menu RetroPie a w grach osie i przyciski były pozamieniane. Poradziłem sobie z tym ręcznie edytując pliki konfiguracyjne i w nich dokonałem odpowiednich zmian. Dłuższe testy pokażą co należy zmienić. Już na wstępie planuję dodanie przycisku do zał/wył malinki: Do dopełnienia całości - od spodu znajduje się 6 gumowych nóżek, by nie rysować stołu/ławy itp. Wszystkie zdjęcia pochodzą z różnych etapów budowy i nie koniecznie odzwierciedlają w pełni gotowe urządzenie. Jednak już w takiej formie przechodziło wstępne testy. RaspPi jest podczepione do dolnej płyty. Wszystko w środku zamocowane by nic nie latało. Wentylator wraz z przeciwległą kratką zapewniają bardzo dobre chłodzenie wszystkich urządzeń. Na razie z niedogodności to w razie problemów z kartą sd trzeba rozkręcić dół (8 śrubek w narożnikach). Przydałaby się jakaś mała klapka rewizyjna, ale na razie nie mam pomysłu na jej wykonanie. Ostatecznie dodam chyba również oznaczenia na przyciskach.
  48. 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.
  49. 6 punktów
    Na wstępie zaznaczam: ani kody nie są mistrzostwem świata, ani ta cała elektronika nie jest najwyższego lotu. Dla mnie ważne było przedstawienie działającego i przydatnego urządzenia, jeśli ktoś będzie chciał zrobić lepiej - przynajmniej będzie miał jakąś podstawę. Również estetyka może być dla kogoś wątpliwa - ale pamiętajmy o jednym: o gustach się nie dyskutuje. Nie zwracajcie uwagi na stan stolarki - mieszkanie jest do remontu. Dlatego też wszystkie elementy dekoracyjne przyklejone są po prostu taśmą - i tak trzeba będzie to rozebrać a po remoncie złożyć z powrotem. A więc... Zaczęło się od narzekań mojej pięknej pani, że odkurzacz się psuje a w ogóle to takie odkurzanie to straszna robota i ona ma dość. Ponieważ jestem bardzo podobnego zdania (jeśli chodzi o straszną robotę) staliśmy się właścicielami urządzenia o nazwie Roomba 805. Odkurzaczyk okazał się bardzo fajny. Bez zbędnych bajerów (nie potrzebujemy sterowania jakąś wymyślną aplikacją z komórki ani pilota żeby go włączyć jak się stoi metr od niego), jeździ, odkurza, karmi się sam i tyle. Niestety, z uwagi na układ mieszkania dwie wirtualne ściany dołączone w komplecie do odkurzacza to dla nas trochę za mało, trzy to minimum a najlepiej cztery. I co się okazało... cena takiego czegoś to 1/4 ceny odkurzacza! Akurat parę dni wcześniej na Forum byłą dyskusja o miganiu ledami. Jako że zadanie wirtualnej ściany sprowadza się praktycznie do migania - stwierdziłem że dwie i pół stówy na takie migadło to nieco za dużo. Zacząłem szukać jakichś gotowców - oczywiście najpierw znalazłem coś na Arduino, potem całą resztę na PIC-ach. Ponieważ moim zdaniem zastosowanie Arduino (nawet Pro Mini) do migania ledą to przerost formy nad treścią, a na PIC-ach to ja się ostatnio znałem w zeszłym tysiącleciu, postanowiłem zrobić coś od zera. W szufladzie leżały sobie dwie sztuki ATtiny13 (nadmiar ze starszego projektu) - więc postanowiłem je wykorzystać. Na początku jednak trzeba było określić, jak ta nieszczęsna dioda IR ma migać. W tym celu zacząłem czytać, czytać, czytać... i okazało się, że co autor to inne zdanie, co komentarz to "mi nie działa". W jednym przynajmniej wszyscy byli zgodni: dioda pracuje na 38 kHz. I tyle. No cóż... właściwie mogłem zacząć od czego innego. Znalazłem w rupieciach TSOP31236, co prawda jest on na 36 a nie na 38 kHz ale szybkie zerknięcie do datasheeta upewniło mnie, że powinien jako-tako działać (przynajmniej z bliska) na 38. Podłączyłem go do analizatora stanów, włączyłem oryginalną ścianę... no i okazało się, że to całe "miganie" sprowadza się do trzech impulsów po 0.5 msec co 8 msec, a potem przerwy gdzieś w okolicach 130 msec (przerwy były różne, od 100 do 150 msec, czyli najprawdopodobniej niekrytyczne). No cóż, teraz wypadałoby sprawdzić efekty moich ciężkich poszukiwań. Nabazgrany na kolanie kod skompilowałem w Arduino IDE, leda IR podłączona przez opornik 100Ω do PB0, czerwona przez 2kΩ do PB1, przytwierdzone gumką do koszyka na baterie... #ifndef F_CPU #define F_CPU 9600000UL #endif #include <avr/io.h> #include <util/delay.h> #define pon do { \ TCCR0A |= (1<<WGM01) | (1<<COM0A0);\ TCCR0B |= (1<<CS00); \ } while(0) #define poff do { \ TCCR0A &= ~((1<<WGM01) | (1<<COM0A0)); \ TCCR0B &= ~(1<<CS00); \ PORTB &= ~(1 << PB0);\ } while (0) int main(void) { char i,n; DDRB |= (1 << PB0) | (1 << PB1); PORTB |= (1 << PB1); OCR0A = 125; for (n=0;; n = (n+1) & 31) { if (!n) PORTB |= (1 << PB1); for (i=0; i<3; i++) { pon; _delay_us(500); poff; _delay_us(7500); } if (!n) PORTB &= ~(1 << PB1); PORTB &= ~(1 << PB0); _delay_ms(132); } } Odkurzacz bardzo grzecznie zareagował, wycofał się z terenu pilnowanego przez moje migadło i ruszył w swoją stronę. W tym momencie już byłem pewny, że urządzenie będzie działać i mogę się bawić w wersję finalną. Przede wszystkim pomyślałem, że warto trochę pooszczędzać baterie. Poczytałem sobie nieco o usypianiu i oszczędnościach, wyszło mi coś takiego, że ATtiny działa przez ok. 17 milisekund a następnie idzie sobie spać. Całkiem nieźle, ale brakowało mi jednej rzeczy, której nie ma w oryginalnej ścianie: wskaźnika poziomu baterii. Niestety - ATtiny13 nie potrafi zmierzyć napięcia zasilania. Wypróbowałem kilka różnych sposobów (w tym jeden, który nawet nieźle działał - potencjometr podłączony do jednego z wyjść ATtiny i porównanie przez komparator z wewnętrznym napięciem wzorcowym), tyle że w pewnym momencie uświadomiłem sobie, że "ściana" będzie na stałe zamocowana w krytycznych miejscach i zasilanie mogę wziąć skądś obok - na pewno nie z baterii (urządzenie ma być stuprocentowo bezobsługowe). Tak więc wszystkie pomiary zostawiłem sobie na przyszłość (jeśli będę robił wersję na baterie) i wziąłem się do roboty. Najważniejsze było niewpuszczenie odkurzacza do kuchni (możliwa mokra podłoga i pałętanie się pod nogami człowiekowi co może nieść coś gorącego) oraz zakaz wstępu za szafkę z telewizorem (trochę kabli tam niestety leży, podłączone jest w sumie siedem urządzeń z których trzy wiszą na ścianie). Ponieważ szafka stoi zaraz obok wejścia do kuchni, postanowiłem zrobić tylko jedno urządzenie, do którego podłączę dwie ledy IR, a zasilanie 5V wezmę po prostu z USB telewizora, dodając dodatkowo akumulatorek na wypadek, gdyby elektrowni akurat odbiło wyłączyć mi prąd kiedy odkurzacz sprząta. Ponieważ musiałem zasilić dwie diody IR (a myślałem jeszcze o trzeciej, chociaż zostawiłem to sobie na później), diody nie są podłączone bezpośrednio do wyjścia ATtiny, ale przez tranzystor (użyłem tu 2N2222 tylko dlatego, że jako pierwszy wpadł mi do ręki). Urządzenie wyszło całkiem zgrabnie: Schemat połączeń: Trzypinowe łącza zastosowałem dlatego, aby nie trzeba było zastanawiać się którą stroną włożyć wtyczkę. Jednocześnie nawet w przypadku pomylenia wtyczki od diody kontrolnej z IR od razu widać, że kontrolka świeci nie tak jak powinna. Kod się niewiele bardziej skomplikował: #ifndef F_CPU #define F_CPU 9600000UL #endif #include <avr/sleep.h> #include <avr/wdt.h> #include <avr/io.h> #include <util/delay.h> ISR(WDT_vect) {} #define pon do { \ TCCR0A |= (1<<WGM01) | (1<<COM0A0);\ TCCR0B |= (1<<CS00); \ } while(0) #define poff do { \ TCCR0A &= ~((1<<WGM01) | (1<<COM0A0)); \ TCCR0B &= ~(1<<CS00); \ PORTB &= ~(1 << PB0);\ } while (0) int main(void) { char i, n; DDRB |= (1 << PB0) | (1 << PB1); PORTB |= (1 << PB1); OCR0A = 125; // watchdog MCUSR &= ~(1<<WDRF); WDTCR |= (1 << WDCE) | (1 << WDE); WDTCR = 1 | (1 << WDCE); WDTCR |= (1 << WDTIE); sei(); for (n=0;; n= (n + 1) & 7) { if (!n) PORTB |= (1 << PB1); for (i=0; i<3; i++) { pon; _delay_us(500); poff; if (i<2) _delay_us(7500); } if (!n) PORTB &= ~(1 << PB1); for (i=0; i<5; i++) { set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_mode(); } } } Pozostało mi tylko umieszczenie diod. O ile przy szafce byłoby to proste (w sumie tę diodę nie bardzo widać czyli można by zrobić dowolne, najprostsze mocowanie), o tyle przy framudze nie chciałem, aby wystawała tam jakaś elektronika. Poradziłem sobie w prosty sposób: ponieważ zawsze mi się podobały różne bajki o krasnoludkach, Pożyczalskich i innych stworach zamieszkujących dom - dioda IR została ukryta w drzwiach do domku Zgredka, dioda kontrolna to alarm przy drzwiach, a wjazdu za szafkę pilnuje Nessie Urządzenie się sprawdza, prąd pobiera jakiś znikomy (zdechły prawie całkowicie akumulatorek wydłubany ze starego laptopa, naładowany na tyle żeby sprawdzić że działa, wytrzymał prawie miesiąc - dopiero potem podłączyłem ładowarkę do USB), alarm przy drzwiach Zgredka wesoło sobie mruga, więc teraz przyszła pora na urządzenie numer dwa. Tu niestety miałem większy problem. Z uwagi na dość dziwaczny układ mieszkania (troje drzwi w wąskim korytarzyku) jak bym nie umieścił diody broniącej drzwi do łazienki, zawsze coś się odbijało od białej stolarki (framugi, trzecie drzwi) i odkurzacz bał się wjechać przez właściwe drzwi. Wymyśliłem sobie, że zrobię jakąś barierę foto która będzie włączać diodę IR tylko wtedy, kiedy robot spróbuje wjechać do łazienki (tam mu nie wolno), ale wychodziło mi to jakieś straszliwie skomplikowane. I tu znowu czytanie Forum mi pomogło - ktoś w pewnym wątku zaproponował diodę laserową. Postanowiłem spróbować - akurat i tak coś w Botlandzie kupowałem, tak że diodę wziąłem przy okazji z myślą, że jeśli to mi nie wyjdzie, wykorzystam do jakiegoś inngo projektu. Okazało się to strzałem w dziesiątkę. Co prawda zaprojektowanie mocowania diody (z koniecznością regulacji, bo kąty w 80-letniej stolarce są proste tylko z nazwy) przysporzyło mi nieco roboty i nie wyszło dokładnie jak chciałem, ale spełnia swoje zadanie. Tu już program się skomplikował. Założenia były następujące: Jeśli fototranzystor jest zwarty (na czas regulacji), dioda laserowa świeci cały czas aby umożliwić wycelowanie promienia; Jeśli drzwi do łazienki są zamknięte (kontaktron), nie ma potrzeby uruchamiania led; nic nie świeci, nic nie miga, ATtiny sprawdza sobie tylko raz na jakiś czas czy otworzyłem drzwi; Jeśli drzwi są otwarte, dioda laserowa włącza się na co chwilę i sprawdzane jest, czy robot nie próbuje wleźć tam gdzie nie trzeba. Dioda kontrola błyska sobie co kilka sekund; Jeśli wykryta zostaje obecność robota, włącza się dioda IR, i wyłącza się dopiero chwilę po odjeździe robota. Dioda kontrolna błyska szybciej. Tutaj też zastosowałem dwie diody IR (po obu stronach framugi), ale tym razem połączyłem je po prostu szeregowo. Tak więc kod znów nieco urósł: #ifndef F_CPU #define F_CPU 9600000UL #endif #include <avr/sleep.h> #include <avr/wdt.h> #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> #define PIN_IRED PB0 #define PIN_CONTROL PB1 #define PIN_PHOTO PB2 #define PIN_LASER PB4 #define PIN_DOOR PB3 #define setPin(a) do {PORTB |= (1 << (a));} while(0) #define unsetPin(a) do {PORTB &= ~(1 << (a));} while(0) ISR(WDT_vect) {} #define pon do { \ TCCR0A |= (1<<WGM01) | (1<<COM0A0);\ TCCR0B |= (1<<CS00); \ } while(0) #define poff do { \ TCCR0A &= ~((1<<WGM01) | (1<<COM0A0)); \ TCCR0B &= ~(1<<CS00); \ unsetPin(PIN_IRED);\ } while (0) int adc_read (void) { ADMUX = (1 << MUX0) | (1 << ADLAR); ADCSRA |= (1 << ADSC) ; while (ADCSRA & (1 << ADSC)); return ADCH; } #define LASER_DELAY 500 #define MIN_PHOTO 20 #define CALIBRATE 2 #define SHADE 1 #define DECAY 3 uint8_t readPhoto(void) { int8_t i, pom;uint8_t na, nb; ADCSRA |= ( 1<<ADEN); for (i=pom=0;i<3;i++) { if (i) _delay_us(LASER_DELAY); na = adc_read(); if (!i && na < 5) { ADCSRA &= ~( 1<<ADEN); return CALIBRATE; } setPin(PIN_LASER); _delay_us(LASER_DELAY); nb=adc_read(); unsetPin(PIN_LASER); if (na > MIN_PHOTO && na - MIN_PHOTO > nb) pom++; } ADCSRA &= ~( 1<<ADEN); return (pom > 1) ? 0 : SHADE; } int main(void) { int8_t ndark, res, i; uint8_t ledctl; DDRB |= (1 << PIN_IRED) | (1 << PIN_LASER) | (1 << PIN_CONTROL); PORTB |= (1 << PIN_CONTROL) | (1 << PIN_DOOR); OCR0A = 125; // watchdog MCUSR &= ~(1<<WDRF); WDTCR |= (1 << WDCE) | (1 << WDE); WDTCR = 1 | (1 << WDCE); WDTCR |= (1 << WDTIE); // ADC ADCSRA = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); sei(); for (ndark = ledctl = 0;;) { if (ndark > 0) { ndark --; res = DECAY; } else { if (!(PINB & (1 << PIN_DOOR))) { for (i = 10; i >= 0; i--) { set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_mode(); } ledctl = 0; continue; } res = readPhoto(); if (res == CALIBRATE) { ndark = 0; setPin(PIN_LASER); for (;;) { _delay_ms(100); res = readPhoto(); if (res != CALIBRATE) break; } unsetPin(PIN_LASER); _delay_ms(100); continue; } } if (res) { if (res != DECAY) ndark = 20; if (!ledctl) setPin(PIN_CONTROL); ledctl = (ledctl + 1) & 3; for (i=0; i<3; i++) { pon; _delay_us(500); poff; if (i<2) _delay_us(7500); } unsetPin(PIN_CONTROL); for (i=0; i<5; i++) { set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_mode(); } } else { if (!ledctl) { setPin(PIN_CONTROL); _delay_ms(10); unsetPin(PIN_CONTROL); } for (i=0; i<3; i++) { set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_mode(); } ledctl = (ledctl + 1) & 31; } } } Schemat połączeń wygląda mniej więcej tak (odtwarzam z pamięci): Kilka uwag: Użyłem gotowego modułu z diodą laserową, który ma już potrzebny rezystor (stąd brak na schemacie). Fototranzystor TEPT 4400 został użyty przede wszystkim dlatego, że go miałem. Prawdopodobnie nadałby się dowolny reagujący na kolor światła diody. Mój błąd - rezystor R3 podłączony jest cały czas do VCC zamiast np. być zasilany razem z diodą laserową (oszczędność energii). Jak już wspomniałem - urządzenie jest zasilane z sieci, czyli nie jest to krytyczne, ale w razie gdyby komuś chciało się coś takiego zrobić można pomyśleć o podłączeniu tego rezystora razem z diodą i odpowiednim przerobieniu funkcji readPhoto(). Na początku bałem się jednak, że nie będę mógł odróżnić zwarcia fototranzystora od jego oświetlenia - dlatego tak już zostało. I znowu kwestia umieszczenia tego wszystkiego na framudze drzwi w ten sposób, aby nic (ani szalejący odkurzacz, ani jakieś przypadkowe potrącenie) nie ruszyło diody. Ponieważ miałem już domek Zgredka - dorobiłem mu saunę i wygódkę. Przy okazji wyskoczył bardzo ciekawy efekt uboczny - światło diody laserowej prześwieca przez pomarańczowy "murek" sauny i tworzy wrażenie płonącego w saunie pieca Urządzenie numer dwa zasilane jest oddzielnie ze zwykłej starej ładowarki od e-papierosa. Planuję wspólne zasilanie obu urządzeń - ale aby podciągnąć tam kabel muszę wbić się po drodze pod dwa progi (i tak do wymiany) - czyli czekam na remont...
  50. 6 punktów
    Od momentu poznania ESP8266 bardzo spodobał mi się ten układ. Gdy skończyłem robić pierwszy projekt oparty na tym układzie od razu zacząłem wymyślać kolejny. Dzisiaj chciałbym Wam przedstawić sterownik oświetlenia. Współpracuje on ze zwykłym włącznikiem świecznikowym i mieści się w puszce fi60, dzięki czemu (nad każdym włącznikiem światła mam puszkę fi70) w puszcze fi70 zostaje jeszcze sporo miejsca na kable. Po zaprojektowaniu płytki w programie Eagle zleciłem wyprodukowanie jej firmie jlcpcb. Wszystko trwało tydzień (zamawiałem również inne płytki dlatego tyle to trwało ). Płytki wysłali w piątek po południu czasu lokalnego a w poniedziałek płaciłem już vat u kuriera z DHL . Płytki wyglądają bardzo dobrze (sami możecie ocenić jakość produktu ) Po odebraniu płytek zacząłem wszystko składać w całość. ESP8266 12F zostało wykorzystane jako "mózg" płytki. Jako zasilacz służy Hi-Link HLK-PM01 230V na 5V. Za załączenie napięcia odpowiedzialne są dwa przekaźniki z cewką na 5V. Dodatkowo do każdego przekaźnika dołożyłem diodę prostowniczą. ESP jest zasilane napięciem 3.3v przez co musiałem zastosować LDO. Dodatkowo na wejściu fazy do układu zamontowałem bezpiecznik firmy bel fuse. Żeby esp mogło załączać przekaźnik musiałem wykorzystać tranzystor bc817 w wersji smd. Do tego potrzebowałem jedno złącze ark raster 5mm podwójne, oraz dwa złącza ark raster 5mm potrójne. Całość po zlutowaniu i umieszczeniu w puszcze fi60 wygląda następująco: Teraz przyszła kolej na oprogramowanie. Zdecydowałem się zrobić prostą stronę konfiguracyjną na której możemy: zmienić hasło do urządzenia zmienić dane serwera mqtt (sterowanie z każdego miejsca na świecie ) zmienić przyłączenie do sieci wi-fi zobaczyć jak długo pracuje nasz układ wybrać ilość podpiętych włączników (jedno albo dwa) Całość jest również sterowana z telefonu z systemem android. Teraz przymierzam się do zrobienia wersji v2 tego sterownika, chcę by był oparty na triakach i optotriakach (z tego co wyliczyłem to żarówka led o mocy 15W pobiera tylko 60mA a świeci jak zwykła żarówka 120W) oraz będzie posiadała wsparcie dla dotykowych podświetlanych włączników (moduły httm oraz panele marki weilak, wszystko mam tylko projekt został ) Jestem bardzo ciekaw Waszych opinii, dajcie znać w komentarzach co myślicie mo jej konstrukcji
Tablica liderów jest ustawiona na Warszawa/GMT+02:00
×
×
  • Utwórz nowe...