Skocz do zawartości

Tablica liderów


Popularna zawartość

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

  1. 6 punktów
    Co może zrobić uczeń podstawówki, kiedy nie można w szkole używać telefonów? Specjalny kalkulator! Tak, wiem że urządzenie jest trochę przekombinowane - za pare zł mam nowy kalkulator z kiosku, ale to nie jest zwykły kalkulator.Ten kalkulator, albowiem, robiłem ja Budowa Urządzenie posiada wyświetlacz TFT 1.8" ze slotem na kartę pamięci, z której możemy odtwarzać zdjęcia. Do wprowadzania informacji posłużyła mi klawiatura 4x4 w formie naklejki. Całość napędza Arduino Pro Mini, które przeprogramowałem na korzystanie z wewnętrznego kwarcu 8Mhz i wyłączenie BOD. Energię dostarcza akumulator Lipo wyjęty z tableta, ładowany przez dolutowanie się do akusa. Napięcie obniża stabilizator AMS1117 na 3,3v, co rozwiązało kwestię napięcia logiki (TFT ma 3.3v, arduino normalnie 5v) oraz zużycia energii. Wyświetlacz, do programowania, można zdejmować. Po prawej stronie dostrzegamy przełącznik włączający urządzenie, zaś pod TFT znajduje się przycisk funkcyjny. Obudowy nie chciałem robić, gdyż chciałem wzbudzić zainteresowanie wśród kolegów Funkcje Kalkulator ma funkcję liczenia. Wow. Potrafi dzielić, mnożyć, dodawać i odejmować liczby dziesiętne, dodatnie i ujemne - ale nie umie wykonywać kolejności działań (dlatego według kalkulatora 2+2x2=8). ALE. Oprócz kalkulatora, utworzyłem aplikację paint - służy do malowania piksel po pikselu. Dobre na nudne lekcje polskiego. Po włączeniu urządzenia, wyświetla nam się lista dostępnych aplikacji. Oprócz niedokończonego snejka, jest jeszcze "czwarta aplikacja" - ściąga Wyświetla ona wzory z fizyki, chemii i matematyki oraz prawa dynamiki Newtona. Kalkulator może również w progmemie przechować jedno kolorowe zdjęcie, i wyświetlić je po 8x naciśnięciu przycisku Równa się. Taki kalkulator to fajna rzecz, z racji tego że można go używać w szkole bez ograniczeń i da się na nim pobawić. W ostateczności przeglądać memy, po włożeniu karty SD. Pozdrawiam, Leoneq :3
  2. 5 punktów
    Metod samodzielnego wykonywania płytek pcb jest wiele. Termotransfer - potrzebna dobra drukarka laserowa, fototransfer - też dobra drukarka, płytki światłoczułe lub własnoręczne pokrywanie takim lakierem, suszenie, naświetlanie itp. Testowałem naświetlanie i albo prześwietliło albo odwrotnie. Można frezować. Ale można też rysować. Oczywiście nie ręcznie, jak to dawniej bywało. Jako, że z cnc i budową urządzeń mam trochę do czynienia, postanowiłem zbudować sobie taką małą maszynkę. Większość materiałów miałem - aluminiowe płytki, kątowniki, silniki krokowe i szlifowane prowadnice ze starych drukarek, a także dostęp do własnej małej tokarko - frezarki cnc. Całość miała być "po taniości" i maksymalne wykorzystanie "przydasiów" . Początkowo użyłem kilku gotowych podzespołów, jak stolik krzyżowy (proxxon) i mechanizm napędu ze starego CD/DVD jako oś Z. Użyte silniczki mają 24 kroki/obrót, zasilane z 12 V. Ich sterowniki to DRV8825 . Koła zębate pod paski też wyrób własny - frezowanie ( aluminium i poliamid ), paski trzeba było nabyć. Całością steruje Arduino Uno i GRLB1.1. Problem pojawił się w momencie zainstalowania krańcówek. Jedna oś działała, reszta martwa. CNC Shield był projektowany pod GRLB-8, a nowsze wersje tego oprogramowania mają pozmieniane niektóre wyprowadzenia. Ale jest to opisane na stronie GRLB. Nic nie pomagało - wgrywanie softu, czyszczenie procka i jego EEPROM'u. Oczywiście kontrola przewodów i samych krańcówek. Do "przyzwoitego" połączenia przewodów z układem sterującym przydają się złącza BLS. Dopiero nowa Atmega328 - i teraz wszystko gra. Nie spodziewałem się wadliwego układu. W ferworze walki o działające krańcówki została wykonana optoizolacja na układzie LTV847 . Płytkę do tego wyrysowała maszyna już sama dla siebie. Ponieważ wszystko działało, można by tak zostawić. Jednak pole robocze było małe - 45 x 120 mm. Trochę pracy trzeba było włożyć - cięcie, frezowanie i toczenie elementów. Ale warto było, bo powstałą dość solidna konstrukcja - już tak przyszłościowo.Dorobienie śruby i nakrętki (żeliwo) z kasowaniem luzu, mocowania silnika i łożyskowania śruby dopełnia reszty. Oczywiście dokładne ustawienie na czujnik zegarowy i dokładnym kątomierzem równoległości oraz kątów. W efekcie obszar roboczy zwiększył się do 180 x 120 mm. Większych płytek chyba robić nie będę. Mocowanie pisaka umożliwia ruch góra/dół - taki luz bezpieczeństwa - z uwagi na czasem spotykane niezbyt "płaskie" laminaty, natomiast luz poprzeczny jest minimalny i wg. pomiarów nie przekracza 0,03mm. Można więc mówić o precyzji . Oczywiście kusi przeróbka. W pierwszym rzędzie zmiana silników na taki krokowiec. Obecnie używane mają cienką oś - 2 mm, która wygina się od naciągniętych pasków. Ich łożyska cierne też się od tego mocno wyrabiają jednostronnie. Drugi powód - mocniejszy silnik umożliwi większą szybkość pracy. Obecnie to 300 mm/min. Nowa oś Z ( mocniejsza ) i zwiększenie jej zakresu pracy oraz obniżenie stołu roboczego. Zamiast obecnego mazaka - bo tylko tyle ten mały silniczek dźwignie - można by założyć jakiś moduł lasera albo i głowicę drukującą ( 3D ). Albo i mały silnik jako wiertarkę. W chwili obecnej pisak daje ścieżkę szerokości 0,4 mm lub wielokrotność. Próby z cieńszą ścieżką nie wypadły na razie pomyślnie - za słabe krycie i potrafią zostać przetrawione - używam chlorku żelaza.
  3. 5 punktów
    Tym razem będzie nietechnicznie — to co tu opisuję, to wyłącznie moja własna prywatna opinia. Jeśli ktoś się nie zgadza, to zapraszam do dyskusji, jednak rezerwuję sobie prawo pozostania przy swoich wnioskach. Będzie o androidach, ale nie systemie operacyjnym dla telefonów, tylko o człekokształtnych robotach. Otóż uważam, że pomimo ich częstego pojawiania się w wiadomościach i na pierwszych stronach gazet, roboty tego typu są generalnie bezużyteczne poza bardzo wąską dziedziną zastosowań, jaką jest przemysł rozrywkowy. Jest tak z bardzo prostego powodu: humanoidalny kształt człowieka nie ma, z technicznego punktu widzenia, żadnych zalet, natomiast niesie ze sobą wiele nietrywialnych wyzwań. Zatem jeśli główną rolą naszego robota jest wyglądanie jak człowiek — po to, żeby stać na wystawie sklepowej i prezentować ubrania, iść w pochodzie, występować na scenie lub w filmie, bawić dzieci jako zabawka, czy zapewniać firmie dobrą prasę — to jak najbardziej ma sens nadanie mu właśnie takiego kształtu. W innych przypadkach nie ma to najmniejszego sensu, bo okazuje się, że wszelkie zalety takiego rozwiązania są tylko pozorne: 1. Zastępowanie ludzi w środowisku przystosowanym dla ludzi: używanie ludzkich maszyn i narzędzi, poruszanie się w mieszkaniach i biurach. Wydaje się, że to niezła racjonalizacja — jeśli dany robot ma zastąpić człowieka, to musi mieć możliwość sięgnięcia do wszystkich rzeczy, do których człowiek by sięgnął, wejścia w miejsce, w które człowiek może wejść i ogólnie robienia tego, co człowiek mógłby zrobić. Ale okazuje się, że można to osiągnąć bez kopiowania ludzkiego ciała. Co więcej, taki nie-człekokształtny robot będzie miał znaczącą przewagę, bo będzie mieć większą zwinność, lepszy zasięg, większą siłę i szybkość, nie mówiąc już o łatwiejszym sterowaniu. Roboty w rodzaju Robosimiania z łatwością mieszczą się wszędzie tam, gdzie człowiek, a są znacznie bardziej uniwersalne. 2. Interakcja z ludźmi. Wydawałoby się, że lepiej się będziemy czuć współpracując z czymś, co wygląda i zachowuje się tak jak my sami. Okazuje się, że wręcz przeciwnie. Im bardziej robot przypomina człowieka, tym bardziej obrzydliwy, wstrętny i straszny będzie się wydawał i tym gorzej przebiegać będzie interakcja. Zjawisko nosi nazwę "uncanny valley" i jest dość dobrze zbadane przez psychologów. Rozwiązaniem jest nadanie abstrakcyjnych lub komiksowych kształtów robotom, ale jeszcze łatwiej po prostu zaprojektować je tak, żeby ich kształt odpowiadał ich funkcji. 3. Wydajność energetyczna. Homo sapiens jest chyba najwydajniejszym biegaczem długodystansowym wśród zwierząt — potrafimy zagonić na śmierć konia. Nasz chód jest bardzo wydajny energetycznie — wybicie się z kostki pozwala na odzyskanie sporej części energii, która w innym przypadku zmarnowana by była na uderzeniu nogi w ziemię. Od dłuższego czasu próbujemy zbudować urządzenia, które by naśladowały ten chód. No ale przy całej tej wydajności tak naprawdę nic nie umywa się do transportu kołowego, który ma tą zaletę, że nie zużywa energii na ciągłą walkę z grawitacją. To między innymi dlatego wszystkie łaziki marsjańskie są na kołach, nawet jeśli mają pokonywać dość nierówny teren. Nawet jeśli kiedyś uda nam się zbudować dwunożne podwozie wzorowane na ludzkim chodzie, to przecież nie ma żadnego powodu dla którego jego górna część miałaby wyglądać jak człowiek. 4. Teleoperacja — w literaturze science fiction oraz filmach, szczególnie japońskich, często pojawia się pomysł robota sterowanego bezpośrednio ruchami człowieka. Pilot takiego robota, siedzący w jego wnętrzu lub w osobnym urządzeniu, jest zwieszony w specjalnej uprzęży (lub pływa w pojemniku ze specjalnym płynem) z sensorami, które każdy jego ruch przekładają na ruch robota. Robot jest przy tym często wielokrotnie większy i silniejszy od człowieka. Wydaje się oczywistym, że w takiej sytuacji najlepiej sprawdzać się będzie kształt kopiujący ludzkie ciało. W praktyce okazuje się jednak, że pomysł taki jest na dzień dzisiejszy praktycznie niemożliwy do zrealizowania w takiej postaci: utrzymywanie równowagi wymaga informacji zwrotnej, której nie mamy jak takiemu pilotowi przekazać. Być może kiedyś będziemy potrafili wpiąć się bezpośrednio w układ nerwowy pilota i w ten sposób sterować robotem, ale wówczas właściwie nie ma powodu, żeby nadal był on człekokształtny, a nie, na przykład, w kształcie goryla czy gibbona — przecież mamy nawet nerwy i odruchy potrzebne do sterowania ogonem! Na dzień dzisiejszy teleoperacja zazwyczaj ogranicza się do samych manipulatorów, czasem zamontowanych na ruchomej platformie — i to w zupełności wystarcza. Nawet nie mają na końcach ludzkich dłoni, tylko od razu narzędzia. 5. Seksboty. Bez komentarza — chyba tylko naprawdę zdesperowany nastolatek mógłby wpaść na taki pomysł. Podsumowując, w zasadzie nie ma żadnego powodu, aby budować praktyczne człekokształtne roboty. Jeśli ich wygląd nie jest celem samym w sobie, to warto zastanowić się nad projektem najlepiej przystosowanym do ich zadań, zamiast niepotrzebnie komplikować sobie życie próbując naśladować naturę.
  4. 5 punktów
    Wstęp O projekcie myślałem od czerwca poprzedniego roku, to jest 2018. Postępy prac opisywałem tu - cel projektu stanowiła budowa robota klasy SCARA - dwuosiowego ramienia robotycznego manipulującego efektorem w płaszczyźnie XY, z funkcją grawerowania laserowego oraz mini- wrzecionem modelarskim, rysowania mazakiem i pick&place realizowanym techniką próżniową. SCARA to akronim słów Selective Compliance Articulated Robot Arm, co oznacza, że robot w osiach X i Y porusza się obrotowo względem siebie i liniowo w osi Z. Dzięki temu, SCARA jest szybsza (w porównaniu do maszyny pracującej w układzie kartezjańskim), pozwala na osiągnięcie zadanego w obszarze roboczym punktu dwoma trajektoriami, a także umożliwia łatwy montaż połączony z niewielkimi rozmiarami w porównaniu do pola roboczego, który oferuje (klasyczna konfiguracja XYZ cechuje się faktem, że pole robocze musi być mniejsze od rozmiarów samej konstrukcji). Nie może natomiast przenosić dużych sił, a samo sterowanie wymaga zastosowania równań trygonometrycznych oraz interpolowania w momencie, kiedy końcówka ma wykonać ruch po linii prostej. Ponadto, roboty tej klasy cechują się mniejszą sztywnością. Całość sprawia, że ramiona robotyczne tego typu są idealne dla lekkich aplikacji, gdzie często wymagana jest szybkość, higieniczność (małe natężenie hałasu) oraz łatwość instalacji połączona z niewielką ilością miejsca, czyli w zastosowaniach "podnieś i przenieś" lub operacjach montażowych, chociaż z powodzeniem może posiadać koncówkę roboczą typową dla tradycyjnych maszyn CNC. Mechanika i elektronika Prostokątna podstawa robota o wymiarach 320x240mm została numerycznie wyfrezowana, z plexi o grubości 16mm. Otwory, służące zamocowaniu silnika oraz dysku łożyskującego I stopień swobody zostały nagwintowane gwintem M5. Wszystkie elementy 2D, które osobiście frezowałem, projektowałem w Solid Edge 2019. Najwięcej problemów miałem z przekładniami (zwiększenie momentów obrotowych silników oraz rozdzielczości (stosunek ilość kroków/obrót) oraz konieczność przenoszenia dużych sił, zgodnie z równaniem dźwigni jednostronnej) - właściwe ramię jest przymocowane do koła zębatego, które łożyskowane jest przez dysk z poliamidu (przeniesienie sił osiowych) oraz wciskane łożyska 6008RS (przeniesienie sił promieniowych). Oś koła stanowi imbusowa śruba M10, przykręcona nakrętką samohamowną z podkładką. W obydwóch stopniach swobody, przekładnie pasowe działają w oparciu o zamknięte paski zębate o module HTD 3mm, które pochodzą ze znanych robotów kuchennych "KASIA". Koła zębate, podobnie jak inne wydruki 3D, wykonano przy pomocy drukarki Zortrax M200. Wykorzystałem bardzo dobry filament ABS firmy Spectrum. Drugi stopień swobody działa i założyskowany jest identycznie z tą różnicą, iż wykorzystano jedno łożysko kulkowe, a dysk wykonano z ABS-u. Warto zaznaczyć, że każdy z uchwytów silników krokowych posiada podłużne otwory, tak zwane "fasolki", które pozwalają na regulację naprężenia paska. Średnice osi silników krokowych to odpowiednio 6,35mm oraz 5mm, niezawodne połączenie małych zębatek z gładkim wałem uzyskano poprzez wcisk na gorąco oraz ścisk dwoma śrubkami imbusowymi M3. Drugie z ramion można łatwo zmienić, w zależności od tego, jaką funkcję ma pełnić robot (na zdjęciu koncówka z modułem lasera IR 500mW). Każde z ramion ma długość 200mm, zatem maksymalny obszar roboczy konstrukcji (bez uwzględnienia faktu ograniczenia go przez wieże silników i podstawę) to koło o średnicy 800mm. Wymiana ramienia (a co za tym idzie - funkcji) jest łatwa i przyjemna, zajmuje mniej niż 5 minut. W robocie nie zastosowano czujników krańcowych, pozycję zerową ustawiam ręcznie w linii prostej. W precyzyjnym ustawieniu pozycji zerowej pomocne są specjalne znaczniki (zdjęcie poniżej), zakładane tylko na czas zerowania. Drugie zdjęcie przedstawia budowę, a trzecie - mosiężne emblematy, jakimi ozdobiłem podstawę Wykorzystałem silniki krokowe, NEMA23 i NEMA17 Osobny blok funkcjonalny robota stanowi sterownik, którego zdjęcie przedstawiono poniżej - połączenia z urządzeniami zewnętrznymi (silniki krokowe oraz efektor) zrealizowane zostały złączami JST-BEC (bądź 3-pinowym złączem 0,1" jeżeli efektorem jest serwomechanizm modelarski). Do zasilacza sterownika (1) podłączany jest przewód zasilający sieciowy. Napięcie wyjściowe wynosi 12V DC, a wydajność prądowa zasilacza to 5A. Celem sterowania robotem (kiedy nie jest wgrana zaprogramowana sekwencja ruchów) należy podłączyć przewodem USB typu B do komputera PC płytkę Arduino UNO (6), która stanowi "mózg" sterownika - podaje ona sygnały sterujące pracą silników krokowych (HY-DIV268N oraz TB6560 - odpowiednio 2 i 3), serwomechanizmu i/lub przekaźnika (7), który może załączać palnik laserowy lub elektrozawór. Płytka (5) posiada bezpiecznik, diodę - wskaźnik napięcia (na płycie znajduje się również woltomierz (9)), kondensatory chroniące przed chwilowym spadkiem napięcia zasilania oraz umożliwia podłączenie każdego modułu odpowiednio pod linię 12V DC lub 5V DC (dla logiki), za którą odpowiedzialny jest moduł stabilizatora szeregowego (8) - LM7805. Zadaniem wentylatora (4) jest chłodzenie sterowników silników krokowych. Sterownik przeszedł drobny lifting i obecnie wygląda tak, jak na II zdjęciu Software Dla funkcji rysowania, palenia laserem oraz grawerowania wykorzystano udostępniane na zasadach open-source oprogramowanie firmy Makeblock - mDraw, rozszerzony o funkcję sterowania przekaźnikiem. Natomiast dla aplikacja pick&place napisałem własny program, z funkcją odwrotnego liczenia kinematyki, oraz zamykania/otwierania elektrozaworu, co pozwala na zaprogramowanie pełnego cyklu "podnieś i przenieś". Składa się on z prostych procedur, typu PrzejdzDoPunktu(x,y), Chwyc(), Pusc(), SilownikZ(pozycja) i tak dalej. Trzecią oś swobody stanowi bowiem dla rysowania i grawerowania niewielka prowadnica podnoszona za pomocą linki przez serwo, natomiast przy chwytaniu podciśnieniowym wspomogłem się https://www.thingiverse.com/thing:3170748 Tak wygląda moduł generujący podciśnienie, wykorzystałem pompę próżniową Film - podsumowanie zawierające ujęcia z budowy oraz działania konstrukcji Robot otrzymał wyróżnienie na tegorocznych zawodach Robotic Arena oraz został zgłoszony do konkursu "Elektronika, by żyło się łatwiej". Przy okazji, pragnę podziękować mojemu sponsorowi - firmie MONDI Polska, która umożliwiła realizację całości projektu. Pozdrawiam, Wiktor Nowacki wn2001 (dla ścisłości - spostrzegawczy z pewnością zauważą rozbieżności w detalach pomiędzy różnymi zdjęciami oraz ujęciami z filmu - wykonałem je po prostu w różnym czasie, kiedy udoskonalałem jeszcze i poprawiałem konstrukcję )
  5. 5 punktów
    Hej koledzy i koleżanki;-) Ci którzy są dłużej obecni na Forbocie mnie już trochę znają, ale dla nowych użytkowników których sporo ostatnio, krótki rys historyczny. Moje zainteresownie elektroniką zaczęło się w październiku 2017 roku od jakiejś nudy przy kompie kiedy to przeglądając neta natknąłem się na kursy Forbota. Długo sie nie namyślając zamówiłem najpierw kurs elektroniki podstawowy i od strzału dodatkowy, no i oczywiście zestaw tablic. Jako, że mnie to strasznie wciągnęło, dalej poszło za ciosem: technika cyfrowa i oczywiście kurs arduino poziom pierwszy i drugi . Wciągnęło niesamowicie, chyba głównie dzięki temu, że autorzy kursu potrafili swoją wiedzę przekazać jasno, klarownie i przede wszystkim ciekawie. Dobra koniec tej łzawej historyjki pora na opis projektu. Mój system zbudowany jest z: Arduino MEGA, nodeMCU z esp8266, czujnik zanieczyszczeń powietrza PMS5003; ethernet Shield do wyżej wymienionego, czterech czujników temperaturyDS18B20, dwa czujniki temperatury i wilgotności typu DHT11, jeden DHT22, czujnik ciśnienia i temperatury BMP280 I2C, moduł zegarowy RTC1307 I2C, enkoder z przyciskiem, wyświetlacz LCD 20×4 I2C, dwie listwy diod programowalnych po 4 szt, czujnik deszczu, buzzer bez generatora, moduł 8 przekaźników; kilka dodakowych przekaźników, radiolinia PIN4 Proxima 2szt, przekaźnik czasowy PCM-04 Zamel; kilka krańcówek, bramki logiczne OR; żaróweczka neonka, fotorezystor, kupa przewodów, rezystorów, diod itp., tablet, telefon. Działanie: Układ mierzy temperaturę w dwóch miejscach w domu, na zewnątrz temperaturę w słońcu, przy gruncie i 1m nad gruntem w cieniu. Dodatkowo mierzy ciśnienie atmosferyczne, wilgotność zewnętrzną i wewnętrzną oraz temperaturę wody w CO. Od niedawna także poziom zanieczyszczenia powietrza (budowę urządzonka pomiarowego przedstawię w kolejnym artykule). Zapamiętuje również, najwyższe i najniższe wartości każdego z czujników wraz z datą zdarzenia. Funkcją dodatkową jest alarm wywoływany przy pierwszych opadach deszczu, a także (na wyraźne życzenie żony) alarm informujący kiedy pralka w piwnicy skończyła pracę. Jeśli kogoś interesuje ta ostatnia funkcja odsyłam do mojego tematu na Forbocie. Główną funkcją układu jest sterowanie pracą 8 przekaźników obsługujących min. bramę, furtkę, oświetlenie. Każdy z przekaźników jest sterowany niezależnie: -sygnałem z arduino idącym przez bramki OR do których dochodzą także -sygnały z zewnętrznych włączników naściennych -radiolini sterowanych pilotem, - no i oczywiscie aplikacją BLYNK z telefonu, tabletu... Dopiero sygnał za bramką OR idzie do przekaźnika oraz dodatkowo jako potwierdzenie wraca do arduino. Dioda led na obudowie tabletu informuje o włączeniu przekaźnika także, kiedy tenże jest włączony „poza arduino” przez włącznik naścienny bądź radiolinię. Informacja o tym fakcie wraca do arduino powodując odpowiednie zmiany zmiennych i na bieżąco aktualizując diody led i informacje wyświetlane na LCD 20×4 oraz wyświetlaczu telefonu i tabletu w kuchni na ścianie będącego "centrum sterowania" całością. Wszystkie dane z czujników są na bieżąco wyświetlane i przewijane na LCD 20×4 który to wyswietlacz stał się dodatkowym(po tablecie) i zainstalowałem go w obudowie szafki zawierającej najważniejsze elementy układu. Po wciśnięciu przycisku enkodera wchodzimy w menu opcje gdzie możemy na LCD20x4 sprawdzić najwyższy i najniższy odczyt każdego z czujników, datę tego odczytu oraz zresetować pamięć dla każdego odczytu i czujnika z osobna. Możemy również tym enkoderem sprawdzić w menu stan przekaźników i nimi sterować. Wciśnięcie enkodera jest sygnalizowane przez buzzer. Osobną sprawą jest BLYNK który miał służyć początkowo tylko do kontroli przez telefon nad przekaźnikami, ale po poznaniu jego możliwości, całkowicie zmieniłem założenia i teraz to tablet z zainstalowanym Blynkiem jest głównym wyświetlaczem i sterownikiem całego układu. Trochę bliżej na temat aplikacji BLYNK w moim systemie możecie przeczytać tu. Na tablecie mam podmenu: przekaźniki, czujniki, max i min, historia, alarmy. W menu przekaźniki sterujemy z tableta i telefonu, w dowolnym miejscu na świecie, przekaźnikami, otrzymując z powrotem informację o faktycznym włączeniu przekaźnika(zmiana koloru widgeta oraz wyświetlonego na nim napisu).Ta informacja pojawia się także kiedy przekaźnik zostanie włączony przez kogoś innego(przez włącznik ścienny, radiolinia lub drugie urządzenie:tablet lub telefon). Choć poprzez udostępnienie tokena do naszego projektu takich urządzeń może być więcej. Drugie menu w BLYNK-uto wyświetlanie danych z wszystkich czujników odświeżane co 1 min, oraz małe „diody led” migające i informujące o prawidłowej komunikacji apka-arduino MEGA i apka-ESP(czujnik jakości powietrza). Kolejne menu to ekran z rozwijanym podmenu na którym wyświetlone są nazwy wszystkich czujników i poprzez wybór któregoś z nich możemy sprawdzić(podobnie jak to było na LCD20x4), najniższą i najwyższą wartość datę tego zdarzenia i zresetować pamięć dla każdego czujnika osobno. Następne menu to historia, czyli wykresy. Ja mam ustawione wyświetlanie na wykresach historii odczytów ciśnienia atmosferycznego, wigotności, temperatury CO, temperatury zewnętrznej, oraz osobny wykres dla czujnika jakości powietrza. Ostatnie menu w BLYNK-uto alarmy. Po pojawieniu się na odpowiednim pinie arduino sygnału z czujnika deszczu lub sygnału o końcu pracy pralki, na wyświetlaczu LCD20x4 przestaje się przewijać informacja o dacie, godzinie i odczytach z czujników, a wyświetla się info o alarmie z odpowiednią treścią, albo o pralce albo o deszczu. Dodatkowo listwy diod led migają na niebiesko(pralka) lub na czerwono(deszcz). Po wciśnięciu enkodera, wyłączeniu na tablecie lub telefonie albo upływie ustawionego czasu alarm wyłącza się. Informacja o alarmie pojawia się jako notyfikacja także na tablecie i telefonie, w sposób wizualny i dźwiękowy, poprzez wybraną melodię z pamięci talefonu/tableta. Ostatnio dołożyłem dodatkowo obsługę przekaźników głosowo poprzez Google Assistant. Jeśli kogoś bliżej interesuje ta kwestia to zapraszam tutaj. Kolejną miłą opcją jest podpięty do tabletu poprzez bluetooth wzmacniacz klasy D z pilotem spięty z głośnikami zainstalowanymi w kuchni. Google assistant pozwala na bezdotykowe, głosowe włączanie ulubionego przeboju z YT, radia itp. Co do przekażnika czasowego i krańcówek i przeakźników to mają one zastosowanie przy sterowaniu bramą garażową i oświetleniem garażu. Bramą garażową steruję z przycisku naściennego, pilotów radiowych, telefonu, tabletu. Jednak dodatkowo jest założony przekaźnik czasowy który po uplywie zadanego czasu zamyka otwartą bramę niezależnie od systemu. Również włącznik naścienny jest niezależny, więc nawet jak by sytem padł(co się nie zdażyło za ostatnie12 miesięcy) mogę sterować bramą. Krańcowki zainstalowane przy bramie podają informację do Aruduino, a te wysyła info do Blynka który informuje mnie notyfikacją, gdziekolwiek bym nie był, że właśnie została otwarta lub zamknięta brama garażowa. Kolejną sprawą jest automatyka oświetlenia garażu. Zrobiona "analogowo" tzn. jesli świeci się lampa z czujnikiem ruchu przed garażem, a brama jest calkowicie otwarta, zapala się całe oświetlenie garażu. Co znakomicie ułatwia nocne parkowanie. Chyba tyle. Aktualnie pracuję nad stroną internetową(dziękuję wszystkim cierpliwym webmasterom z Forbota za pomoc) postawioną na RPi, która będzie pokazywać światu jakie to fajne powietrze mamy w Kryrach;-). W planach też pomiar siły i kierunku wiatru. Gdyby ktoś chciał poczytać o początkach tego projektu to tutaj, a cały worklog jest tu. Cały sketch sterujący arduino i Blynkiem tutaj. Teraz kilka zdjęć i filmów. Wnętrze szafki z Arduino i całym osprzętem, płytka główna przed i po tuningu: "Centrum": Kilka slajdów z BLYNK-a: Wzmacniacz i głośniki w kuchni: Jeden film mówi więcej niż tysiąc słów więc: Prototyp: Aktualny: LCD dodatkowy: Jeszcze LCD z dołożoną obsługą PMS5003: Działanie BLYNK-a i przekaźników: BLYNK, "centrum", google assistant: Google Assistant w praktyce: Dziękuję za poświęcony czas i pozdrawiam wszystkich. Lajkujcie, subskrybujcie bądzcie moimi followersami i co się tam jeszcze robi
  6. 4 punkty
    Witajcie! Mam przyjemność przedstawić mojego "nowego" robota minisumo - Predator. Stworzyłem go mianowicie w II Liceum (2016-17) ale jako, że jest teraz czas sesji to jako wzorowy student postanowiłem go tutaj opisać. Koncepcja, projektowanie konstrukcji i późniejsze jej wykonanie oraz zaprogramowanie układu sterującego zajęło do 6 miesięcy. Robot wziął udział w swoim pierwszym turnieju na Cyberbocie w Poznaniu, po czym przeszedł na urlop aż do obecnego roku gdzie wziął udział w Robotic Arenie we Wrocławiu. Mechanika: Konstrukcja składa się głównie z laminatu i stali. Podwozie robota zbudowane jest z 2 płyt 10x10 (z docięciami na koła i śruby) o szer. 2mm i 3mm jedna na drugiej, przy czym na końcu ostatniej zamocowane jest ostrze kupione jako "nóż do strugarek HSS". Reszta obudowy to laminat zlutowany ze sobą i połączony śrubami z elektroniką i podwoziem. Obudowa pomalowana czarnym sprayem (głównie dla estetyki, ale też aby był cięższy do wykrycia). Napęd w robocie stanowią 2 silniki micro Pololu 50:1 a sterują nimi 2 mostki H TB6612. Felgi zostały zrobione na zamówienie przez użytkownika HungryDevil, a opony są odlane z silikonu. Sam projekt był tworzony w Inventorze, niestety wraz z rozpoczęciem studiów zmieniłem komputer, a stary był sformatowany Elektronika: Płytki PCB zaprojektowane zostały w programie Eagle i wytrawione domowymi sposobami. Sercem robota jest Atmega328P w wersji SMD. Układy zasilane są z LiPo'la 2s, 20c, 3,85Wh przez stabilizator liniowy 5V 7805. Czujnikami zastosowanymi do wykrywania przeciwnika są legendarne cyfrowe czujniki Sharp 40cm w liczbie czterech. Planowane były również czujniki linii, jednak płytki z czujnikami nie działały przed zawodami tak jak powinny także usunąłem je na czas zawodów Cyberbot. Ich implementacja miała się odbyć tuż po zawodach, ale z dzisiejszej perspektywy po 2 latach mogę powiedzieć, że raczej Predator się ich już nie doczeka Na górnej płytce, oprócz wejścia programatora i wejścia na moduł startowy, znajdują się przełączniki, których zadaniem miało być wyłączanie/pomijanie czujników które doznały awarii podczas walki (w praktyce nie użyte ani razu). Software: Program napisany w Atmel Studio. Raczej nie należy do zaawansowanych i w głównej mierze został wykonany metodą prób i błędów. Strategia robota to często używane tzw. "Tornado". Robot obraca się dopóki nie wykryje przeciwnika a po jego znalezieniu, rusza prosto na niego. Podsumowanie: Predator nie do końca wyszedł w taki sposób jaki miał. Posiada sporo wad, jako główną podałbym oczywiście brak czujników podłoża. Dodatkowo niedokładność konstrukcji spowodowała delikatne wystawanie przednich czujników poza obudowe. Mimo tego jestem bardzo zadowolony z niego, ponieważ jest to dopiero mój drugi robot, którego zbudowałem. Mam nadzieję, że ten post pomoże innym początkującym konstruktorom. Osiągnięcia: I miejsce na XI Robotic Arena 2019 we Wrocławiu Tu dorzucam jeszcze parę dodatkowych zdjęć: Pozdrawiam i do zobaczenia na kolejnych zawodach, Paweł
  7. 4 punkty
    Parę lat temu podczas remontu u rodziców postanowiłem usprawnić im parę rzeczy w mieszkaniu. Stworzyłem sterownik, który integrował ze sobą sterowanie oświetleniem i ogrzewaniem. Pomimo prostej obsługi nie przewidziałem tego, że moi rodzice będą bali się tego używać, myśleli że coś zepsują... niestety są na bakier z elektroniką. Tak powstała druga wersja, która jest dla nich absolutnie bezobsługowa w kwestii ustawień czy sterowania ogrzewaniem. Podstawowe cechy komunikacja Wifi integracja z Firebase zdalny dostęp z dowolnego miejsca na świecie Odtwarzanie komunikatów głosowych sterowanie za pomocą asystenta Google pilot radiowy 2,4GHz sterowanie oświetleniem sterowanie ogrzewaniem dwupunktowy pomiar temperatury detekcja otwartego okna automatyczne aktualizowanie czasu z serwera NTP łatwe dokładanie bezprzewodowych czujników, np. zalania duży i czytelny wyświetlacz Tym razem uwzględniłem obawy rodziców i w każdej chwili mam dostęp do wszystkich ustawień. Dla niecierpliwych filmik (polecam włączyć dźwięk) Główny moduł Całe sterowanie oparłem o układ ESP12. Jest to układ o bardzo dużych zasobach z wbudowanym wifi. Niestety ma on bardzo mało wyprowadzeń, dlatego aby obsłużyć wszystkie dodatkowe elementy takie jak: LCD ST7565 DS18B20 Enkoder Trzy przyciski Kilka wyjść Moduł MP3 (JQ8400-FL) RFM73 Wykorzystałem 16 bitowy ekspander na SPI MCP23S17. Wymagało to dostosowania kilku bibliotek np. LCD czy enkodera, aby zamiast pinów ESP używały MCP23S17. To było największym wyzwaniem dla mnie, aby zrealizować obsługę trzech układów SPI, gdzie dwa z nich były częściowo sterowane poprzez pierwszy czyli MCP23S17. Dodatkowo przydzielanie czasu na poszczególny element trzeba było odpowiednio rozdzielić, aby obsługa enkodera nie gubiła kroków. Zdalny dostęp Sterownik działa jako klient w sieci wifi, nie chciałem przekierowywać portów na routerze, ani stawiać dodatkowego VPNa. Wykorzystałam tutaj RealTime Database od Google czyli Firebase. Jest darmowe, względnie proste w obsłudze wprost z układu ESP, a dostęp do Firebase posiada każdy kto ma konto w Google. Wykorzystanie tego mechanizmu daje nam bardzo duże możliwości. Możemy hostować tam własną stronę, która będzie naszym frontendem, możemy wykorzystać “cloud functions”, które są praktycznie zasobami node.js, jest to bardzo fajne, bo możemy mieć własny serwer node.js w chmurze. Mając cloud functions i bazę danych, możemy z łatwością podpiąć pod to inne usługi, np. asystenta Google, który pozwoli sterować urządzeniem naturalną mową a nie nagranymi wcześniej próbkami komend. Pod nasz system możemy podpiąć również IFTTT (if this then that) i jeszcze bardziej zautomatyzować obsługę urządzenia. ESP co kilka sekund odczytuje bazę danych z Firebase i sprawdza czy stan jakiegoś elementu uległ zmianie, jeśli tak to znaczy, że zdalnie zmieniliśmy parametry pracy. W drugą stronę, jeśli to sterownik lokalnie zmieni jakąś wartość to od razu aktualizuje ją w Firebase. Asystent Google i komunikaty głosowe Rodzice swoje lata już mają i pamięć nie ta, dlatego dołożyłem system komunikatów głosowych. Pilot, który będzie zawsze pod ręką tuż obok pilota od TV, będzie (czeka na obudowę) jasno opisany i wystarczy nacisnąć jeden przycisk a sterownik powie jaka jest temperatura w mieszkaniu, jaka jest na zewnątrz, czy ogrzewanie aktualnie pracuje albo ile razy dzisiaj było włączane. Pomyślałem, że fajnym dodatkiem będzie wgranie również instrukcji obsługi, gdzie sterownik powie do czego służą poszczególne przyciski. Ze sterownikiem można rozmawiać w sposób naturalny z wykorzystaniem asystenta Google, nie musimy być nawet w pobliżu sterownika, mówimy do telefonu czy komputera znajdując się w dowolnym miejscu na świecie. Sam Google asystent nie steruje bezpośrednio urządzeniem, z wykorzystaniem mechanizmu Dialogflow, który bazuje na machine learning zmienia parametry pracy w Firebase, a urządzenie docelowe pobiera je i wykonuje. Zależności między komponentami pokazałem na poniższej grafice. Moduł radiowy 2,4GHz W sterowniku znajduje się RFM73, jest to niewielki układ radiowy wykorzystany tutaj jako pilot, we wcześniejszej wersji tradycyjny pilot na podczerwień kiepsko się sprawdzał, nie zawsze było się w polu widzenia odbiornika. Dodatkowe czujniki Posiadając na pokładzie układ RFM73 możemy dowolnie rozbudować system o dodatkowe czujniki, np. czujnik zalania w łazience, czujnik oświetlenia czy dodatkowe czujniki temperatury. Dokładanie dodatkowych czujników nie wiąże się z przeprogramowaniem głównego sterownika, ponieważ wszystkie reguły obsługi możemy umieścić w Firebase, a sterownik je pobierze. Poniżej kilka fotek, a jako trzecią rękę polecam taki stojak:
  8. 4 punkty
    Witam serdecznie, Chciałbym zaprezentować mój projekt którym jest czworonożny robot kroczący sterowany za pomocą kolna arduino - nano V3. Głównym celem powstania tej konstrukcji było zabicie wolnego czasu oraz wykorzystanie nowo zamówionych części. Cały proces tworzenia od koncepcji do gotowego czworonoga trwał poniżej tygodnia. Funkcjonalność robota skupiała się na chodzeniu do przodu oraz pokonywaniu małych przeszkód. Elektronika Do stworzenia projektu potrzebny był kontroler - wspomniane już wcześniej arduino nano lub jego klon. W mojej opinii jest to najbardziej użyteczne arduino do projektów DIY, ze względu na jego małą wielkość i masę oraz identyczne możliwości obliczeniowe jak jego więksi bracia. Arduino zostało zamontowane na płytce rozszerzającej z wieloma wyprowadzeniami dla serw i nie tylko. Ten element jest bardzo uniwersalny i ułatwia podłączenie wielu komponentów bez potrzeby tworzenia odpowiedniej płytki PCB lub używania płytki stykowej. Motoryka została oparta o małe serwomechanizmy - po dwa na nogę, łącznie 8 sztuk. Dodatkowo na końcach nóg zostały zamontowane czujniki krańcowe w celu wykrywania kolizji z podłożem i optymalizacji ruchu. Siła serwomechanizmów okazała się być wystarczająca, jednakże, problemem okazało się być zasilanie. Duża ilość serwomechanizmów działających jednocześnie mocno obciąża arduino, dlatego też, serwomechanizmy powinny mieć własne źródło zasilania. W tym przypadku ograniczenie prędkości ruchów ograniczyło ten problem, ale wskazuje to na popełniony przy projektowaniu błąd. Konstrukcja Konstrukcja składa się z korpusu głównego do którego przymocowano arduino oraz 4 nóg. Jedna noga składa się z dwóch segmentów, a jeden segment z dwóch elementów łączonych śrubą. Lepiej wyjaśni to poniższe zdjęcie. Robot jest tu przedstawiony leżący na swoich plecach. Poniżej znajdują się jeszcze dwa zdjęcia pokazujące jego posturę. W pozycji leżącej, ze wszystkimi nogami skierowanymi względem siebie pod kątem prostym, robot ma przekątną około 30 cm. Powyższe elementy zostały wydrukowane przy pomocy drukarki 3D. Trwało to około 10 godzin. Kod Ze względu na krótki czas rozwoju projektu, jego funkcjonalność nie jest duża. Postało kilka wersji programu, dopasowanych do konkretnego podłoża. Nie różnią się one znacząco, więc przedstawię główny program służący do pokonywania płaskiego terenu w najszybszy i najstabilniejszy sposób. Na początek trochę definicji. Zmienne nazwane są od numeru nogi oraz jej stopnia. Przykładowo tl1 - top-left-1, oraz br2 - bottom-right-2. #include <Servo.h> Servo tr1; Servo tr2; Servo tl1; Servo tl2; Servo br1; Servo br2; Servo bl1; Servo bl2; #define br A1 #define tr A2 #define tl A3 #define bl A4 int i=0; int o=50; int p=20; int h=70; int t=10; int k=0; int l=0; int n=0; int m=0; int timer=0; int d=0; int x=50; int y=50; void setup() { Serial.begin(9600); pinMode(A1, INPUT_PULLUP); pinMode(A2, INPUT_PULLUP); pinMode(A3, INPUT_PULLUP); pinMode(A4, INPUT_PULLUP); tl1.attach(8); tl2.attach(4); bl1.attach(9); bl2.attach(5); tr1.attach(10); tr2.attach(6); br1.attach(11); br2.attach(7); tr1.write(90); tr2.write(75); tl1.write(90); tl2.write(90); br1.write(90); br2.write(90); bl1.write(90); bl2.write(90); } Kolejnym elementem kodu są definicje funkcji. void ltl(int a, int b){ tl1.write(map(a+3, 0, 100, 10, 150)); tl2.write(map(b, 100, 0, 5, 180)); } void ltr(int a, int b){ tr1.write(map(a, 100, 0, 30, 170)); tr2.write(map(b-9, 0, 100, 0, 177)); } void lbl(int a, int b){ bl1.write(map(a, 0, 100, 30, 150)); bl2.write(map(b+1, 0, 100, 0, 178)); } void lbr(int a, int b){ br1.write(map(a, 100, 0, 30, 150)); br2.write(map(b+4, 100, 0, 8, 180)); } void move(){ lbr(100-i,100-k-d); i++; if(i==100){ i=0; k=y; } if(k>0){ if(digitalRead(br)==HIGH){ k--; } } lbl(100-o,100-l-d); o++; if(o==100){ o=0; l=y; } if(l>0){ if(digitalRead(bl)==HIGH){ l--; } } ltr(100-p,100-n-d); p++; if(p==100){ p=0; n=x; l=l+10; k=k+10; } if(n>0){ if(digitalRead(tr)==HIGH){ n--; } } ltl(100-h,100-m-d);; h++; if(h==100){ h=0; m=x; k=k+10; l=l+10; } if(m>0){ if(digitalRead(tl)==HIGH){ m--; } } delay(t); } Przykładowo, nazwa funkcji ltl oznacza leg-top-left i służy do ujednolicenia określania położenia nogi, gdyż niektóre serwa położone są przeciwnie i wysoka wartość sygnału PWM oznacza dla nich przeciwne położenia. Funkcja move to gówna funkcja służąca do poruszania się. Działa ona tak, że wszystkie nogi poruszają się cały czas do tyłu, jednakże, początkowe położenia wszystkich nóg są różne. Gdy noga poruszając się do tyłu dojdzie do płożenia końcowego, podnosi się ona i przemieszcza do maksymalnego płożenia do przodu, wtedy zbliża się ona do podłoża aż do napotkania oporu odebranego przez czujnik krańcowy lub osiągnięcia pozycji maksymalnej, wtedy porusza się znów do tyłu. W ten sposób wszystkie nogi cały czas znajdują się w ruchu, który jest bardzo płynny. Brak 3 stopnia swobody w nodze wpływa jednak na to, że ślizganie jest nieuniknione. Ostatnia część kodu służy jedynie do egzekwowania funkcji move pod pewnymi warunkami. void loop() { if(analogRead(br)==LOW or analogRead(tr)==LOW or analogRead(bl)==LOW or analogRead(tl)==LOW && timer<50){ timer=200; } timer--; if(timer>0){ move(); }else{ lbl(50,50); lbr(50,50); ltl(50,50); ltr(50,50); } } Kod w funkcji loop powoduje również, że w razie podniesienia robota na pewien czas, przestaje on dalej iść. Gdy robot zostanie podniesiony, żaden czujnik krańcowy nie sygnalizuje, że stoi na ziemi, powoduje to spadek licznika timer do 0 i przejście robota w stan spoczynkowy, aż do aktywacji przez ponowne wciśnięcie któregoś czujnika. Gotowy robot Poniżej przedstawiam kilka zdjęć z postępu składania konstrukcji. Niestety nie posiadam dużo zdjęć tego projektu, gdyż serwa i mikrokontrolery szybko zmieniają u mnie właściciela. Podczas testów robot pokonał najwyższy próg o wysokości nieco ponad 4 cm. Może nie jest to imponująca wartość, ale biorąc pod uwagę, że nie może on biegać ani skakać, a maksymalna wysokość własna na jakiej znajduje się jego korpus wynosi około 4,5 cm jest to taki sam wyczyn jak pokonanie przez człowieka, z marszu, przeszkody sięgającej mu do pasa. A tu jeszcze jedno zdjęcie gotowego projektu (słaba jakość, klatka z filmu). Pozdrawiam, i czekam na pytania i porady.
  9. 4 punkty
    Dzień dobry Nie będę zupełnie oryginalny i zrobiłem dwie stacje meteo. Chciałem poduczyć się trochę arduino i szukałem pomysłu na projekt przeglądając Botland znalazłem czujniki pyłu PM 2,5 i PM 10. Przez to, że temat smogu jest na czasie uznałem to za dobry pomysł by zweryfikować czy miejscowość w której mieszkam (wieś) jest od niego wolna. Zacząłem od kursów na Forbocie (vel wyświetlacz lcd) by załapać podstawy arduino i elektroniki. Były bardzo pomocne. Następnie chciałem przetestować niektóre czujniki i tak sprawdziłem Temperatura DS18B20 MCP9808 SHT 15 SHT 31 Wilgotność DHT 11 DHT 22 SHT 15 SHT 31 Przy wyborze też między innymi kierowałem się dokładnością pomiaru najlepiej ok 5% oraz możliwością pracy przy ujemnych temperaturach im mniej tym lepiej gdyż mrozy tu mogą sięgać -20C. Przy testowaniu tylko miałem problem z DHT11 i DHT22 - mianowicie nie podawał mi poprawnych danych (miałem obok kupny wilgotnościomierz i dane zupełnie nie pasowały do siebie ale opisałem to w innym poście na forum). Wybrałem MCP9808 gdyż uznałem, na czujnik temperatury gdyż wg moich obserwacji najlepiej się sprawdzał w różnych warunkach i wahaniach temperatury. Osobno tak samo wybrałem czujniki wilgotności tylko do tego celu tj. SHT31. Barometr widziałem, że polecany jest BMP180 więc na nim zostałem. Z programowaniem nie było problemu ze względu na biblioteki. Czujniki pyłu były małym wyzwaniem. Brak bibliotek. I śmigają na UART Trzeba operować na przykładowych kodach i rozumieć jak lecą bity. Ponadto trzeb zwracać uwagę na to że różne modele mogą mieć różny formę ramki danych przesyłanych. Przetestowałem 3 czujniki pyłu PMS5003 PMS5003 z detekcją formaldehydu Gravity Każdy z nich miał inaczej ramkę ukształtowaną. Ponadto czujniki zasilane są napięciem 5V a linia danych 3,3V Do tego należy dokupić konwerter poziomów logicznych by zadziałało to sprawnie. Do tego zestawu dokupiłem stacje meteo z wiatromierzem. Następnie chciałem aby stacja mogła być na zewnątrz i komunikować się z odbiornikiem w środku przez nRF24L01+. Warto brać moduł z zewnętrzną anteną oraz adapterem poprawia działanie modułu. Mając już na płytce w miarę temat ogarnięty chciałem najpierw testowo zrobić mobilny czujnik pyłu aby zrobić pierwsze przymiarki do lutowania i montażu. Lutowałem na płytkach prototypowych z cienkimi kablami. Nie był to dobry pomysł ale na daną chwilę dało się. Zamiast przylutowywać moduły na stałe lutowałem sloty do nich. Wolałem trochę poświęcić jakość wykonania na rzecz sytuacji w której mógłbym się pomylić. Nie mam rozlutotwnicy a standardowa lutownica i odsysacz słabo mi się sprawdzały. Specyfikacja mobilnego czujnika pyłu (główne moduły) Arduino Pro Mini 328 - 5V/16MHz SHT 15 (wilgotność i temperatura) PMS5003 (czujnik pyłu) Konwerter USB-UART FTDI FT232RL - gniazdo miniUSB (programator ew zasilanie poza bateryjne) Wyświetlacz LCD 4x20 znaków zielony Efekt był zadowalający wiec uznałem, że warto będzie spróbować wysyłać dane na stronę www. Do tego zadania zaprzęgłem malinkę z modułem nRF24L01+. Idea była taka aby: ->pomiar stacji ->wysłanie danych drogą radiową ->odebranie przez Ras Pi ->wysłanie danych na zewnętrzny bazę danych MySQL ->pobranie danych z bazy i wyświetlenie jej na stronie bazującej na wordpresie Główny problem był z liczbami zmiennoprzecinkowymi. Gdyż malinka odbiera surowe bity i nie chce ich prze konwertować na liczbę zmiennoprzecinkową. W każdym bądź razie wszelkie próby konwersji i operacji na bitach skończyły się komunikatem ze Python nie obsługuje przesunięć bitowych dla liczb zmiennoprzecinkowych. Do zastosowań domowych wystarczy mi pomiar do drugiego miejsca po przecinku (temperatura) więc po prostu każdą zmienna która miała część ułamkową mnożę na Arduino razy 100 i jak odbiorę na malince dzielę przez 100. O ile odbiór między arduino to linijka kodu to tu przy odbiorze suchej transmisji (już przy użyciu biblioteki ...... ) trzeba było bity ręcznie składać bo Arduino wysyła jedną zmienną w dwóch bajtach trzeba było używać operatorów bitowych by te bajty złączyć w jeden. Przed montażem należało wyznaczyć miejsce dla stacji. Z tego względu do stacji mobilnej dorzuciłem adapter NRF na który mogłem po prostu zamiennie testować wersje z zewnętrzną i wewnętrzną anteną. Syngał nadawał do malinki kolejne cyfry a obok miałem telefon z teamviwerem na którym patrzałem się co na konsoli wyświetliło. Jeśli był cykl 1 2 3 .... 11 12, tzn., że w miejscu w którym stałem jak nadawano cyfry 4 5 6 7 8 9 10 sygnał nie doszedł i nie należało go brać pod uwagę do montażu docelowej stacji meteo. Wiatromierz montowałem na chwycie antenowym. Średnica rur od stacji była mniejsza więc wkładając ją uzupełniłem całość pianką montażową by nie latało. Rezultat był taki, że moduł z wbudowaną anteną za oknem 3m tracił zasięg drugi miał sygnał spokojnie do ok 10m (nawet przez ściany). Po teście wysłania danych i odebrania jednej danej na wordpresie przytępiono do montażu stacji. Jej finalna konfiguracja to Arduino Pro Mini 328 - 5V/16MHz Konwerter USB-UART FTDI FT232RL - gniazdo miniUSB (programator i zasilanie) nRF24L01+ Stacja meteo dfrobot PMS5003 (czujnik pyłu) Gravity (czujnik pyłu) MCP9808 (temperatura) SHT 31 (wigotność) BMP180 (ciśnienie) Dorzuciłem dwa czujniki pyły by porównać ich działanie. Jako końcowy element została mi kwestia wyświetlenia danych na stronie www. Dzięki wtyczce do pisania skryptów php w wordpresie udało napisać moduł pobierania danych z bazy i jego wyświetlania. Wykresy były większym problemem. Darmowe wtyczki do wordpressa nie chcą pobierać danych z sql i są ciężko edytowalne preferują prace na .csv i najlepiej jakby je ręcznie przeładowywać. Finalnie użyłem do tego celu Google Charts (nota bene na których wiele darmowych wtyczek do wordpresa bazuje). Z racji, że nie jestem web-developerem było to moje pierwsze spotkanie z php oraz javascriptem na którym Google Charsty operują. Zmuszenie tego pracy odbyło się metodą prób i błędów ale efekt jest zadowalający. Mam wykresy z 24h, tygodnia i miesiąca. Problemy Kodowanie int w arduino i malince Ze względu za na sposób kodowania liczny int w pythonie ujemne wartości temperatury np -5C na arduino pokazywały na malince wartość 65531 C. Trzeba było dopisać fragment który usuwał ten błąd. Komunikacja L01+ Moduły te są bardzo wrażliwe na zmiany napięcia. Można do nich dokupić adapter do wpięcia który głównie ma stabilizator napięcia. Nawet nie zawsze on pomaga jeśli do niego pójdzie nie wystarczające napięcie. Widać to np. jak się rusza kable to migocze LED lub jest "ciut" jaśniejszy. Wtedy anteny nie mogą się dogadać. Potrafi się zdarzyć takie kuriozum, że w takiej sytuacji antena straci własny adres. Jeśli anteny nie są w stanie się dogadać należy bezwzględnie sprawdzić pewność zasilania. Błąd wysłania danych SQL Czasami kod w malince się zawiesi w oczekiwaniu na wysłanie danych do serwera SQL. Trudno mi powiedzieć jak dobrze temu zaradzić. Występuję to przy dłuższej pracy (tydzień, miesiąc) aczkolwiek jedyne co mi przychodzi do głowy to ustawić na Raspianie auto-restart systemu co 24h. Wycinanie dziur w plastikowej obudowie Próbowałem wycinać szlifierką modelarską ale zawsze plastik się topił. Po szperaniu w necie rozwiązaniem są albo nożyki do plastiku albo wycinarka włosowa do plastiku ale nie udało mi się tego przetestować. Niestety wycięcia wyglądają mało estetycznie ale trudno. Google Charts Przesyłanie danych z PHP do JavaScript tak aby Google bylo problemem. Wykresy nie łykną dowolnego formatu danych i nie wyświetlą błędy jeśli uznają, że format im nie pasuje. Co mógłbym zrobić inaczej? Gdybym miał robić update to zamiast komunikacji radiowej do malinki wydaje mi się bardziej rozsądne pójście w wyposażenie arduino w WIFI i wysyłanie bezpośrednio na serwer. Nie uczyniłem tak ze względu na to pisanie kodu komunikacji Arduino WIFI nie jest to pare linijek. Plus też kod do wysyłania do SQL który znalazłem też pewnie by zawierał trochę kodu. Więcej dłubania ale efekt powinien być bardziej zadowalający. Plany na przyszłość Jedyne co chce na dniach zrobić to poduczyć się Eagla i przenieść całość na płytkę drukowaną i zamówić ją np. JLCPCB. Wtedy wypnę moduły z tamtych płytek i przypnę je do zamówionej. Reasumując Stacja działa już ok 6 miesięcy. Bez problemowo. Jak widać na wykresie z ostatniego miesiąca (tj. 26.12.2018 - 25.12.2019), że normy dla smogu w mojej wsi były w paru dniach przekroczone (a mieszkam w centrum pomorskiego bez kopalni, ciężkiego przemysłu wokół). Gdyby dobrze opisać każdy etap pracy można by z tego zrobić osobny kurs Poniżej kody źródłowe Arduino Ras PI Wordpress pobieranie suchych danych Wordpress pobieranie danych i osadzenie ich na Google Charts //Autor Bartosz Jakusz. Można wykorzystywać komercyjnie. Proszę tylko podać autora kodu #include <math.h> #include <Wire.h> #include <Arduino.h> #include <Adafruit_BMP085.h> #include <SoftwareSerial.h> #include <SPI.h> #include <nRF24L01.h> #include <RF24.h> #include "Adafruit_SHT31.h" #include "Adafruit_MCP9808.h"7 #define dataSize 16 #define PMS5003BufferSize 40 #define dustGravityBufferSize 32 Adafruit_BMP085 bmp; Adafruit_MCP9808 tempsensor = Adafruit_MCP9808(); Adafruit_SHT31 sht31 = Adafruit_SHT31(); // software serial #2: RX = digital pin 8, TX = digital pin 9 SoftwareSerial Serial1(4, 5); SoftwareSerial Serial3(8, 9); SoftwareSerial Serial2(3, 2); RF24 radio(10, 14); // CE, CSN const byte address[6] = "00001"; char col; unsigned int dustSensor25 = 0,dustSensor10 = 0; unsigned int PMS = 0,PM10 = 0, formalin = 0,CR1 = 0,CR2 = 0; float PMSTemp = 0, PMSHigro = 0; bool readErr = false, readErr25 = false; int dustSensorReadError = 0; unsigned int higherBit = 0, LowerBit = 0; unsigned char buffer_RTT[40]={}; //Serial buffer; Received Data char meteoDatabuffer[35]; int measurments [dataSize]; void readPMS(); void readPM25(); void readDustSensorUARTData(SoftwareSerial S, unsigned char *bufferData, int bufferSize); unsigned int mergeBitsToInt(unsigned char *bufferData, int startBit, int stopBit); void clearBuffer(unsigned char *bufferData); void calculateControlSum(unsigned int &CR1Var, unsigned int &CR2Var, unsigned char *bufferData, int bufferSize); void encageDustSensorReadError(); void printUARTRecievedData(); void readMeteoStation(); int transCharToInt(char *_buffer,int _start,int _stop); //char to int) void sendData(); void printSendedData(); void printDataBySensor(); void setup() { Serial.begin(115000); Serial.println("start"); Serial1.begin(9600); Serial1.setTimeout(1500); delay(2000); Serial2.begin(9600); Serial2.setTimeout(1500); delay(1000); Serial3.begin(9600); Serial3.setTimeout(1500); radio.begin(); radio.setPALevel(RF24_PA_MAX); radio.setDataRate(RF24_1MBPS); radio.setChannel(0x76); radio.openWritingPipe(0xF0F0F0F0E1LL); radio.enableDynamicPayloads(); radio.stopListening(); if (!bmp.begin()) { while (1) {} } if (!tempsensor.begin()) { while (1); } if (! sht31.begin(0x44)) { // Set to 0x45 for alternate i2c addr while (1) delay(1); } Serial.println("Zakończony Init"); delay(2000); } void loop() { readPMS(); readPM25(); readMeteoStation(); printDataBySensor(); if(readErr == false && readErr25 == false) { //Serial.println("Wysyłam dane"); sendData(); //Serial.print("Wysłano dane: "); } delay(1000); } void readPMS() { Serial.println("Czujnik pm5003"); readDustSensorUARTData(Serial1, buffer_RTT, PMS5003BufferSize); calculateControlSum(CR1, CR2, buffer_RTT, PMS5003BufferSize); if(CR1 == CR2 && CR1 != 0) //Check { PMS = mergeBitsToInt(buffer_RTT, 12, 13); PM10 = mergeBitsToInt(buffer_RTT, 14, 15); formalin = mergeBitsToInt(buffer_RTT, 28, 29); PMSTemp = mergeBitsToInt(buffer_RTT, 30, 31) / 10; PMSHigro = mergeBitsToInt(buffer_RTT, 32, 33) / 10; readErr = false; } else { PMS = 4321; PM10 = 4321; formalin = 4321; encageDustSensorReadError(); } clearBuffer(buffer_RTT); } void readPM25() { Serial.println("Czujnik p, 25"); readDustSensorUARTData(Serial3, buffer_RTT, dustGravityBufferSize); calculateControlSum(CR1, CR2, buffer_RTT, dustGravityBufferSize); if(CR1 == CR2 && CR1 != 0) //Check { dustSensor25 = mergeBitsToInt(buffer_RTT, 6, 7); dustSensor10 = mergeBitsToInt(buffer_RTT, 8, 9); readErr25 = false; } else { dustSensor25 = 4321; dustSensor10 = 4321; encageDustSensorReadError(); } clearBuffer(buffer_RTT); } void readDustSensorUARTData(SoftwareSerial S, unsigned char *bufferData, int bufferSize) { S.listen(); while(!S.available()); while(S.available()>0) //Data check: weather there is any Data in Serial1 { col =S.read(); delay(2); if (col == 0x42) { bufferData[0]=(char)col; col =S.read(); delay(2); if (col == 0x4d) { bufferData[1]=(char)col; for(int i = 2; i < bufferSize; i++) { col =S.read(); bufferData[i]=(char)col; delay(2); } break; } } S.flush(); } } unsigned int mergeBitsToInt(unsigned char *bufferData, int startBit, int stopBit) { higherBit = buffer_RTT[startBit]; //Read PM2.5 High 8-bit LowerBit = buffer_RTT[stopBit]; //Read PM2.5 Low 8-bit return (higherBit << 8) + LowerBit; } void clearBuffer(unsigned char *bufferData) { for(int i = 0; i < 40; i++) { bufferData[i] = 0; } delay(100); } void calculateControlSum(unsigned int &CR1Var, unsigned int &CR2Var, unsigned char *bufferData, int bufferSize) { CR1Var = 0; CR2Var = 0; CR1Var =(buffer_RTT[bufferSize - 2]<<8) + buffer_RTT[bufferSize - 1]; for(int i = 0; i < bufferSize - 2; i++) { CR2Var += bufferData[i]; } } void encageDustSensorReadError() { dustSensorReadError = dustSensorReadError + 1; readErr = true; readErr25 = true; Serial.print("Błąd odczytu: "); Serial.println(dustSensorReadError); //printUARTRecievedData(); } void readMeteoStation() { Serial.println("Stacja meteo nasłuchuje"); Serial2.listen(); while(!Serial2.available()); while(Serial2.available()>0) //Data check: weather there is any Data in Serial1 { Serial.println("Start meteo"); for (int index = 0;index < 35;index ++) { if(Serial2.available()) { meteoDatabuffer[index] = Serial2.read(); if (meteoDatabuffer[0] != 'c') { //Serial.println("Transmisja w toku meteo"); index = -1; } } else { index --; } } } Serial.println("Koniec"); } int transCharToInt(char *_buffer,int _start,int _stop) { //char to int) int result = 0; int num = _stop - _start + 1; int tempArr[num]; for (int indx = _start; indx <= _stop; indx++) { tempArr[indx - _start] = _buffer[indx] - '0'; result = 10 * result + tempArr[indx - _start]; } return result; } void sendData(){ for(int z = 0; z < dataSize; z++) { measurments[z] = -1000; } //Meteo station measurments[0] = (int)(transCharToInt(meteoDatabuffer,1,3)); //wind directoin measurments[1] = (int)(transCharToInt(meteoDatabuffer,28,32) / 10.00); //pressure //measurments[2] = (int)(((transCharToInt(meteoDatabuffer,13,15) - 32.00) * 5.00 / 9.00) * 100); //temp *C //measurments[3] = (int)((transCharToInt(meteoDatabuffer,25,26)) * 100); //higro %RH measurments[2] = (int)((transCharToInt(meteoDatabuffer,5,7) / 0.44704) * 100); //avg wind speed 1 min (m/s) measurments[3] = (int)((transCharToInt(meteoDatabuffer,9,11) / 0.44704) * 100); //max wind speed 5min (m/s) measurments[4] = (int)((transCharToInt(meteoDatabuffer, 17, 19) * 25.40 * 0.01) * 100); // rain 1 hour (mm) measurments[5] = (int)((transCharToInt(meteoDatabuffer, 21, 23) * 25.40 * 0.01) * 100); // rain 24 hour (mm) //MCP9008 measurments[6] = (int)(tempsensor.readTempC() * 100); //SHT31 measurments[7] = (int)(sht31.readHumidity() * 100); measurments[8] = (int) (sht31.readTemperature()* 100); //PMS5003 //measurments[11] = PMSTemp * 100; //measurments[12] = PMSHigro * 100; measurments[9] = PMS; measurments[10] = PM10; measurments[11] = formalin; //PM2,5 Gravity measurments[12] = dustSensor25; measurments[13] = dustSensor10; //BMP180 measurments[14] = bmp.readPressure() / 100; //measurments[19] = (int) (bmp.readTemperature() * 100); measurments[15] = 666; int testMe = 0; for(int k = 0; k < 15; k++) { testMe += measurments[k]; } Serial.print("PRZYKLADOWA CHECK SUMA: "); Serial.println(testMe); radio.write(&measurments, sizeof(measurments)); printSendedData(); } void printSendedData() { Serial.println("Wysłano następujące dane:"); for (int i = 0; i < dataSize; i++) { Serial.print(measurments[i]); Serial.print(" "); } Serial.println(); } void printUARTRecievedData() { byte b; for(int i=0;i<32;i++) { b = (byte)buffer_RTT[i]; //buffer_RTT[i] = 0; Serial.print(b, HEX); Serial.print(" "); } Serial.print("CR1: "); Serial.print(CR1); Serial.print(" = "); Serial.println(CR2); } void printDataBySensor() { Serial.println(); Serial.println("MCP9808: "); Serial.print("Temperatura: "); float c = tempsensor.readTempC(); Serial.print(c); Serial.println("*C"); Serial.println(); Serial.println("SHT31: "); Serial.print("Wilgotność: "); Serial.print(sht31.readHumidity()); Serial.println("%RH"); Serial.print("Temperatura: "); Serial.print(sht31.readTemperature()); Serial.println("*C"); Serial.println(); Serial.println("BMP180: "); Serial.print("Temperatura: "); float bmpTemp = bmp.readTemperature(); Serial.print(bmpTemp); Serial.println("*C"); Serial.print("Ciśnienie: "); Serial.print(bmp.readPressure() / 100); Serial.println("hPa"); Serial.print("Teoretyczna wysokość: "); Serial.print(bmp.readAltitude(101100)); Serial.println("m.n.p.m"); Serial.println(); Serial.println("PMS5003:"); Serial.print("PM 2,5: "); Serial.print(PMS); Serial.println(" /25 ug/m3 "); Serial.print("PM 10 : "); Serial.print(PM10); Serial.println(" /50 ug/m3 "); Serial.print("Formaldehyd : "); Serial.print(formalin); Serial.println(" ug/m3 "); Serial.print("Temperatura: "); Serial.print(PMSTemp); Serial.println("*C"); Serial.print("Wilgotność: "); Serial.print(PMSHigro); Serial.println("%RH"); Serial.println(); Serial.println("PM 2,5 GRAVITY:"); Serial.print("PM 2,5: "); Serial.print(dustSensor25); Serial.println(" /25 ug/m3 "); Serial.print("PM 10 : "); Serial.print(dustSensor10); Serial.println(" /50 ug/m3 "); Serial.println(); Serial.println("STACJA METEO: "); Serial.print("Temperatura: "); c = (transCharToInt(meteoDatabuffer,13,15) - 32.00) * 5.00 / 9.00; Serial.print(c); Serial.println("*C"); Serial.print("Wilgotnosc: "); float metHigro = transCharToInt(meteoDatabuffer,25,26); Serial.print(metHigro); Serial.println("%RH"); Serial.print("Cisnienie: "); float pp = transCharToInt(meteoDatabuffer,28,32) / 10.00; Serial.print(pp); Serial.println("hPa"); Serial.print("Predkośc wiatru: "); float w = transCharToInt(meteoDatabuffer,5,7) / 0.44704; Serial.print(w); Serial.println("m/s"); Serial.print(" "); w = w * 3.6; Serial.print(w); Serial.println("km/h"); Serial.print("Max Predkośc wiatru: "); float m = transCharToInt(meteoDatabuffer,9,11) / 0.44704; Serial.print(m); Serial.println("m/s (ostatnie 5 minut)"); Serial.print(" "); m = m * 3.6; Serial.print(m); Serial.println("km/h (ostatnie 5 minut)"); Serial.print("Kierunek wiatru: "); Serial.print(transCharToInt(meteoDatabuffer,1,3)); Serial.println("C"); Serial.print("Opad (1h): "); float rain1 = transCharToInt(meteoDatabuffer, 17, 19) * 25.40 * 0.01; Serial.print(rain1); Serial.println("mm"); Serial.print("Opad (24h): "); float rain24 = transCharToInt(meteoDatabuffer, 21, 23) * 25.40 * 0.01; Serial.print(rain24); Serial.println("mm"); Serial.println(); } #Autor Bartosz Jakusz. Można replikować nawet w celach komercyjnych. Tylko umieścić autora kodu. import RPi.GPIO as GPIO from lib_nrf24 import NRF24 import time import spidev import struct import os import mysql.connector from mysql.connector import errorcode from datetime import date, datetime, timedelta GPIO.setmode(GPIO.BCM) pipes = [[0xE8, 0xE8, 0xF0, 0xF0, 0xE1], [0xF0, 0xF0, 0xF0, 0xF0, 0xE1]] radio = NRF24(GPIO, spidev.SpiDev()) radio.begin(0, 17) radio.setPayloadSize(32) radio.setChannel(0x76) radio.setDataRate(NRF24.BR_1MBPS) radio.setPALevel(NRF24.PA_MAX) radio.setAutoAck(True) radio.enableDynamicPayloads() radio.enableAckPayload() i = 0 j = 0 sucRate = 0 errRate = 0 totalRec = 0 try: while True: radio.openReadingPipe(1, pipes[1]) #radio.printDetails() radio.startListening() print(" ") print(datetime.now()) radio.print_address_register("RX_ADDR_P0-1", NRF24.RX_ADDR_P0, 2) radio.print_address_register("TX_ADDR", NRF24.TX_ADDR) j = 0 while not radio.available(0): j = j + 1 receivedMessage = [] radio.read(receivedMessage, radio.getDynamicPayloadSize()) radio.stopListening() print("Received: {}".format(receivedMessage)) string = "" floatBytes = [] indx = 0 while indx < 4: floatBytes.append(0) indx = indx + 1 measurments = [] indx = 0 mIndx = 0 tmpStr= "" tmpIndx = 0 crc = 0 if not receivedMessage: print("pusto wywalam sie") else: for n in receivedMessage: #print(tmpIndx) tmpIndx = tmpIndx + 1 floatBytes.insert(indx, n) if(indx >= 1): b1 = floatBytes[0] b2 = floatBytes[1] host = 0 host = b2 host = host << 8 host = host | b1 if(mIndx == 6): print("trt {}".format(host)) if(host > 65536 / 2): host = (65536 - host) * (-1) print("trti {}".format(host)) tmpVar = host / 100 print("trtf {}".format(tmpVar)) elif(mIndx >= 2 and mIndx < 5 or mIndx >= 7 and mIndx <= 8): tmpVar = host / 100 else: tmpVar = host indx = 0 measurments.append(tmpVar) mIndx = mIndx + 1 else: indx = indx + 1 totalRec = totalRec + 1 print(" ") if(host == 666): sucRate = sucRate + 1 print("Odebrano z sukcesem {}/{} transmisji".format(sucRate, totalRec)) try: print("Zaczynamy łacznie z baza") cnx = mysql.connector.connect(host='00.00.00.00',database='db') cursor = cnx.cursor() print("Połączono z baza") #zapytania SQL add_probeTime = ("INSERT INTO MeasrumentDateTime" "(time, date)" "VALUES (%s, %s)") add_MCP8006 = ("INSERT INTO MCP8006 " "(temperature, MeasrumentDateTime_ID) " "VALUES (%(temperature)s, %(timeID)s)") add_SHT31 = ("INSERT INTO SHT31 " "(humidity, temperature, MeasrumentDateTime_ID) " "VALUES (%(humidity)s ,%(temperature)s, %(timeID)s)") add_BMP180 = ("INSERT INTO BMP180" "(pressure, MeasrumentDateTime_ID) " "VALUES (%(pressure)s, %(timeID)s)") add_PMS5003 = ("INSERT INTO PMS5003" "(pm25, pm10, formaldehyd, MeasrumentDateTime_ID) " "VALUES (%(pm25)s, %(pm10)s, %(formaldehyd)s,%(timeID)s)") add_PMGravity = ("INSERT INTO PM25" "(PM25, PM10, MeasrumentDateTime_ID) " "VALUES (%(pm25)s, %(pm10)s, %(timeID)s)") add_meteoSation = ("INSERT INTO MeteoStation" "(windDirection, avgWindSpeedPerMinute, avgWindSpeedPerFiveMinutes, rainfallOneHour, rainfal24Hours, pressure, MeasrumentDateTime_ID) " "VALUES (%(windDir)s, %(avgWind)s, %(maxWind)s, %(rain1h)s, %(rain24h)s, %(pressure)s, %(timeID)s)") #Dodaj obecna godzine currentTimeAndDate = datetime.now() formatted_time = currentTimeAndDate.strftime('%H:%M:%S') currentDate = datetime.now().date() toSendSampleDate = (formatted_time, currentDate) cursor.execute(add_probeTime, toSendSampleDate) sampleTimeID = cursor.lastrowid #Wyslij do bazy MySql dane czujnik temp MCP8006Data = { 'timeID' : sampleTimeID, 'temperature' : measurments[6], } cursor.execute(add_MCP8006, MCP8006Data) #Wyslij do bazy MySql dane higrometr SHT 31 SHT31Data = { 'timeID' : sampleTimeID, 'humidity' : measurments[7], 'temperature' : measurments[8], } cursor.execute(add_SHT31, SHT31Data) #Wyslij do bazy MySql dane barometr BMP180 BMP180Data = { 'timeID' : sampleTimeID, 'pressure' : measurments[14], } cursor.execute(add_BMP180, BMP180Data) #Wyslij do bazy MySql dane czujnik pylu PMS5003 PMS5003Data = { 'timeID' : sampleTimeID, 'pm25' : measurments[9], 'pm10' : measurments[10], 'formaldehyd' : measurments[11], } cursor.execute(add_PMS5003, PMS5003Data) #Wyslij do bazy MySql dane czujnik pylu PM 2,5 Gravity PMGravityData = { 'timeID' : sampleTimeID, 'pm25' : measurments[12], 'pm10' : measurments[13], } cursor.execute(add_PMGravity, PMGravityData) #Wyslij do bazy MySql dane stacja meteo metStationData = { 'timeID' : sampleTimeID, 'windDir' : measurments[0], 'avgWind' : measurments[2], 'maxWind' : measurments[3], 'rain1h' : measurments[4], 'rain24h' : measurments[5], 'pressure' : measurments[1], } cursor.execute(add_meteoSation, metStationData) print("Wysylanie danych na serwer") cnx.commit() cursor.close() print("Wyslano do bazy pomyslnie dane") except mysql.connector.Error as err: if err.errno == errorcode.ER_ACCESS_DENIED_ERROR: print("Something is wrong with your user name or password") elif err.errno == errorcode.ER_BAD_DB_ERROR: print("Database does not exist") else: print(err) else: cnx.close() time.sleep(14) else: print("BLAD TRANSMISJI: nie zgadza sie suma kontrolna") #errRate = errRate + 1 #print("Żle odebrano {}/{} transmisji".format(errRate, totalRec)) print(" ") print("------------------------------------------") #radio.closeReadingPipe(pipes[1]) #radio.stopListening() time.sleep(1) except KeyboardInterrupt: print(i) except: # this catches ALL other exceptions including errors. # You won't get any error messages for debugging # so only use it once your code is working print ("Other error or exception occurred!") finally: GPIO.cleanup() # this ensures a clean exit echo"Autor Bartosz Jakusz. Można wykorzystywać komercyjnie. Wsakazać tylko autora kodu. "; $mydb = new wpdb('usr_db','db','localhost'); $currentTime = $mydb->get_results("SELECT time, date FROM MeasrumentDateTime ORDER BY ID DESC LIMIT 1"); $tempData = $mydb->get_results("SELECT temperature FROM MCP8006, MeasrumentDateTime WHERE MeasrumentDateTime_ID = MeasrumentDateTime.ID ORDER BY MeasrumentDateTime.ID DESC LIMIT 1"); $higroData = $mydb->get_results("SELECT humidity FROM SHT31, MeasrumentDateTime WHERE MeasrumentDateTime_ID = MeasrumentDateTime.ID ORDER BY MeasrumentDateTime.ID DESC LIMIT 1"); $pressureData = $mydb->get_results("SELECT pressure FROM BMP180, MeasrumentDateTime WHERE MeasrumentDateTime_ID = MeasrumentDateTime.ID ORDER BY MeasrumentDateTime.ID DESC LIMIT 1"); $pms5003Data = $mydb->get_results("SELECT pm25, pm10, formaldehyd FROM PMS5003, MeasrumentDateTime WHERE MeasrumentDateTime_ID = MeasrumentDateTime.ID ORDER BY MeasrumentDateTime.ID DESC LIMIT 1"); $gravityData = $mydb->get_results("SELECT PM25, PM10 FROM PM25, MeasrumentDateTime WHERE MeasrumentDateTime_ID = MeasrumentDateTime.ID ORDER BY MeasrumentDateTime.ID DESC LIMIT 1"); $metStationData = $mydb->get_results("SELECT windDirection, avgWindSpeedPerMinute, avgWindSpeedPerFiveMinutes, rainfallOneHour, rainfal24Hours FROM MeteoStation, MeasrumentDateTime WHERE MeasrumentDateTime_ID = MeasrumentDateTime.ID ORDER BY MeasrumentDateTime.ID DESC LIMIT 1"); $avgkmPerHour = $metStationData[0]->avgWindSpeedPerMinute * 3.6; $maxkmPerHour = $metStationData[0]->avgWindSpeedPerFiveMinutes * 3.6; echo "<table>"; echo"<col width=50%>"; echo"<tr>"; echo"<th>Ostatni pomiar z: </th>"; echo"<th></th>"; echo"</tr>"; echo"<tr>"; echo"<th>".$currentTime[0]->time." </th>"; echo"<th>".$currentTime[0]->date." </th>"; echo"</tr>"; echo"</table>"; echo "<br>"; echo "<br>"; $dir = $metStationData[0]->windDirection; $dirName = ""; if ($dir < 45) { $dirName = "Północny"; } elseif ($dir >= 45 or $dir < 90 ) { $dirName = "Północno-Wschodni"; } elseif ($dir >= 90 or $dir < 135 ) { $dirName = "Wschodni"; } elseif ($dir >= 135 or $dir < 180 ) { $dirName = "Południowo-Wschodni"; } elseif ($dir >= 180 or $dir < 225 ) { $dirName = "Południowy"; } elseif ($dir >= 225 or $dir < 270 ) { $dirName = "Południowo-Zachodni"; } elseif ($dir >= 270 or $dir < 315 ) { $dirName = "Zachodni"; } else { $dirName = "Północno-Zachodni"; } echo "<table>"; echo"<col width=50%>"; echo"<tr>"; echo"<th>Temperatura: </th>"; echo"<th>".$tempData[0]->temperature."*C </th>"; echo"</tr>"; echo"<tr>"; echo"<th>Wilgotność: </th>"; echo"<th>".$higroData[0]->humidity."%RH </th>"; echo"</tr>"; echo"<tr>"; echo"<th>Ciśnienie: </th>"; echo"<th>".$pressureData[0]->pressure."hPa </th>"; echo"</tr>"; echo"</table>"; echo "<br>"; echo "<br>"; echo "<table>"; echo"<col width=50%>"; echo"<tr>"; echo"<th>Czystość powietrza: </th>"; echo"<th></th>"; echo"</tr>"; echo"<tr>"; echo"<th>PM 2,5 - norma 25ug/m3: </th>"; echo"<th></th>"; echo"</tr>"; echo"<tr>"; echo"<th>PMS5003: </th>"; echo"<th>".$pms5003Data[0]->pm25."ug/m3</th>"; echo"</tr>"; echo"<tr>"; echo"<th>Gravity: </th>"; echo"<th>".$gravityData[0]->PM25."ug/m3</th>"; echo"</tr>"; echo"</tr>"; echo"<tr>"; echo"<th><br></th>"; echo"<th><br></th>"; echo"</tr>"; echo"</tr>"; echo"<tr>"; echo"<th>PM 10 - norma 50ug/m3</th>"; echo"<th></th>"; echo"</tr>"; echo"<tr>"; echo"<th>PMS5003: </th>"; echo"<th>".$pms5003Data[0]->pm10."ug/m3 </th>"; echo"</tr>"; echo"<tr>"; echo"<th>Gravity: </th>"; echo"<th>".$gravityData[0]->PM10."ug/m3 </th>"; echo"</tr>"; echo"<tr>"; echo"<th><br></th>"; echo"<th><br></th>"; echo"</tr>"; echo"</tr>"; echo"<tr>"; echo"<th>Formaldehyd</th>"; echo"<th></th>"; echo"</tr>"; echo"<tr>"; echo"<th>PMS5003: </th>"; echo"<th>".$pms5003Data[0]->formaldehyd."ug/m3 </th>"; echo"</tr>"; echo"</table>"; echo "<br>"; echo "<br>"; echo "<table>"; echo"<col width=50%>"; echo"<tr>"; echo"<th>Stacja Meteo: </th>"; echo"<th></th>"; echo"</tr>"; echo"<tr>"; echo"<th>Kierunek Wiatru: </th>"; echo"<th>".$dirName."</th>"; echo"</tr>"; echo"<tr>"; echo"<th><br></th>"; echo"<th><br></th>"; echo"</tr>"; echo"<tr>"; echo"<th>Średnia prędkość wiatru (ostatnia minuta): </th>"; echo"<th></th>"; echo"</tr>"; echo"<tr>"; echo"<th> </th>"; echo"<th>".$metStationData[0]->avgWindSpeedPerMinute."m/s</th>"; echo"</tr>"; echo"<tr>"; echo"<th> </th>"; echo"<th>".$avgkmPerHour."km/h</th>"; echo"</tr>"; echo"</tr>"; echo"<tr>"; echo"<th><br></th>"; echo"<th><br></th>"; echo"</tr>"; echo"</tr>"; echo"<tr>"; echo"<th>Max prędkość wiatru (ostatnie 5 min)</th>"; echo"<th></th>"; echo"</tr>"; echo"<tr>"; echo"<th> </th>"; echo"<th>".$metStationData[0]->avgWindSpeedPerFiveMinutes."m/s</th>"; echo"</tr>"; echo"<tr>"; echo"<th> </th>"; echo"<th>".$maxkmPerHour."km/h</th>"; echo"</tr>"; echo"<tr>"; echo"<th><br></th>"; echo"<th><br></th>"; echo"</tr>"; echo"</tr>"; echo"<tr>"; echo"<th>Opad atmosferyczny</th>"; echo"<th></th>"; echo"</tr>"; echo"<tr>"; echo"<th>Ostatnia godzina: </th>"; echo"<th>".$metStationData[0]->rainfallOneHour."mm</th>"; echo"</tr>"; echo"<tr>"; echo"<th>Ostatnia doba: </th>"; echo"<th>".$metStationData[0]->rainfal24Hours."mm </th>"; echo"</tr>"; echo"</table>"; echo"Autor Bartosz Jakusz. Można wykorzystywać komercyjnie. Wsakazać tylko autora kodu. "; $mydb = new wpdb('usr_db','db','localhost'); $pm25DataPMS5003 = $mydb->get_results("SELECT time, date, round(avg(pm25), 0) as pm25PMSMonth FROM PMS5003, MeasrumentDateTime WHERE TIMESTAMP(date, time) > NOW() - INTERVAL 1 MONTH and MeasrumentDateTime_ID = MeasrumentDateTime.ID group by DAY(TIMESTAMP(date, time)) ORDER BY MeasrumentDateTime.ID"); $pm25DataGravity = $mydb->get_results("SELECT time, date, round(avg(PM25.PM25),0) as pm25GravMonth FROM PM25, MeasrumentDateTime WHERE TIMESTAMP(date, time) > NOW() - INTERVAL 1 MONTH and MeasrumentDateTime_ID = MeasrumentDateTime.ID group by DAY(TIMESTAMP(date, time)) ORDER BY MeasrumentDateTime.ID"); $tmpPM25 = []; $i = 0; $someRandData = 25; foreach ($pm25DataGravity as $obj) : array_push($tmpPM25, array(substr((string)$obj->date,5)." ".substr((string)$obj->time,0,-9), (float)$obj->pm25GravMonth, (float)$pm25DataPMS5003[$i]->pm25PMSMonth, (float)$someRandData )); $i++; endforeach; echo"<div id='pm25ChartMonth'></div>"; ?> <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> <script type="text/javascript"> var pm25 = <?php echo json_encode($tmpPM25); ?>; google.charts.load('current', {packages: ['corechart', 'line']}); google.charts.setOnLoadCallback(drawBasic); function drawBasic() { var data = new google.visualization.DataTable(); data.addColumn('string', 'Last Hour'); data.addColumn('number', 'Gravity'); data.addColumn('number', 'PMS5003'); data.addColumn('number', 'Norma'); data.addRows(pm25); var options = { 'title':'Zaniczyszczenie PM 2,5 ostatni miesiąc', 'curveType': 'function', hAxis: { title: 'Godzina' }, vAxis: { title: 'um/m3' }, height: 500 }; var chart = new google.visualization.LineChart(document.getElementById('pm25ChartMonth')); chart.draw(data, options); } </script>
  10. 4 punkty
    Duchy z poprzedniej cyfry występują gdy rzeczy nie dzieją się w odpowiedniej kolejności, więc Twoje podejrzenie jest słuszne. Powinieneś dokonywać zmiany numeru zapalonego wyświetlacza podczas wygaszonego rejestru segmentów. Teraz sytuacja wygląda tak,: 1. Ładujesz rejestry 595 nową maską włączenia kolejnego wyświetlacza, ale jeszcze go nie zapalasz 2. Przeładowujesz ekspander obrazem nowego znaku -> nowa cyfra już pali się na starym wyświetlaczu 3. Zmieniasz wyświetlacz na kolejny na zboczu narastającym impulsu ładowania 595. A powinno być tak: 1. Ładujesz rejestry 595 nową maską włączenia kolejnego wyświetlacza, ale jeszcze go nie zapalasz. 2. Ładujesz zero do ekspandera 3. Zmieniasz wyświetlacz na kolejny na zboczu narastającym impulsu ładowania 595. 4. Przeładowujesz ekspander obrazem nowego znaku -> nowa cyfra pali się już tylko na nowym wyświetlaczu. Przydatny w tym przypadku analizator logiczny masz w głowie.
  11. 4 punkty
  12. 4 punkty
  13. 4 punkty
    Nixie - lampy od których nie można oderwać wzroku. Kiedyś podstawowy element do prezentowania danych, dzisiaj poszukiwany rarytas przez pasjonatów. Chciałem zaprezentować kilka zegarów jakie wykonałem swego czasu. Opiszę jeden konkretny, a na końcu pokażę kilka innych konstrukcji, które maja podobną elektronikę. Podstawowe funkcje Lampy LC-531 firmy DOLAM Format wyświetlania czasu HH:MM:SS Wyświetlanie daty Dwa niezależne czujniki temperatury DS18B20 Podtrzymanie bateryjne pracy układu zegara Prosta obsługa za pomocą pilota Programowany alarm z różnymi trybami powtórzeń Kilka melodyjek do wyboru jako dźwięk budzika Możliwość wł/wył zera z przodu Programowana częstotliwość „przewijania” lamp, w celu uniknięcia zatrucia katod Elektronika Zegar został oparty o mikrokontroler Atmega8 i zegar RTC PCF8563 . W urządzeniu można wyszczególnić kilka funkcjonalnych bloków: Zegar RTC przetwornica czujniki temperatury odbiornik IR multipleksowanie lamp Przetwornica oparta jest o poczciwego NE555, a jasność lamp reguluje się potencjometrem. Do zegara można podłączyć dwa czujniki temperatury, a odczytana z nich wartość będzie wyświetlana co kilkadziesiąt sekund. Do sterowania wykorzystany jest malutki pilot na podczerwień. Zegar posiada zaprogramowanych kilka melodyjek monofonicznych, które można ustawić jako dźwięk budzika. Użyłem do tego celu buzzer bez wbudowanego generatora. Fajną opcją jest tzw. “slot machine”, czyli krótkie przewijanie wszystkich cyfr, głównym celem tego zabiegu jest ochrona lamp przed zatruciem katod, zwłaszcza pierwszych lamp, gdzie cyfra nie zmienia się przez wiele godzin, szczególnie w zegarach gdzie wyświetlana jest tylko godzina. Efekt przewijania cyfr jest włączany co pięć minut i trwa 10 sekund. Jest to efekt naprawdę miły dla oka. Lampy są aktywowane za pomocą wysokonapięciowych tranzystorów z serii MMBT, a poszczególne cyfry z wykorzystaniem modułu 74141. Układ 74141 jest dekoderem kodu BCD na kod dziesiętny, który zaprojektowano specjalnie do sterowania lampami z zimną katodą. Obudowa W tym konkretnym egzemplarzu wykonana została z poczciwej zabejcowanej sosny. Całość została zamknięta pod pleksą, która była wygięta na ciepło zwykła opalarką. Płytka z elektroniką jest wyeksponowana, widać wszystkie elementy, a PCB jest zabezpieczone prostą soldermaską wykonaną z farby termoutwardzalnej. Schemat Niżej zamieszczam zdjęcia kilku innych zegarów jakie wykonałem, schemat elektroniczny jest w nich podobny.
  14. 4 punkty
    Powoli zbliżam się do listy moich ukończonych projektów, które nadają się do publikacji na forum. Ten będzie jednym z ostatnich, jeśli nie ostatnim. Oczywiście na jakiś czas, a nie zupełnie. Prawdę mówiąc początkowo miałem wątpliwości, czy w ogóle nadaje się on do publikacji. Po pierwsze nie jest to zupełnie autorskie opracowanie, po drugie nie jestem pewien, czy można nazwać go dostatecznie ambitnym. Co do pierwszej kwestii, to po przejrzeniu poprzednich wpisów doszedłem do wniosku, że większość amatorskich projektów nie jest zupełnie oryginalna - każdy szuka gdzieś inspiracji, wykorzystuje gotowe biblioteki, odtwarza na swój sposób cudze projekty. W końcu zegarów nixie i zegarów binarnych także powstało całe mnóstwo przed moim... A co do drugiej kwestii to cóż... Pozostawiam to ocenie czytelników. Źródłem inspiracji tego projektu był artykuł znaleziony w serwisie Evil Mad Scientist. Jego autorzy postanowili odtworzyć Tennis for two, czyli tytuł uważany za pierwszą grę elektroniczną w historii. Oryginalna wersja z 1958 roku pracowała na analogowym komputerze lampowym, wykorzystując oscyloskop w roli wyświetlacza. Prezentowano ją gościom amerykańskiego Broohaven National Laboratory, odwiedzającym tę placówkę badawczą w ramach dni otwartych. Rozgrywka przypomina trochę późniejszego "Ponga", z tą różnicą, że tutaj obserwujemy kort tenisowy w rzucie z boku. Każdy z dwóch graczy otrzymuje kontroler wyposażony w przycisk i potencjometr. Przycisk służy do serwowania i odbijania piłeczki, pokrętło regulują kąt odbicia. Pośrodku ekranu znajduje się również siatka. Wersja ze strony EMS była typowym "weekendowym" projektem, przygotowanym za pomocą zestawu uruchomieniowego i płytki uniwersalnej. Ja postanowiłem zbudować bardziej "finalną" wersję. Płytkę zaprojektowałem od podstaw, stosując inny mikrokontroler (Atmega32), co wymagało niewielkich zmian w kodzie. Przy okazji poprawiłem kilka bugów znalezionych w oryginalnej wersji (brakowało m.in. jednej instrukcji warunkowej, co w pewnych warunkach pozwalało na oszukiwanie przez wpływanie na zachowanie piłeczki, gdy ta znajdowała się po stronie przeciwnika). Urządzenie zostało zabudowane w kompaktowej, plastikowej obudowie. Dwie mniejsze obudowy okazały się być świetnym materiałem na kontrolery. Sygnał sterujący oscyloskopem (ustawionym w tryb XY) jest generowany przez dwa przetworniki R2R, których sygnały są wyprowadzone na dwa złącza RCA. Do podłączenia oscyloskopu potrzebna jest jeszcze przejściówka na BNC. W ramach ciekawostki mogę dodać, że gra przez kilka miesięcy była prezentowana na wystawie w krakowskim Muzeum Inżynierii Miejskiej. Wytrzymała intensywne użytkowanie przez szkolna młodzież. Obecnie można w nią zagrać odwiedzając krakowski Hackerspace.
  15. 4 punkty
    Pewnego wieczoru chciałem zrobić zabawkę na arduino. Bez wahania postanowiłem zrobić grę 2048. Zrobiłem to! Teraz chcę pokazać proces. Przed pracą opowiem ci kilka słów na temat gry. Została stworzona przez Gabriele Cirulli - 19-letniego włoskiego dewelopera. Gra została napisana w celu ćwiczenia programowania. Mimo że osiągnęła wielki sukces, facet nie kontynuował tworzenia gier. W oryginale znajduje się pole 4 * 4, na którym z prawdopodobieństwem 91% pojawia się kafelek "2" i 9% liczba "4". Celem gry jest zdobycie kafla 2048, przenoszenie wszystkiego na jedną ze stron. Nasza gra będzie wersją uproszczoną. Będzie tylko dwójka, po osiągnięciu 2048 gra się skończy i nie będziesz mógł prowadzić zapisów. To wszystko - pole dla twoich ulepszeń. Zacznijmy od składników. Sercem zabawki jest płytka arduino nano - tania mała płytka. Aby kontrolować potrzebujesz 5 przycisków. 4 wskazują kierunki boków i jeden do przeładowania. Każdy z nich potrzebuje rezystora na 10 kOm. Informaja będzie wyświetlana na ekranie calowym. Pierwszą częścią pracy jest podłączenie wyświetlacza do arduino. Znajdź adres ekranu i2c. Aby to zrobić, pobierz szkic I2C SCANNER. Podłączamy ekran zgodnie z tabelą. Płyta -> Ekran GND ->GND 5V->VCC SCL->A5 SDA->A4 Szkic jest załadowany, ekran jest podłączony. Otwórz monitor szeregowy. Tutaj widzimy adres i2c ekranu. Radzę napisać czarny znacznik z tyłu ekranu. Teraz przygotuj go do wyświetlania danych. Zainstaluj dwie biblioteki (adafruit ssd1306, adafruit gfx). W folderze z biblioteką znajduje się wspaniały plik Adafruit_SSD1306.h. Otwórz go. Tutaj musisz odkomentować linię z rozmiarem twojego ekranu. Po uporządkowaniu wyświetlacza pobierz i otwórz szkic. Kilka słów o algorytmie. Gra rozpoczyna się i tworzy się kafelek "2". Można go rzucać w dowolnym kierunku. Po każdym ruchu na bok, wszystkie płytki powinny zostać przybite do ściany, po złożeniu razem, należy je ponownie przybić do ściany, a wkońcu w przypadkowym miejscu wyrzucić numer 2. Jeśli nie ma pustych miejsc i nie można ułożyć kilku płytek, pojawia się napis GAMEOVER. Jeśli kafelek 2048 zostanie złożony, pojawi się zwycięski komunikat. W void loop znajduje się spora część skomentowanego kodu. Jest potrzebny do debugowania. Jest odpowiedzialny za uzyskanie liczby od 1 do 4 i przesłanie płytek we właściwym kierunku. Konieczne jest określenie pinów, do których podłączone są przyciski i adres ekranu. Teraz możesz zmontować urządzenie na breadboard'e i wypróbować algorytmy. Jeśli robisz wszystko "zgodnie z twoim rozumem", musisz zamówić produkcję płytki z obwodem drukowanym, na której umieścisz nasze komponenty. Ale w przypadku pierwszego prototypu wystarczy go zmontować na płytce prototypowej. Bierzemy zieloną płytkę makietową 5 * 7 cm jako podstawę. Przylutuj na niej Arduino. Nad nim robimy złącze ekranu. Rozmieść scl, sda na a4, a5 - będzie wygodniej lutować. Przyciski lutujemy z rezystorami. Wstaw ekran. Rysujemy strzałki. Graj Demonstracja
  16. 3 punkty
    Cyfrowa stacja lutownicza Jest to mój 2 prolekt po 12 letniej przerwie z elektroniką. Była to okazja do poznania Eagle oraz podstaw programowania mikroprocesorów. Obecnie wiele rzeczy bym poprawił i docelowo tak się chyba stanie. Działanie stacji oceniam bardzo pozytywnie. Obecnie mam 3 stacje lutownicze, więc już mogę coś na ten temat napisać ale to już dłuższy temat. Projekt polecam wszystkim, którzy mają niewykorzystane trafo 2x12V 60VA (minimum) oraz LCD1602 i procesor Atmega8. Są to chyba 3 najdroższe elementy (pomijając kolbę 907A, którą przez internet można kupić poniżej 30zł z kosztami wysyłki). Docelowo schemat podzieliłem na 2 zasadnicze części: 1. Płytka zasilająca 2.Płytka sterowania Wzory płytek w Eagle standardowo dostępne są w załączniku. A tutaj jeszcze foto gotowych płytek: Aby ułatwić możliwość wymiany procesora i wzmacniacza operacyjnego, zostały one umieszczone w podstawkach. Płytka sterująca oraz płytka zasilająca zostały połączone za pomocą złącza goldpin. Obudowa została wykonana z płyt meblowych oklejonych fornirem dębowym. Dodatkowo składałem dla znajomego CNC z chin i w ramach testów wykonałem panel przedni oraz tylni na tym CNC (materiał to 1,5mm blacha aluminiowa). Efekty pracy widać na poniższych zdjęciach: Zasilanie 230V trafia na gniazdo komputerowe oraz włącznik (pozyskane z zasilacza ATX). Następnie mamy bezpiecznik szklany i transformator toroidalny 50VA 2x12V. Transformator miał wymienione uzwojenia wtórne. Miałem transformator z tylko jednym uzwojeniem o napięciu 10,5V, więc od nowa zostały nawinięte uzwojenia 2x12V. Takie napięcia są wprowadzone zgodnie z zamieszczonym schematem na płytkę zasilającą. Zastosowałem najprostszą kolbę 907A z termoparą. Wykorzystałem dostępne w sieci oprogramowanie stacji lutowniczej RL-1 zawierające algorytm PID do sterowania grzałką. Konstrukcja nie jest pozbawiona wad: Obudowa nie jest dokładnie spasowana z panelami czołowym i tylnym (miała być tymczasowa, a wyszło jak zwykle). Słaby obieg powietrza w obudowie (pomimo tego faktu nic się nie przegrzewa przy długiej pracy. Oto film prezentujący rozgrzewanie grota od temperatury 38 stopni do 320 stopni: Już w 22 sekundzie grzałka osiąga temperaturę zadaną. Od 35 sekundy przykładam do grota cynę o grubości 0,7mm. Cyna zaczyna się topić ok. 50 sekundy. Temperatura grota została zestrojona ze wskazaniem stacji za pomocą termopary i procedury opisanej w dokumentacji stacji RL-1 (w załączniku AVT987). A to obecnie przeze mnie posiadane stacje lutownicze: Jak już wcześniej wspomniałem, wykonałbym kilka zmian. Najważniejsza to zmniejszenie wymiarów stacji. Trafo zajmuje 1/3-1/4 obudowy. Obecnie całość znalazłaby się na jednym PCB, wszystkie elementy SMD, cała stacja zmieściłaby się za LCD. Do tego trafo i wymiar całości zmniejszony minimum o połowę. Poza tym, prócz najdłuższego rozgrzewania i braku mocy przy lutowaniu dużych pól masy stacja działa nad wyraz dobrze. EAGLE_Moja stacja lutownicza.zip AVT987.pdf Cyfrowa_Stacja_Lutownicza_RL1 - do wgrania.zip Cyfrowa_Stacja_Lutownicza_RL1 - edytowalne oprogramowanie.zip Cyfrowa stacja lutownicza RL1.zip
  17. 3 punkty
    "Chciałbym aby drodzy koledzy wsparli mnie dobrą radą" Oto moja wskazówka, moim zdaniem kluczowa dla zrozumienia idei tego co chcesz zrobić: w układzie Ty (czyli sterownik) <-> silnik, to silnik rządzi. BLDC to nie krokowy, w którym podajesz impuls STEP, driver przełącza fazy a silnik obraca się o krok. Tutaj musisz zdać sobie sprawę, że zostałeś sprowadzony do roli głupiego komutatora silnika szczotkowego. Na pewno wiedziałeś taki od środka, prawda? Nie ma tam żadnej wycudowanej mechniki ani tym bardziej elektroniki. Jest za to obracający się przełącznik (komutator+szczotki) sztywno związany z wirnikiem i zapodający prąd na uzwojenia w zależności od kątowego położenia wału. No i to tu jest tak samo. Cała Twoja rola sprowadza się do wykrycia położenia wału i załączenia napięcia na odpowiednią parę uzwojeń. Koniec. A to jak szybko silnik się kręci zależy od mnóstwa czynników, z których Ty jako sterownik masz wpływ tylko na jeden: na wielkość prądu, który załączasz. To regulujesz wypełnieniem PWM, które musi być wielokrotnie szybsze niż komutacje faz (np. 8-16kHz) i jest to jakby zmiana napięcia baterii podłączonej do zwykłego silniczka DC. Reszta zależy od obciążenia mechanicznego. W silniku oczujnikowanym masz łatwo, bo czytasz stan 3 wejść i w zależności od tego załączasz swój potrójny driver w jeden z sześciu stanów. W przypadku prostego silnika bez czujników sprawa się komplikuje, bo musisz zbudować dodatkowy układ elektroniczny który na podstawie siły SEM indukowanej przez wirnik i przy wsparciu algorytmu programowego "wyczai" najlepszy moment przełączenia. To tyle. Tak więc driver BLDC w swojej podstawowej formie jest trywilanym przełącznikiem sterowanym stanem silnika, który musi znać - bez tego nie zadziała. Dopiero jakiś zewnętrzny proces, dostając prędkość obrotową (lub okres obrotu - wszystko jedno) znaną z definicji działania drivera, zadaje wielkość wysterowania (prąd czyli wypełnienie PWM) na podstawie swojego algorytmu wyznaczania prędkości, położenia czy czego tam jeszcze. A zwykle działa to w otwartej pętli, tj. zamykana jest ona dopiero przez Twoje własne doznania, użytkownika stojącego na desce lub latającego modelem samolotu. Widzisz, że za wolno? No to dodajesz gazu przez proste zwiększenie PWM a silnik dostając więcej mocy zwiększa obroty. Driver wciąż jest tylko przełącznikiem zapodającym w odpowiednich chwilach PWM na dwa z trzech drutów wystających z silnika. Oczywiście wchodząc w szczegóły zaczynają się schody, bo tak jak np. w spalinowym masz zmianę kąta wyprzedzenia zapłonu bo prędkość spalania mieszanki w komorze jest ograniczona i kręcąc się szybciej musisz odpalić ją trochę wcześniej by cała zdążyła się zamienić na ciśnienie w najbardziej odpowiednim położeniu tłoka, tak i tutaj trzeba lekko korygować punkt komutacji w zależności od prędkosci obrotowej bo prąd faz nie narasta i nie opada w czasie zerowym. Na szczęście tamat jest dobrze rozpoznany więc na pewno znajdziesz zmnóstwo literatury na ten temat. Sa przecież amatorskie sterowniki, są noty aplikacyjne firm zajmujących się produkcją driverów BLDC itd itp. Możesz zacząć od Texasa, skoro przypadły Ci do gustu jego scalaki. Nawiasem mówiąc jak początkujący - nawet nie elektronik - wybrałeś skok na głęboką wodę i nawet nie przypuszczasz ile pułapek siedzi w realizacji takiego drivera. Zrobienie tego na silniku 20W nie jest trywialne i są to długie tygodnie albo i miesiące pracy (przy założeniu, że nie ściągasz z sieci gotowego kodu na gotowy hardware) a 250-500W jakie potrzebujesz do żwawej deskorolki to już cały wachlarz kłopotów. Nie sprowadzają sie tylko do narysowania (zwykle naiwnie optymistycznego) schematu, ale siedzą w rozmieszczeniu elementów, grubości ścieżek, typie laminatu, gospodarce ciepłem, chłodzeniu, ograniczonej przestrzeni, algorytmach odzyskiwania energii (lub innego jej wytracania przy hamowaniu silnikiem), zabezpieczeniach, ochronie środowiskowej (woda, sól, temperatury) itp itd. No nic, nie chcąc zniechęcać życzę powodzenia, ale sugeruję rozejrzenie się za gotowcami, ew. zajrzenie tam do środka i zastanowienie się jak dużo jeszcze musisz wiedzieć by zrobić projekt, który tak wygląda i tak działa.
  18. 3 punkty
  19. 3 punkty
  20. 3 punkty
    Cześć wszystkim, chciałbym przedstawić wam mój pierwszy projekt DIY - wyścigówka Robo Race. Projekt ten powstał w celu rozwinięcia pasji, jaką było stworzenie własnego autonomicznego pojazdu oraz w celu wzięcia udziału w wyścigach Roborace będących częścią międzynarodowych zawodów Sumo Challenge. Zawody te polegają na wyścigu mobilnych robotów czterokołowych. Zadaniem robota jest przejechanie określonej liczby okrążeń (od startu do mety) toru z liniami jak w kategorii Line Follower ograniczonego bandami, wraz z resztą robotów ścigających się. Pierwszy z robotów, który przejedzie określoną liczbę okrążeń, wygrywa etap konkurencji. Z powodu wymagań opisanych powyżej postanowiłem stworzyć pojazd reagujący na sygnały z czujników wykrywających przeszkody (w postaci ścianek tworzących tor ruchu robota, oraz innych robotów biorących udział w wyścigu). Na podstawie zdobytych informacji pojazd wymija przeszkody dopasowując odpowiednio skręcenie kół oraz prędkość obrotu silnika. Jako, że w swojej karierze wykorzystywałem głównie arduino do sterowania robotem postanowiłem wykorzystać moduł firmy DFRobot, który zgodny jest z Arduino Uno. Dodatkowo wykorzystałem możliwość komunikacji dzięki modułowi BLE w celu skomunikowania mikrokontrolera z telefonem, dzięki któremu mogłem dostrajać robota. Do budowy projektu wykorzystałem następujące elementy: Płyta mocująca: wykonana z pleksi, stanowi bazę do pozostałych części. Płyty wydrukowane na drukarce 3D: Zaprojektowane w programie DesignSpark Mechanical 2.0, zapewniają utrzymanie części sterujących robota. Układ napędowy: przenosi moment z osi silnika na tylną oś pojazdu, która wyposażona jest w mechanizm różnicowy ułatwiający skręcanie. Układ skręcający: skrętne 2 przednie koła sterowane poprzez Serwo Hitec HS-322HD. Mikrokontroler: Romeo BLE pozwalający bezpośrednio sterować robotem oraz komunikować się z urządzeniami dzięki modułowi BLE CC2540. Sterownik silnika PWM: sterownik, potrafiący sterować silnikiem napędzającym pojazd. Silnik: Redox bardzo wydajna jednostka napędzająca pojazd. Zasilanie: 2 akumulatory, większy Pakiet Li-Pol Redox 4400mAh 30C 4S 14,8V do zasilania silnika, oraz mniejszy Pakiet Li-Pol Redox 2200mAh 30C 2S 7.4V do zasilania serwomechanizmu, mikrokontrolera oraz czujników. Układ sensoryczny: 3 analogowe czujniki podczerwieni. Układ, sterowany przez mikrokontroler Romeo BLE odczytuje pomiary przez analogowe czujniki, następnie na ich podstawie wytwarza sygnał sterujący serwomechanizmem pojazdu, oraz sygnał sterujący silnikiem napędowy, który podany na sterownik PWM przetwarzany jest na sygnały kluczujące zasilaniem silnika napędowego. Dzięki modułowi BLE możliwa stała się bezprzewodowa komunikacja z mikrokontrolerem, co pozwoliło na dynamiczne dostrajanie regulatora PD sterującego serwomechanizmem robota. Wykorzystałem do tego program pracujący z poziomu przeglądarki App Inventor 2. Program ten pozwolił mi na stworzenie mobilnej aplikacji, pozwalającej na przesyłanie do robota i odczytywanie aktywnych parametrów regulatora PD robota. Parametry regulatora odczytywane są przez mikrokontroler pojazdu i zapisywane do odpowiednich zmiennych dzięki zastosowaniu poniższego kodu: void loop() { regulator(kp, kd); if(Serial.available()>0){ val = Serial.readStringUntil('\n'); if(val.startsWith(kp)){ val.remove(0, 2); kp=val.toInt(); }else if(val.startsWith(kd)){ val.remove(0, 2); kd=val.toInt(); }else{ val=""; } } } Pojazd odczytuje wartości położenia od przeszkód z czujników w przerwaniach czasowych z częstotliwością 500Hz. Pozwala to na stały odczyt odległości, dzięki czemu możliwe jest sterowanie w czasie rzeczywistym. void setup() { Serial.begin(115200); //Set Serial Baud noInterrupts(); // zatrzymaj wykonywanie jakichkolwiek przerwań // ustawianie timera 2 // TCCR2A i TCCR2B to rejestry kontrolne timera 2 // służą one do wyboru trybu pracy danego timera // i wartości podzielnika TCCR2A = 0;// zerowanie rejestru kontrolnego A TCCR2B = 0;// zerowanie rejestru kontrolnego B TCNT2 = 0;// zerowanie licznika // ustawiamy rejestr porównawczy na 500 Hz OCR2A = 255;// = (16000000) / (500*256) - 1 [ustawianie liczby impulsów do zliczenia TCCR2A |= (1 << WGM22); // ustawianie timer2 w tryb obsługi przerwań (CTC) TCCR2B |= (1 << CS22) | (1 <<CS20); // ustawianie dzielnika na 256 // ustawianie maski rejestru przerwań zegarowych timera 2 TIMSK2 |= (1 << OCIE2A); // wzkazanie że przerwanie ma być wywołane dla rejestru OCR2A interrupts(); //zezwalenie na wykonywanie przerwań } ISR(TIMER2_COMPA_vect) // wywołanie funkcji po zliczeniu impulsów timera { odczyt(); } void odczyt(){ wartosclewego = analogRead(SENSORLEWY); wartoscprawego = analogRead(SENSORPRAWY); wartoscsrodek = analogRead(SENSORSRODEK); } Dzięki wartością pozycji odczytanych z czujników oraz parametrom regulatora sygnał sterujący robotem wytwarzany jest przez poniższy kod void regulator(const int kp, const int kd){ err_prev = err; err = wartoscprawego - wartosclewego; serwo = kp * (err + kd*fpr*(err - err_prev)); //kp - wzmocnienie; fpr - częstotliwość próbkowania; kd - wzmocnienie części różniczkującej if(serwo > 85) serwo = 85; if(serwo < 5) serwo = 5; } Poniżej przedstawiam moje dzieło Wyścigówka w akcji:
  21. 3 punkty
  22. 3 punkty
    Witam chciałbym Wam przedstawić regułę działania regulatora PID, model ten wykonałem na studia jako projekt zaliczeniowy. Gdybyście nie chcieli czytać tego wszystkiego to możecie tego posłuchać na YouTube Regulator ten składa się z trzech członów: Proporcjonalnego (proportional) Całkującego (integral) Różniczkowego (derivative) Jego celem jest utrzymanie wartości wyjściowej na poziomie wartości zadanej. Oblicza on wartość różnicy pomiędzy parametrem zadanym a zmierzonym. Działa w taki sposób aby zredukować wartość różnicy. Stosuje się go w obiektach które poddawane są falą zakłóceń. Ja go zastosowałem w modelu piłki i belki, aby przeciwdziałał sile grawitacji. Jest to prosty model który jest niestabilny w otwartej pętli. To znaczy że jeśli kąt odchylenia belki od poziomu jest bardzo zbliżony do zera i nie ma sprzężenia zwrotnego to grawitacja spowoduje, że belka zacznie się przechylać w daną stronę i piłka w końcu z niej spadnie. Aby ustabilizować piłkę należy użyć regulatora PID, który będzie mierzył położenie piłki i odpowiednio regulował kąt nachylenia belki. W tym modelu ramię dźwigni jest przymocowane na jednym końcu belki a ramię serwomechanizmu na drugim. Gdy ramię serwomechanizmu obraca się o kąt θ to dźwignia zmienia kąt nachylenia belki o α. Rysunek przedstawiający model piłki i belki Funkcja która opisuje zależność położenia piłki R(s) od kąta serwomechanizmu θ(s) w otwartej pętli jest następująca: Miałem do wyboru albo zasymulować układ w programie simulink albo zbudować rzeczywisty model, wiadomo co wybrałem Schemat blokowy regulatora PID który implementowałem jest następujący: Oprogramowanie na komputerze do którego jest podpięta kamera, mierzy odległość piłki od zadanej pozycji, odejmując centroid piłki od połowy szerokości rozdzielczości obrazu z kamery. Na samym początku zacząłem od przygotowania modelu w programie SketchUp. Gotowa złożona konstrukcja przeze mnie wygląda następująco: Podpięcie serwomechanizmu do Arduino: Serwomechanizm został podpięty do Arduino za pomocą przewodów męsko-męskich wg poniższego schematu: Serwomechanizm Arduino czerwony kabel 5V brązowy kabel GND pomarańczowy kabel Pin 3 Następnie zaprogramowałem Arduino tak by mogło odczytywać dane po serialu i sterować serwem: 1: #include <Servo.h> 2: Servo servo; 3: int servoPin = 3; 4: int radius = 90; 5: 6: void setup() { 7: Serial.begin(115200); 8: servo.attach(servoPin); 9: servo.write(radius); 10: } 11: 12: void loop() { 13: if (Serial.available()) { 14: radius = Serial.read(); 15: servo.write(radius); 16: } 17: } Kod który oblicza odległość piłki od zadanej pozycji, dokonuje obliczenia i wysyła dane do arduino został napisany w języku Python. Do przetwarzania obrazu z kamerki wykorzystałem bibliotekę Opencv (Open Source Computer Vision Library), która jest na licencji BSD. Żeby program rozpoznał piłkę musiałem: Zdefiniować zakres barw. Lekko rozmazać obraz po przez rozmycie Gaussa. Stworzyć maskę dla danego zakresu. Na podstawie stworzonej maski znaleźć kontur. Na podstawie konturu znaleźć centroid oraz narysować linię wokół obiektu. z racji tego że kod napisany w języku Python zajmuje trochę miejsca to umieściłem go na githubie, poniższy link przekieruje do mojego repozytorium z kodem wraz z komentarzami: kod programu Wnioski które wynikły z budowy: Bardzo ważną rzeczą przy budowie konstrukcji jest eliminacja luzów które mogą się pojawić na ramieniu łączącym serwomechanizm z pochylnią jak i na łączeniu pochylni z podporą. Arduino podczas przyjmowania danych po serialu zamienia znaki na kody ASCII dodatkowo przyjmuje te dane jako String. Dlatego podczas wysyłania danych z programu należy najpierw zrzutować zmienną radius do typu całkowitego (wartości z regulatora są typu double a serwomechanizm działa na wartościach całkowitych), następnie należy to zamienić na char który potem zamieniamy na String. Przykład dla wysłania kąta równego 120 stopni 120 - kod ASCII reprezentuje znak x (mały) Znak ten jest rzutowany na String po czym jest wysyłany do Arduino po serialu Arduino przyjmuje te dane po czym zamienia każdy znak na kod ASCII - otrzymujemy 120. Maskę w bibliotece OpenCV tworzy się w przestrzeni barw HSV, dlatego musiałem wykonać konwersję z BGR na HSV Biblioteka OpenCV operuje na zmapowanych zakresach HSV. Poniżej przedstawiam tabelę zmapowanych wartości
  23. 3 punkty
    Cześć, dziś pokażę Wam, w jaki sposób złożyć do kupy moduł ESP8266 (w tym wypadku płytkę WeMos D1 mini) oraz matrycę LED na sterowniku MAX7219. Oprócz tego będziemy potrzebować paru przewodów połączeniowych, lutownicy z cyną, obudowy do zegarka oraz przewód microUSB do zasilenia naszego układu. Całość projektu jest dostępna w repozytorium na moim Githubie. Obudowa Jako obudowę użyłem sklejki o grubości 6 milimetrów, którą zamówiłem na aukcji Allegro razem z docięciem. Całość obudowy z dostawą kosztowała mnie mniej niż 40 złotych. Zaprojektowałem obudowę korzystając z programu Inkscape. Następnie została ona sklejona i pokryta lakierobejcą w kolorze jasnego dębu. Na front obudowy nakleiłem mleczną plexi o grubości 3 mm, którą dostałem w lokalnym sklepie za 5 złotych. Elektronika Tutaj będzie dużo łatwiej, gdyż wystarczy połączyć tylko odpowiednie piny. Połączyłem zgodnie z adnotacją w bibliotekach do obsługi MAX7219. Należy pamiętać, że WeMos D1 operuje na napięciu 3.3V na szczęście sterownik MAX7219 akceptuje je i nie będzie problemu aby układ ruszył. Na zdjęciu połączenie za pomocą przewodów z haczykami. Kod programu Przed kompilacją programu, należy dodać do Arduino IDE obsługę płytek opartych na ESP8266 (opisał to SOYER w tym temacie). Będziemy potrzebować trzech bibliotek, które nie są standardowo dołączone do Arduino IDE: MD_MAX72XX - dzięki której nasz procesor skomunikuje się ze sterownikiem MAX7219 MD_Parola - biblioteka rozszerzająca funkcje MD_MAX72XX NTPClient - dzięki niej zyskamy możliwość pobierania czasu z Internetu bez potrzeby używania modułu RTC. Po dodaniu bibliotek, kod wygląda następująco: #include <MD_Parola.h> #include <MD_MAX72xx.h> #include <SPI.h> #include <ESP8266WiFi.h> #include <NTPClient.h> #include <ESP8266WebServer.h> #include <WiFiUdp.h> #define HARDWARE_TYPE MD_MAX72XX::FC16_HW #define MAX_DEVICES 4 #define CLK_PIN D5 // or SCK #define DATA_PIN D7 // or MOSI #define CS_PIN D8 // or SS MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES); WiFiUDP ntpUDP; const char *ssid = ""; const char *password = ""; NTPClient timeClient(ntpUDP, "0.pl.pool.ntp.org", 3600, 60000); WiFiServer server(80); uint8_t frameDelay = 25; textEffect_t scrollEffect = PA_SCROLL_LEFT; #define BUF_SIZE 512 char curMessage[BUF_SIZE]; char newMessage[BUF_SIZE]; bool time_interval = false; String data; unsigned int run_seconds = 0; String timeCheck(){ timeClient.update(); data = timeClient.getFormattedTime(); data.toCharArray(newMessage, BUF_SIZE); P.displayText(newMessage, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); return data; } String wifiCheck(){ WiFiClient client = server.available(); while(client.available()){ String req = client.readStringUntil('\r'); req = req.substring(5,req.length()-9); req.replace("%20", " "); client.flush(); String s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nReq: " + req + "</html>\n"; client.print(s); delay(1); client.stop(); switch(req[0]){ case 's': data = req.substring(2); P.displayScroll(curMessage, PA_LEFT, PA_SCROLL_LEFT, frameDelay); return data; break; case 'i': data = req.substring(2); P.setIntensity(data); break; } } } void setup() { P.begin(); P.displayClear(); P.displaySuspend(false); P.displayScroll(curMessage, PA_LEFT, PA_SCROLL_LEFT, frameDelay); P.setIntensity(0); curMessage[0] = newMessage[0] = '\0'; WiFi.begin(ssid, password); while ( WiFi.status() != WL_CONNECTED ) { delay ( 500 ); } timeClient.begin(); server.begin(); sprintf(curMessage, "%03d:%03d:%03d:%03d", WiFi.localIP()[0], WiFi.localIP()[1], WiFi.localIP()[2], WiFi.localIP()[3]); } void loop() { if ( (millis()/1000 - run_seconds) > 30 ){ run_seconds = millis()/1000; time_interval = true; } if (time_interval){ data = timeCheck(); time_interval = false; } data = wifiCheck(); data.toCharArray(newMessage, BUF_SIZE); if (P.displayAnimate()) { strcpy(curMessage, newMessage); P.displayReset(); } } W zależności od zakupionego modułu trzeba będzie wybrać wersję hardware sterownika MAX7219. Definiujemy ją w linijce 10: #define HARDWARE_TYPE MD_MAX72XX::FC16_HW Obecnie dostępne typy sprzętowe to: FC16_HW, PAROLA_HW, GENERIC_HW, ICSTATION_HW. Musimy tutaj dobrać wartość eksperymentalnie. Niewłaściwy typ sprzętowy powoduje błąd w kolejnosci wyświetlania oraz w kolejności animacji poszczególnych pikseli matrycy. Dodatkowa opcja (własny tekst) W kodzie dociekliwi zobaczą, że jest możliwość wyświetlenia własnego tekstu. Po uruchomieniu programu na wyświetlaczu przewinie się adres IP, który WeMos uzyskał z naszego routera. Po otworzeniu strony z poziomu przeglądarki (lub wywołaniu polecenia curl) na stronę: http://<IP>/s=<TEXT> na wyświetlaczu będzie on przewijany od prawej do lewej strony przez maksymalnie 30 sekund. Dodatkowa opcja (zmiana jasności wyświetlacza) Można również zmienić jasność wyświetlacza, albo na stałe (w kodzie linijka: 86) P.setIntensity(<liczba z zakresu 0-15>); lub poprzez stronę www pod adresem: http://<IP>/i=<LICZBA Z ZAKRESU 0-15> gdzie 0 - to wartość minimalna, a 15 odpowiada za maksymalny poziom świecenia. Zegarek prezentuje się następująco: obudowa.pdf
  24. 3 punkty
  25. 3 punkty
    Posiadając dwa futrzaki pojawiła się potrzeba zapewnienia im odpowiedniej ilości jedzenia, szczególnie podczas weekendowych wyjazdów. Przeglądając gotowe rozwiązania stwierdziłem, że najlepiej zbudować samemu mając przy tym sporo frajdy i satysfakcji. Urządzenie zostało zbudowane w oparciu o: Raspberry Pi Zero W Kamera do Raspberry Pi Serwo L360 Uchwyt na kamerę Czujnik odległości Główne cechy urządzenia Zdalna możliwość karmienia z dowolnego miejsca na świecie podgląd z ruchomej kamery ultradźwiękowy czujnik wykrywający kota oświetlenie IR pozwalające na podgląd w nocy możliwość wgrywania i odtwarzania dowolnych plików audio opcja "Text To Speach" duży 10 litrowy zbiornik na karę detekcja pustego zbiornika harmonogram automatycznego podawania karmy Pierwszym etapem i najtrudniejszym było wykonanie niezawodnego mechanizmu podającego karmę. Przetestowałem kilka rozwiązań: podajnik ślimakowy mechanizm koszykowy zasuwa podajnik tłokowy - to rozwiązanie sprawdziło się świetnie Układ podający powstał z ogólnodostępnych elementów PCV Niżej widok od tyłu pokazujący montaż tłoka Na filmie pokazana jest idea pracy mechanizmu, były to pierwsze próby ze słabszym serwomechanizmem. Ostatecznie został wymieniony na L360, a ruch obrotowy samego serwa zastąpiony ruchem "po łuku - przód, tył" co pozwoliło zapobiec ewentualnemu zakleszczeniu się karmy. Mechanizm - film Kolejnym etapem było wykonanie dodatkowej elektroniki obsługującej: 2 serwa do kamery 1 serwo podające karmę wzmacniacz audio czujnik odbiciowy IR czujnik zbliżeniowy Dodatkową wyzwaniem było przerobienie zwykłej kamery na kamerę NoIR, w tym celu zdemontowałem układ optyczny i delikatnie usunąłem filtr podczerwony. Poniżej wygląd matrycy po zdemontowaniu tych elementów. Po ponownym zamontowaniu soczewki, kamera działała już prawidłowo przy oświetleniu podczerwonym. Poniżej widok od spodu: Główne oprogramowanie sterujące sprzętem zostało napisane w pythonie, a interfejs użytkownika w PHP, na raspberry pi jest postawiony serwer www razem z mysql, tak więc jest to mały potworek do karmienia kotów. Sterowanie odbywa się przez stronę www, co wyeliminowało pisanie dedykowanej aplikacji na każdy z systemów osobno. Na koniec kilka dodatkowych zdjęć
Tablica liderów jest ustawiona na Warszawa/GMT+01:00
×