Skocz do zawartości

spook

Użytkownicy
  • Zawartość

    8
  • Rejestracja

  • Ostatnio

  • Wygrane dni

    1

spook wygrał w ostatnim dniu 28 marca

spook ma najbardziej lubianą zawartość!

Reputacja

23 Dobra

O spook

  • Ranga
    2/10

Informacje

  • Płeć
    Mężczyzna
  • Lokalizacja
    Wrocław
  • Języki programowania
    C#, C++, Python
  • Zainteresowania
    Fotografia lotnicza, elektronika, programowanie
  • Zawód
    Programista
  • www

Ostatnio na profilu byli

Blok z ostatnio odwiedzającymi jest wyłączony i nie jest wyświetlany innym użytkownikom.

  1. Co to jest timelapse? W razie, gdyby ktoś nie wiedział, co mało prawdopodobne, ale jednak - jest to przeciwieństwo slow-motion, czyli film, który jest kilka albo kilkanaście razy przyspieszony. Filmy takie pozwalają obejrzeć zjawiska, które ze swojej natury są bardzo wolne, na przykład wyrastanie ciasta, płynięcie chmur na niebie, rośnięcie roślin i tak dalej. Nagranie timelapse nie jest jakimś wielkim wyczynem, bo większość aparatów i kamerek sportowych ma taki tryb wbudowany, a na telefony komórkowe istnieją tysiące aplikacji realizujących takie właśnie nagranie. Do czego więc może służyć maszyna do timelapse? Najbardziej podstawową wersją timelapse jest oczywiście nieruchomy kadr - ustawiamy kamerę na statywie, uruchamiamy proces nagrywania i zostawiamy wszystko na odpowiedni czas. Filmy takie same w sobie robią już wrażenie, ale znacznie ciekawiej jest, gdy kadr jest zmienny, na przykład gdy zastosujemy panoramowanie (czyli powolny obrót) albo liniowe przesunięcie. Kłopot polega na tym, że obrót taki musi być realizowany bardzo wolno - w tempie dopasowanym do tempa robienia zdjęć. 45 stopni to być może dużo, ale jeżeli kamera ma zarejestrować, powiedzmy, 40 minut materiału przy 30 klatkach na sekundę, to mamy prędkość obrotu rzędu 45/(40*60) = 0,01875 stopnia na sekundę. Żeby było trudniej, kamera musi obracać się płynnie, bo timelapse będzie wyraźnie poszarpany. Do czegoś takiego trzeba już dedykowanego urządzenia: kupionego albo - co znacznie ciekawsze - zrobionego samodzielnie. Komponenty Tym razem kontrolerem stało się Arduino Uno, między innymi dlatego, że razem z shieldem z wyświetlaczem i klawiaturą zapewniło dosyć niskim kosztem interfejs użytkownika. Płynny i w pełni kontrolowany obrót zapewni silnik krokowy, a ten konkretny, czyli SY35ST26-0284A wybrałem w dużej mierze dlatego, że można go zasilić bezpośrednio dwucelowym akumulatorem LiPo. Do silnika należało oczywiście również kupić odpowiedni sterownik. Oprócz tego wyposażyłem się też w obudowę - być może efekt końcowy nie powala estetyką, ale bardzo nie lubię kończyć projektów z gmatwaniną sterczących przewodów i po ukończeniu jeżdżącego robota stwierdziłem, że będę dbał również o wygląd moich projektów. To nie był koniec zakupów - silnik krokowy oferuje 200 kroków na obrót, co daje 1,8 stopnia na krok. Tymczasem ja potrzebowałem stukrotnie większej dokładności, dlatego konieczne było zbudowanie przekładni zębatej. Zębatki Z przekonaniem, że w Internecie można znaleźć wszystko zacząłem szukać zębatek i okazało się, że wcale nie tak łatwo jest znaleźć zębatki do takiego projektu. Byłem ograniczony ich rozmiarem oraz sposobem montażu - nie posiadając wtedy drukarki 3D (na której pewnie sam wydrukowałbym potrzebne koła), miałem też ograniczone możliwości związane z montażem komponentów. W akcie desperacji napisałem email z zapytaniem ofertowym do krakowskiej firmy, która specjalizuje się w produkcji kół zębatych, ale koszty rzędu (o ile dobrze pamiętam) 800 PLN za cztery koła zębate przekraczały wielokrotnie mój budżet na ten projekt. Wróciłem więc do poszukiwań i w końcu znalazłem odpowiednie koła zębate w sklepie conrad.pl. Kupiłem dwa czterdziestozębowe i dwa trzydziestozębowe - w przypadku, gdyby te pierwsze nie zmieściły się wewnątrz obudowy. Oprócz tego kupiłem jedną piętnastkę oraz dwunastkę - ta ostatnia miała otwór na oś pasujący do kupionego przeze mnie silnika krokowego. Przekładnia Teraz musiałem zaprojektować przekładnię - skorzystałem w tym celu z darmowego Solid Edge Drafting - świetnego dwuwymiarowego cada. Pomierzyłem wszystkie przestrzenie suwmiarką i wyszło na to, że przekładnia zmieści się w całości w przedniej, podwyższonej części obudowy. Nie dysponuję żadną maszyną CNC, ale otwory w aluminiowych płytkach musiałem zrobić bardzo dokładnie, bo inaczej zębatki nie zazębiłyby się (w jedną albo drugą stronę) albo obracały ze zbyt dużym oporem. Wpadłem więc na pomysł - wydrukowałem układ otworków w obu płytkach przygotowany wcześniej w cadzie na papierze samoprzylepnym i przykleiłem go na obrabianym aluminium. Potem dociąłem płytki zgodnie z ramkami i wywierciłem otwory w oznaczonych miejscach - okazało się, że taka dokładność była wystarczająca i wszystko ładnie wskoczyło na swoje miejsca. Montaż Po zbudowaniu przekładni nie zostało już zbyt dużo do roboty - zamontowałem wewnątrz obudowy Uno z shieldem LCD, podłączyłem do przygotowanej wcześniej płytki prototypowej ze sterownikiem silnika, a do niej sam silnik. Potem skręciłem wszystko i dostałem (w miarę) zgrabne urządzenie. Programowanie Na potrzeby tego projektu napisałem specjalnie własny zestaw bibliotek, które ułatwiają budowanie wielopoziomowych menu na shieldzie LCD. Programik jest stosunkowo prosty - należy obrócić silnik w położenie źródłowe i docelowe, ustalić czas obrotu - i uruchomić. Program oblicza o jaki kąt obrócić silnik w jakim czasie i powolutku obraca oś z jednego położenia do drugiego. Ponieważ program z bibliotekami jest dosyć duży, nie będę go tu zamieszczał, ale można go ściągnąć z mojego repozytorium na gitlabie. Efekty Czy cel został osiągnięty? Definitywnie tak - nagrane filmy poddaję niewielkiej obróbce w VirtualDub (korzystam z plugina o nazwie deshaker), żeby odrobinę poprawić ich jakość, ale nawet i bez tej obróbki filmiki wyglądają całkiem przyzwoicie. Przykładowy poniżej: Wnioski Po pierwsze, maszyna jest niestety przeraźliwie głośna. Obracanie (przynajmniej na początku, podczas ustawiania zakresów) osi generuje dźwięk przypominający skrzyżowanie kosiarki do trawy i piły łańcuchowej. Winne są najprawdopodobniej pancerne zębatki oraz luzy na osiach - teraz, mając już drukarkę 3D, wydrukowałbym sobie wszystko z PLA spasowane do dziesiątych milimetra, podczas budowy tego urządzenia skazany byłem jednak tylko na własne ręce i montaż przekładni na pewno nie jest tak dokładny jaki mógłby być. Poza tym warto byłoby rozważyć również zastosowanie mniejszych, plastikowych zębatek, tylko obawiałem się trochę, czy będę w stanie zamontować je pewnie do osi - tak, by nie wyrobiły się i nie zaczęły obracać w miejscu. Plusem tej przekładni jest natomiast fakt, że jest po prostu pancerna - myślę, że spokojnie mógłbym zamontować na niej lustrzankę z solidnym obiektywem i ani przekładnia ani silnik nawet by nie sapnęły. Zdecydowanie warto było zainwestować w obudowę - kosztowała mnie raptem 15 złotych, a znacząco podniosła estetykę projektu (i przynajmniej w pewnym stopniu chroni elektronikę przed warunkami atmosferycznymi i kurzem. Wbrew pozorom dosyć wygodne okazało się zasilenie urządzenia z zewnątrz - wyprowadziłem sobie ze środka kabelek zakończony wtyczką T-Dean i gdy chcę uruchomić narzędzie, po prostu łapię jeden z dwucelowych akumulatorów LiPo walających się gdzieś po domu i maszyna może na nim działać naprawdę długo. Choć urządzenie powstało już jakiś czas temu, nie użyłem go jeszcze zbyt wiele razy; tak naprawdę zbudowałem je pod kątem filmowego projektu, który mam zamiar nakręcić, ale wymaga on dosyć dużo czasu w plenerze, a tego zasobu od jakiegoś czasu niestety trochę mi brakuje. Ale też nie tracę nadziei - być może za kilka lat zrealizujemy go razem z córką? Czas pokaże...
  2. Trudne początki Tak naprawdę to jest chyba mój pierwszy projekt w świecie Arduino! Zamarzyłem sobie zbudowanie własnego, terenowego pojazdu zdalnie sterowanego - takiego, na którym można potem zamontować jakiś chwytak, ramię albo kamerkę z przekaźnikiem FPV. Kontroler Tu akurat nie miałem większego wyboru, bo wtedy pod ręką miałem akurat Arduino Leonardo. Zaletą tej płytki jest kompatybilność z popularnymi shieldami do Uno a także złącze microUSB typu B (zamiast mało popularnego złącza "drukarkowego" w Uno). Na potrzeby tego niezbyt skomplikowanego projektu moc obliczeniowa tej płytki jest również całkowicie wystarczająca. Podwozie Moim planem było zbudowanie definitywnego i niepokonanego łazika marsjańskiego, więc zwykłe podwozie nie wchodziło w grę - koniecznie musiało być terenowe. Przegrzebałem naprawdę połowę Internetu w poszukiwaniu tego idealnego podwozia (ale - nie ukrywajmy - mieszczącego się również w moim budżecie) i w końcu mój wybór padł na podwozie Dagu DG012-ATV z napędem na cztery koła. Nieco podwyższony prześwit w stosunku do innych podwozi (dający nadzieję na pokonywanie niewielkich przeszkód), napęd na cztery koła - wszystko to brzmiało bardzo zachęcająco. Czterema silnikami coś oczywiście musi obracać, razem z podwoziem nabyłem więc również czterokanałowy sterownik silników DFRobota (w postaci shieldu dla Arduino). Serwo i czujnik Żeby urozmaicić nieco projekt, dodałem do niego serwo modelarskie, na którym zamontowałem ultradźwiękowy czujnik odległości z założeniem, że spróbuję kiedyś napisać program do autonomicznego poruszania się. RC Od długiego czasu używam do zdalnego sterowania aparatury FrSky Taranis, więc oczywiście musiałem skorzystać z kompatybilnego odbiornika - w tym przypadku X8R. Zasilanie Projekt oczywiście musiał być mobilny, więc zdecydowałem się na zasilenie go dwucelowym akumulatorem Lipo; konieczne okazało się też zastosowanie układu BEC, który obniżył napięcie akumulatora do 5V. Teraz pozostało już tylko zmontować wszystko w całość. Montaż podwozia Tu obyło się bez niespodzianek i problemów, po prostu skręciłem wszystko zgodnie z instrukcją i wyprowadziłem na zewnątrz przewody, którymi zasilane miały być silniki. Potem sprawy nieco się skomplikowały. Wszystko rozbiło się generalnie o to, że jak bym nie ułożył elementów na podwoziu, po prostu nie było takiego ułożenia, żeby wszystko się zmieściło. Sprawy utrudniał również fakt zastosowania Leonardo, które - umówmy się - jest raczej kobylaste i znacznie lepiej sprawdziłoby się tu kompatybilne z tą płytką Arduino Micro. Do tego dochodził BEC, odbiornik, serwo (którego nota bene nie miałem jak zamocować, bo w wersji 4WD podwozia miejsce na serwo zajmowane było przez dwa dodatkowe silniki) no i oczywiście akumulator. Dlatego zdecydowałem się na umieszczenie wszystkiego ponad podwoziem, pozostawiając na dole sam akumulator. Przykleiłem więc na dolnym pokładzie rzep, na którym mocowany jest akumulator - przeciążenia podczas poruszania robota są tak znikome, że jest to naprawdę pewny i sprawdzony sposób montażu (pozwalający też szybko zamontować albo zdemontować akumulator w razie potrzeby). Oprócz tego przykręciłem w narożnikach podwozia długie dystanse (kupione kiedyś w Chinach na potrzeby projektu quadrokoptera) i zabrałem się za przygotowywanie górnej części pojazdu. Górne podwozie wykonałem z fragmentu płytki aluminiowej, którą dociąłem tak, by znalazła się dokładnie ponad górną częścią podwozia - i jednocześnie dzięki temu przykryła koła, z których pył mógł się dostawać do elektroniki. W płytce wyciąłem otwór na serwo; ponieważ nie dysponuję żadnym sprzętem CNC, który pomógłby mi wyciąć równy, parametryczny otwór, rad nierad wziąłem do ręki wiertarkę, najpierw nawierciłem otwory, potem zamontowałem w niej frez i zacząłem ręcznie wycinać aluminium, a na końcu doszlifowałem wszystko pilnikami o zmniejszającej się ziarnistości. Cały proces poniżej: Teraz można było powoli przystąpić do montażu. Na pierwszy ogień poszło serwo, które na szczęście wpasowało się w wycięty przeze mnie otwór po prostu idealnie. Następnym krokiem było zamontowanie BECa, którego umieściłem pod górnym pokładem. Przewód zasilający (zakończony złączem T-Dean) musiałem rozgałęzić, ponieważ jedna para przewodów musiała zostać połączona z BECem, który obniży napięcie do 5V dla części elektroniki, zaś druga para - do sterownika silników, który będzie potem je zasilał. Szczęśliwie silniki akceptują napięcie dwucelowego akumulatora LiPo - trzeba to koniecznie sprawdzić przed zakupem/montażem! Arduino Leonardo można zasilić bezpośrednio z akumulatora, natomiast konieczne było przylutowanie odpowiedniej wtyczki (na zdjęciu po prawej stronie). Na koniec pozostało podłączenie wszystkich komponentów i otrzymujemy następujący efekt: Programowanie Pierwszym programikiem, który napisałem, był tester silników, który uruchamiał każdy z nich na pół sekundy. Kod wygląda następująco: const int E1 = 3; // Motor1 Speed const int E2 = 11; // Motor2 Speed const int E3 = 5; // Motor3 Speed const int E4 = 6; // Motor4 Speed const int M1 = 4; // Motor1 Direction const int M2 = 12; // Motor2 Direction const int M3 = 8; // Motor3 Direction const int M4 = 7; // Motor4 Direction void M1_advance(char Speed) { digitalWrite(M1, HIGH); analogWrite(E1, Speed); } void M2_advance(char Speed) { digitalWrite(M2, LOW); analogWrite(E2, Speed); } void M3_advance(char Speed) { digitalWrite(M3, LOW); analogWrite(E3, Speed); } void M4_advance(char Speed) { digitalWrite(M4, HIGH); analogWrite(E4, Speed); } void M1_back(char Speed) { digitalWrite(M1, LOW); analogWrite(E1, Speed); } void M2_back(char Speed) { digitalWrite(M2, HIGH); analogWrite(E2, Speed); } void M3_back(char Speed) { digitalWrite(M3, HIGH); analogWrite(E3, Speed); } void M4_back(char Speed) { digitalWrite(M4, LOW); analogWrite(E4, Speed); } void setup() { for (int i = 3; i < 9; i++) pinMode(i, OUTPUT); for (int i = 11; i < 13; i++) pinMode(i, OUTPUT); pinMode(LED_BUILTIN, OUTPUT); } void loop() { digitalWrite(LED_BUILTIN, HIGH); M1_advance(100); delay(500); M1_advance(0); M2_advance(100); delay(500); M2_advance(0); M3_advance(100); delay(500); M3_advance(0); M4_advance(100); delay(500); M4_advance(0); digitalWrite(LED_BUILTIN, LOW); delay(2000); // Delay 2S } Jak widać, sterowanie silnikami odbywa się poprzez podawanie kierunku poprzez piny cyfrowe i prędkości poprzez piny analogowe - proste, jak konstrukcja cepa. Teraz przyszła kolej na odbiornik RC i tu zaczęły się schody. Odbiorniki klasycznie podają stan każdego kanału poprzez sygnał PWM. W praktyce jest to seria naprzemiennych zer i jedynek, z których każda para 1+0 trwa 1/55 sekundy. Wartość możemy odczytać mierząc czas trwania sygnału 1: jeżeli jest to 1 milisekunda, przyjmujemy wartość minimalną ("-1"), jeżeli 1.5 milisekundy, to wartość środkową ("0"), zaś jeśli 2 milisekundy, to wartość maksymalną ("1"). Niektóre odbiorniki pozwalają na nieco szerszy zakres - od 0.5ms do 2.5ms, X8R domyślnie podaje wartości z tego pierwszego zakresu. Ponieważ musiałem mierzyć wartości na dwóch różnych kanałach, zdecydowałem się skorzystać z mechanizmu przerwań. Działa on mniej więcej następująco: gdy zajdzie wybrane zdarzenie (na przykład zmiana stanu danego pinu z niskiego na wysoki), wykonanie programu jest przerywane, zostaje wykonana funkcja oznaczona jako tzw. handler przerwania, a po jej zakończeniu program wznawia wykonanie w miejscu, w którym został przerwany. Na Arduino można znaleźć bardzo wygodną bibliotekę EnableInterrupt, która uogólnia sposób dodawania i usuwania handlerów przerwań pomiędzy różnymi wersjami Arduino, a korzysta się z niej w następujący sposób: #include "EnableInterrupt.h" volatile int microsStart = 0; void pin0Rising() { microsStart = micros(); disableInterrupt(0); enableInterrupt(0, pin0Falling, FALLING); } void pin0Falling() { int microsEnd = micros(); int diff = microsEnd - microsStart; if (diff > 1500) { digitalWrite(LED_BUILTIN, HIGH); } else { digitalWrite(LED_BUILTIN, LOW); } disableInterrupt(0); enableInterrupt(0, pin0Rising, RISING); } void setup() { enableInterrupt(0, pin0Rising, RISING); pinMode(LED_BUILTIN, OUTPUT); } void loop() { } Po wpięciu przewodu sygnałowego do pinu 0 i uruchomieniu programiku na kontrolerze, wbudowana dioda powinna się zapalić w momencie, gdy ustawimy drążek w położeniu większym niż połowa. Zwrócę jeszcze uwagę na magiczne słówko "volatile" przy deklaracji zmiennej - informuje ono kompilator, że zmienna ta nie może zostać zoptymalizowana (kompilator, a dokładniej optymalizator w niektórych przypadkach może w locie usunąć zmienną, na przykład jeżeli nie jest ona używana lub przez cały czas trwania programu ma zawsze tę samą wartość). Dodam tu jeszcze jedną ważną informację: program obsługi przerwania powinien być tak krótki, jak to tylko możliwe! Ma to sporo sensu jeżeli się nad tym nieco dłużej zastanowić, ale ja na to nie wpadłem i w pierwotnej wersji programu cała obsługa silników umieszczona była właśnie w programie obsługi przerwania. I ku mojemu zdziwieniu, po uruchomieniu programu, pomimo tego, że drążki były w położeniu zerowym, dwa silniki zaczęły się obracać. Okazało się, że obsługa przerwania obliczającego czas trwania PWM na pierwszym kanale trwała tak długo, że sztucznie opóźniała wywołanie drugiego przerwania wykonującego te same obliczenia dla drugiego kanału, przez co podawało ono zawyżone wartości. Wystarczyło przebudować program w taki sposób, by obsługa silników znalazła się poza programami obsługi przerwań i wszystko wróciło do normy. Pamiętajmy - to może oczywiste, ale mimo wszystko warto to powiedzieć - że mikrokontrolery nie są wielowątkowe, a przerwania nie są wątkami: wykonują się tylko jedno na raz. Drugie przerwanie musi czekać, aż pierwsze zostanie do końca obsłużone. Przed napisaniem końcowego programu pozostało mi już tylko przetestować obsługę czujnika ruchu, programik wygląda następująco: #include <NewPing.h> #define ULTRASONIC_TRIGGER_PIN 9 #define ULTRASONIC_ECHO_PIN 10 #define MAX_DISTANCE 400 NewPing sonar(ULTRASONIC_TRIGGER_PIN, ULTRASONIC_ECHO_PIN, MAX_DISTANCE); void setup() { pinMode(LED_BUILTIN, OUTPUT); } void loop() { digitalWrite(LED_BUILTIN, HIGH); delay(50); digitalWrite(LED_BUILTIN, LOW); delay(50); int ping = sonar.ping_cm(); if (ping < 30) digitalWrite(LED_BUILTIN, HIGH); else digitalWrite(LED_BUILTIN, LOW); delay(200); } Również i tu korzystam z wbudowanej diody, która powinna zapalić się, gdy zmierzona przez czujnik odległość będzie mniejsza niż 30 cm. Wreszcie program obsługujący całego robota: #include <NewPing.h> #include "EnableInterrupt.h" #include "NewPing.h" // Motor constants // Motor1 Speed #define MOTOR_1_SPEED_PIN 3 // Motor2 Speed #define MOTOR_2_SPEED_PIN 11 // Motor3 Speed #define MOTOR_3_SPEED_PIN 5 // Motor4 Speed #define MOTOR_4_SPEED_PIN 6 // Motor1 Direction #define MOTOR_1_DIR_PIN 4 // Motor2 Direction #define MOTOR_2_DIR_PIN 12 // Motor3 Direction #define MOTOR_3_DIR_PIN 8 // Motor4 Direction #define MOTOR_4_DIR_PIN 7 // Ultrasonic constants #define ULTRASONIC_TRIGGER_PIN 9 #define ULTRASONIC_ECHO_PIN 10 #define MAX_DISTANCE 400 // Servo constants #define SERVO_PIN 13 // AI constants #define DISTANCE_THRESHOLD_2 80 #define DISTANCE_THRESHOLD_1 40 // Ultrasonic control NewPing sonar(ULTRASONIC_TRIGGER_PIN, ULTRASONIC_ECHO_PIN, MAX_DISTANCE); // Motor control functions void M1_advance(byte Speed) ///<Motor1 Advance { digitalWrite(MOTOR_1_DIR_PIN, HIGH); analogWrite(MOTOR_1_SPEED_PIN, Speed); } void M2_advance(byte Speed) ///<Motor2 Advance { digitalWrite(MOTOR_2_DIR_PIN, LOW); analogWrite(MOTOR_2_SPEED_PIN, Speed); } void M3_advance(byte Speed) ///<Motor3 Advance { digitalWrite(MOTOR_3_DIR_PIN, LOW); analogWrite(MOTOR_3_SPEED_PIN, Speed); } void M4_advance(byte Speed) ///<Motor4 Advance { digitalWrite(MOTOR_4_DIR_PIN, HIGH); analogWrite(MOTOR_4_SPEED_PIN, Speed); } void M1_back(byte Speed) ///<Motor1 Back off { digitalWrite(MOTOR_1_DIR_PIN, LOW); analogWrite(MOTOR_1_SPEED_PIN, Speed); } void M2_back(byte Speed) ///<Motor2 Back off { digitalWrite(MOTOR_2_DIR_PIN, HIGH); analogWrite(MOTOR_2_SPEED_PIN, Speed); } void M3_back(byte Speed) ///<Motor3 Back off { digitalWrite(MOTOR_3_DIR_PIN, HIGH); analogWrite(MOTOR_3_SPEED_PIN, Speed); } void M4_back(byte Speed) ///<Motor4 Back off { digitalWrite(MOTOR_4_DIR_PIN, LOW); analogWrite(MOTOR_4_SPEED_PIN, Speed); } // PWM control volatile int microsYStart = 0; volatile int microsXStart = 0; volatile int microsZStart = 0; volatile int yMicros = 0; volatile int xMicros = 0; volatile int zMicros = 0; int UScounter = 0; int USlock = 0; // Pin 0 interrupt handling void pin0Rising() { microsYStart = micros(); disableInterrupt(0); enableInterrupt(0, pin0Falling, FALLING); } void pin0Falling() { yMicros = micros() - microsYStart; disableInterrupt(0); enableInterrupt(0, pin0Rising, RISING); } // Pin 1 interrupt handling void pin1Rising() { microsXStart = micros(); disableInterrupt(1); enableInterrupt(1, pin1Falling, FALLING); } void pin1Falling() { xMicros = micros() - microsXStart; disableInterrupt(1); enableInterrupt(1, pin1Rising, RISING); } // Pin2 interrupt handling void pin2Rising() { microsZStart = micros(); disableInterrupt(2); enableInterrupt(2, pin2Falling, FALLING); } void pin2Falling() { zMicros = micros() - microsZStart; disableInterrupt(2); enableInterrupt(2, pin2Rising, RISING); } void setup() { for (int i = 3; i < 9; i++) pinMode(i, OUTPUT); for (int i = 11; i < 13; i++) pinMode(i, OUTPUT); pinMode(ULTRASONIC_TRIGGER_PIN, OUTPUT); pinMode(ULTRASONIC_ECHO_PIN, INPUT); pinMode(SERVO_PIN, OUTPUT); enableInterrupt(0, pin0Rising, RISING); enableInterrupt(1, pin1Rising, RISING); enableInterrupt(2, pin2Rising, RISING); } void loop() { // Eval motor signals int yValue = (yMicros - 1500) / 2; int xValue = (xMicros - 1500) / 2; if (yValue > 260 || yValue < -260) yValue = 0; if (xValue > 260 || xValue < -260) xValue = 0; int leftEngines = constrain(xValue + yValue, -250, 250); int rightEngines = constrain(yValue - xValue, -250, 250); // Check for obstacles every 10th iteration UScounter++; if (UScounter >= 10) { UScounter = 0; int frontDistance = sonar.convert_cm(sonar.ping_median(5)); if (frontDistance != 0 && frontDistance < DISTANCE_THRESHOLD_2) { if (frontDistance > DISTANCE_THRESHOLD_1) USlock = 1; else USlock = 2; } else USlock = 0; } if (USlock == 1) { leftEngines = constrain(leftEngines, -250, 128); rightEngines = constrain(rightEngines, -250, 128); } if (USlock == 2) { leftEngines = constrain(leftEngines, -250, 0); rightEngines = constrain(rightEngines, -250, 0); } if (abs(leftEngines) < 20) { M3_advance(0); M4_advance(0); } else if (leftEngines > 0) { M3_advance(leftEngines); M4_advance(leftEngines); } else { M3_back(-leftEngines); M4_back(-leftEngines); } if (abs(rightEngines) < 20) { M1_advance(0); M2_advance(0); } else if (rightEngines > 0) { M1_advance(rightEngines); M2_advance(rightEngines); } else { M1_back(-rightEngines); M2_back(-rightEngines); } } Pozwala on na kontrolowanie robota przy pomocy jednego drążka aparatury (dwóch kanałów). Oprócz tego program cyklicznie sprawdza odległość przed robotem i zabezpiecza przed wjechaniem w ścianę: w przypadku przeszkody znajdującej się bliżej niż 80 cm od robota, zostanie ograniczona jego maksymalna prędkość, natomiast jeżeli odległość ta będzie mniejsza niż 40cm, robot zatrzyma się całkowicie i niemożliwe będzie ruszenie nim do przodu. Wnioski To była prawdziwa frajda zobaczyć, jak robot rusza i jeździ zgodnie z instrukcjami z aparatury! Pierwszy skończony projekt. Nauczyłem się na nim dużo, bo okazało się, że w trakcie pracy podjąłem bardzo dużo nietrafnych decyzji. Robot tak naprawdę nigdy nie wyjechał z domu, ma bardzo otwartą konstrukcję, która sprzyja dostawaniu się do obudowy pyłu i piachu. W domowych warunkach wystarczyłby natomiast napęd na dwa koła - w ten sposób miałbym też trochę miejsca wewnątrz obudowy. Arduino Leonardo jest świetne, ale wielkie. Znacznie lepiej sprawdziłoby się Arduino Micro albo Teensy. To drugie nawet bardziej, bo shield do sterowania silnikami pożera dużo pinów i niewiele zostaje na odbiór sygnału z odbiornika RC. Udało mi się jeszcze podłączyć czujnik odległości, ale serwo wpiąłem już bezpośrednio w odbiornik, bo po prostu zabrakło mi pinów. Nie ma większego sensu robić żadnych projektów (innych niż wstępne prototypy) na przewodzikach połączeniowych. Lepiej kupić sobie płytkę prototypową, złącza goldpinowe, przewodziki kydexowe i polutować wszystko na płytce - układ zajmuje znacznie mniej miejsce i jest znacznie mniej podatny np. na przypadkowe wyjęcie przewodu. Chodzi mi po głowie wskrzeszenie projektu - właśnie przy pomocy Teensy oraz drukarki 3D, przy pomocy której mogę wydrukować całe nadwozie szyte na miarę. Ale podejrzewam, że zajmę się tym dopiero za jakiś czas...
  3. Raport mniejszości Oglądaliście kiedyś ten film? Główny bohater posługiwał się tam futurystycznym (przynajmniej jak na tamte czasy) interfejsem, którym sterował przy pomocy gestów. Fragment, który mam na myśli, możecie obejrzeć na Youtube. Osiągnięcie takiego poziomu zaawansowania wymaga oczywiście lat pracy, ale możemy spróbować skonstruować urządzenie, które będzie namiastką opisanego powyżej interfejsu. Koncepcja Zbudujemy urządzenie typu pan-tilt, czyli platformę obracającą się w dwóch osiach, sterowaną serwami. Sterowanie odbywać się będzie zdalnie, poprzez przechylanie telefonu komórkowego - platforma będzie próbowała naśladować gesty wykonywane przy pomocy telefonu. Platforma Na początku sama platforma - zdecydowałem się zaprojektować własną od zera, bazując na projekcie znalezionym na Thingiverse. Obrót w osi poziomej jest zmniejszony przez zębatkę - pozwala ona mniej więcej na 1/3 oryginalnego zakresu ruchu, ale za to jest on bardziej płynny. Obrót wokół osi pionowej jest natomiast nieograniczony. Pewnym kłopotem okazały się zębatki - przygotowanie dobrego koła zębatego wbrew pozorom wcale nie jest tak prostym zadaniem, jakim mogłoby się wydawać. Co więcej, nie ma również zbyt dużo narzędzi pozwalających na wygenerowanie prawidłowych kół zębatych. Po długich poszukiwaniach udało mi się jednak odnaleźć odpowiednie narzędzie - umożliwia ono konstruowanie kół zewnętrznych, wewnętrznych oraz listew zębatych: Gear Bakery. Wygenerowane koła zębate zapisywane są jako pliki .dxf, a te można już bez problemu wciągnąć do Fusion 360. Założyłem, że platforma będzie obracana zwykłymi, małymi serwami modelarskimi. Docelowo planowałem postawić na niej co najwyżej sportową kamerkę, więc moment rzędu 1.8 kg*cm jest tu całkowicie wystarczający. Pozostało wymodelowanie i wydrukowanie części, z których złożyłem potem platformę. Kontroler Tym razem potrzebowałem trochę więcej mocy obliczeniowej niż w wypadku prostszych projektów - w końcu musiałem postawić serwer odbierający dane po UDP - zdecydowałem się więc na Raspberry Pi. Serwer napisałem w Pythonie, bo język ten jest stosunkowo wysokopoziomowy i mocno separuje programistę od warstwy systemu i sprzętu: mogłem dzięki temu skupić się na istocie zadania, a nie na detalach implementacyjnych. Raspberry ma jednak pewną wadę - nie za dobrze radzi sobie z generowaniem sygnału PWM - sprzętowo może robić to tylko na jednym pinie. Co gorsza, standardowa biblioteka RPi.GPIO pozwala jedynie na programowe generowanie PWM - nie dawało to zbyt dobrych efektów. Na szczęście problem ten można bardzo elegancko obejść. Firma Adafruit przygotowała bowiem cwany układzik, który pozwala na sterowanie przy pomocy PWM aż dwunastoma niezależnymi urządzeniami, natomiast komunikacja tego układu z Raspberry odbywa się już po moim ulubionym protokole, czyli I2C. Układ ten rozwiązał jeszcze jeden problem, którego istnienie uświadomiłem sobie tak naprawdę w momencie, gdy właśnie zniknął. Serwa trzeba przecież jakoś zasilić, a nie można podłączyć ich do pinu 5V Raspberry - mają przecież znacznie większe zapotrzebowanie prądowe. PCA9685 w tym celu wyprowadza jednak szybkozłączkę, do której można podpiąć zewnętrzne zasilanie. Za każdym razem, gdy potrzebuję gdzieś 5V, zwykle robię mały przekręt: korzystam z kontrolera silnika bezszczotkowego, do którego przylutowałem wtyczkę T-Dean. Oprócz tego - co oczywiste - że ESC służy do sterowania silnikiem, obniża on również napięcie do 5V i przekazuje przewodami, które normalnie podłącza się do odbiornika RC. W pierwotnym zamyśle mechanizm ten nosił nazwę BEC - Battery Eliminator Circuit - i pozwalał w łatwy sposób zasilić odbiornik RC bez użycia dodatkowego akumulatora. Tymczasem wystarczy ominąć pin sterowania i mamy mały, wygodny, mobilny zasilacz na 5V. Konfiguracja Raspberry Aby mój skrypt w Pythonie uruchomił się, konieczne jest doinstalowanie biblioteki Adafruit pozwalającej na sterowanie kontrolerem PCA9685. Na początku trzeba oczywiście włączyć szynę I2C; robimy to w oknie konfiguracji Raspberry. Potem upewniamy się, że zainstalowane są następujące pakiety: sudo apt-get install python-smbus sudo apt-get install i2c-tools Po zainstalowaniu i2c-tools możemy użyć programiku i2cdetect, żeby sprawdzić, czy Raspberry prawidłowo wykrył kontroler PCA9685: ~ $ sudo ic2detect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: 70 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- Teraz możemy ściągnąć i zainstalować bibliotekę do Pythona. sudo apt-get install build-essential python-dev cd ~ git clone https://github.com/adafruit/Adafruit_Python_PCA9685.git cd Adafruit_Python_PCA9685 sudo python setup.py install Uruchamiamy Do uruchomienia całego urządzenia potrzebujemy dwóch kawałków kodu, jednego mniejszego, drugiego większego. Tym mniejszym jest serwer w Pythonie, który steruje serwami. Możecie go ściągnąć z mojego repozytorium albo po prostu przekleić, bo jego kod jest stosunkowo krótki: # -*- coding: utf-8 -*- import socket import sys import time import Adafruit_PCA9685 # Częstotliwość sterowania PWM FREQUENCY = 50 # Czas jednego cyklu CYCLE_TIME = 1 / float(FREQUENCY) # Procentowy czas cyklu dla położenia minimalnego SERVO_MIN_PERCENT = 0.00055 / CYCLE_TIME # Procentowy czas cyklu dla położenia maksymalnego SERVO_MAX_PERCENT = 0.00245 / CYCLE_TIME # Wartość minimalna dla 4096 kroków SERVO_MIN = int(4096 * SERVO_MIN_PERCENT) # Wartość maksymalna dla 4096 kroków SERVO_MAX = int(4096 * SERVO_MAX_PERCENT) # Tworzymy obiekt pwm (przyjmie domyślnie adres 0x40) pwm = Adafruit_PCA9685.PCA9685() pwm.set_pwm_freq(FREQUENCY) def setAngle(channel, angle): pwm.set_pwm(channel, 0, int(SERVO_MIN + (float(angle) / 180.0) * (SERVO_MAX - SERVO_MIN))) def processCommand(command): angles = command.split("|") angles = list(map(lambda x: max(-90, min(90, -int(x.strip()))) + 90, angles)) print("P: {0}, Y: {1}".format(angles[0], angles[2])) setAngle(14, int(angles[0] / 10) * 10) setAngle(15, int(angles[2] / 10) * 10) def main(): # Create TCP/IP socket sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Bind the socket to the port server_address = ('', 10000) sock.bind(server_address) print("Waiting for data...") buffer = "" try: while True: data, addr = sock.recvfrom(32); buffer = buffer + data commandEnd = buffer.find(";") if (commandEnd != -1): command = buffer[0:commandEnd] buffer = buffer[commandEnd+1:] processCommand(command) except KeyboardInterrupt: print('Ctrl+C received, closing...') # Start program main() Drugi program jest już większy - to aplikacja na Androida, która pozwala na podłączenie się do serwera i przesyłanie informacji o orientacji telefonu. Ta również dostępna jest w moim repozytorium, tylko tym razem trzeba kod już ściągnąć i przebudować w Android Studio. Efekt Końcowy efekt na zdjęciu będzie wyglądał marnie, dlatego przygotowałem krótki filmik prezentujący, jak uruchomić cały projekt i jakie są efekty jego działania: Wnioski Serwa są bardzo wygodne w montażu i obsłudze, ale niestety również mało precyzyjne. Do takiego prostego projektu nadają się świetnie, ale w warunkach produkcyjnych trzeba byłoby wymienić je na ciche i dokładne silniki bezszczotkowe - takie, jakie są używane w gimbalach. Platforma reaguje na specyficzne ułożenie komórki; dzieje się tak dlatego, że komórka obrót wokół osi pionowej wysyła jako odchylenie od północy (0 stopni to północ). Tymczasem platforma "nie wie", w którą stronę jest zwrócona. Aby projekt był bardziej uniwersalny, można zastosować jedno z dwóch rozwiązań: dodać czujnik orientacji do platformy i kompensować różnice albo zapamiętać ułożenie komórki podczas łączenia do serwera i uznać je za pozycję "zerową". Mojemu projektowi daleko oczywiście do zaprezentowanego rozwiązania w "Raporcie mniejszości", ale nie ukrywam, że dał mi sporo frajdy. Można wykorzystać go jako bazę do zdalnego sterowania położeniem kamery lub na przykład ramienia robota.
  4. Elektroniczny Multitool Jak się zaczęło? Ano zaczęło się od tego, że zanabyłem sobie hulajnogę elektryczną. Długo i sumiennie przeglądałem różne recenzje, opisy, wertowałem informacje, aż w końcu zdecydowałem się na Airwheel Z5 Strong, którą na marginesie polecam. Pomimo wielu zalet - w tym kilku rozwiązań unikalnych w tym segmencie urządzeń, jak na przykład wygodne podesty albo łatwo wymienny akumulator - ma jednak pewną niewielką wadę: brak wyświetlacza informującego na przykład o bieżącej prędkości czy o przejechanej drodze. Ponieważ od jakiegoś czasu bawię się Arduino, stwierdziłem, że stosunkowo łatwo powinno dać się skonstruować taki licznik samodzielnie - wystarczy przecież zastosować GPS, który uniezależnia mnie od rozwiązań zastosowanych w hulajnodze. Zacząłem więc powoli kolekcjonować wszystkie niezbędne komponenty. Wyświetlacz Zacząłem trochę nietypowo, bo od wyświetlacza. Zrobiłem już kilka projektów na wyświetlaczach znakowych, ale - oczywiście - nie dają one takich możliwości, jak solidny, kolorowy, a najlepiej dotykowy wyświetlacz. Mój wybór padł na dotykowy wyświetlacz 2.4", pracujący z rozdzielczością 320x240 i komunikujący się przez popularny protokół SPI. Mój wygląda nieco inaczej niż ten w sklepie, ale różnią się płytką z kontrolerem, wyświetlacz jest ten sam. Kontroler Obejrzałem sobie sporo filmików dotyczących podłączania wyświetlaczy do Arduino i uderzyło mnie to, że w większości przypadków liczba klatek na sekundę osiągała bardzo często wartości jednocyfrowe. Jest to zrozumiałe - wszelkie Uno, Leonardo, Micro i tak dalej są taktowane ze stosunkowo małymi częstotliwościami, a przynajmniej zbyt małymi, by osiągnąć zadowalające rezultaty graficzne. Dlatego tym razem porzuciłem całę rodzinę *ino na rzecz chyba nieco mniej znanego kontrolera, Teensy 3.5. Kosztuje swoje, ale dostajemy naprawdę ciężką artylerię: 120 Mhz, 512 kB Flash, 192 kB RAMu, 62 piny, zintegrowany zegar czasu rzeczywistego (trzeba go tylko zasilić bateryjką CR2032), czytnik kart microSD i oczywiście możliwość programowania przez micro USB, bez konieczności stosowania programatorów. Polecam, korzystanie z takiego kombajnu to naprawdę czysta przyjemność. GPS Ta decyzja była stosunkowo prosta, zdecydowałem się na rozwiązanie ekonomiczne i kupiłem prosty GPS komunikujący się przez UART (szeregowo). Przyznam szczerze, że po zakupie Teensy trochę szkoda było mi poświęcić taki świetny kontroler li tylko na licznik do hulajnogi. Dlatego zanabyłem jeszcze popularny czujnik pogodowy BME280 i stwierdziłem, że w czasie, gdy nie będę korzystał z urządzenia jako licznika, zrobię z niego małą stację pogodową. Na wyposażeniu urządzenia znalazł się również czujnik położenia MPU9250, bo zamierzałem napisać rejestrator pochyłości podłoża (i ewentualnie również jego jakości), ale w końcu z tego pomysłu zrezygnowałem. Być może za jakiś czas dopiszę sobie do urządzenia cyfrową poziomicę. Konstrukcja Zacząłem od płytki prototypowej, która błyskawicznie zamieniła się na totalnie zagmatwaną pajęczą sieć przewodów kydexowych. Myślałem na początku, że poprzestanę na niej, ale plątanina ta wyglądała tak dramatycznie, że z pomysłu w końcu zrezygnowałem, traktując go tylko jako prototyp. Sprawdziłem, czy wszystkie urządzenia będą pracowały prawidłowo i generalnie każdy moduł udało mi się uruchomić i pobrać z niego dane, z tym wyjątkiem, że BME280 i MPU9250 odmawiały współpracy podłączone do jednej szyny I2C. Szczęśliwie Teensy oferuje tych szyn chyba z pięć, więc przepiąłem BME na inną i wszystko wstało bez problemu. Kiedy wszystko działało, siadłem do Eagle'a i zaprojektowałem PCB, który miał zastąpić tę nieszczęsną płytkę prototypową. W Polsce jest kilka firm, które wykonują nawet pojedyncze sztuki takich płytek, a moją zamówiłem w firmie Satland Prototype. Musiałem poczekać dwa tygodnie, ale w końcu płytka doszła pocztą i mogłem zacząć lutować. Jakiś czas temu wyposażyłem się w drukarkę 3D, więc po zlutowaniu (i uruchomieniu) wszystkiego siadłem do Fusion 360 i zaprojektowałem obudowę na całe urządzenie. Zadbałem o to, żeby BME został odseparowany wewnętrzną ścianką od reszty urządzenia, bo ekran LCD potrafi się grzać i zafałszowuje odczyty. GPS zamocowałem w osobnym miejscu, wsunięty w odpowiedni slot i zabezpieczony ścianką z drugiej strony, ponieważ niestety wersja, którą kupiłem, nie posiadała żadnych otworów montażowych. Płytka z komponentami siedzi w przygotowanym na jej wymiary wgłębieniu i jest ściśnięta pomiędzy dwiema częściami obudowy, więc nie ma możliwości się przesunąć. Do tego otwór na wyświetlacz, siateczka w przedniej ściance przepuszczająca powietrze do czujnika pogodowego i tyle. Słowo komentarza, bo pewnie padnie takie pytanie: schemat dotyczy nowej wersji płytki z usuniętym radyjkiem NRF, na zdjęciu jest starsza wersja - szczegóły na końcu artykułu. Programowanie Proces programowania musiałem rozłożyć w czasie, ale projekt szedł powoli do przodu. Zacząłem od części pogodowej, ogrom miejsca w programowalnej pamięci kontrolera pozwolił mi poszaleć, więc przygotowałem sobie w C++ klasy odpowiedzialne za obsługę interfejsu użytkownika, spięcie różnych modułów w jedną całość, wyświetlanie grafiki i tak dalej. Również i tym razem dałem się ponieść - nie mogłem znaleźć w Internecie dobrego programu do generowania czcionek w formacie zrozumiałym dla biblioteki ILI9341_t3 przygotowanej przez twórcę Teensy (działa ona szybciej od regularnej biblioteki), a zależało mi na czcionce siedmiosegmentowej - takiej trochę "retro" (widać na screenach). Zapytałem więc Paula o kilka rzeczy, siadłem któregoś wieczoru i wyrzeźbiłem edytor czcionek dla ILI9341_t3. Mój prosty programik może się podobać lub nie, ale najlepszą alternatywą jaką znalazłem jest gigantyczne makro w Excelu, więc sami rozumiecie... Dużym odkryciem było dla mnie, że na platformę Arduino można już programować w Visual Studio Code - Arduino Studio jest wciąż wymagane (bo zawiera wszystkie narzędzia potrzebne do zbudowania programu dla kontrolerka), ale tak naprawdę wszystkie operacje - budowanie, wrzucanie na płytkę, monitor portu szeregowego - można już obsłużyć bezpośrednio w tym świetnym środowisku. Tylko że... niestety Teensy nie jest całkowicie zgodne z Arduino i do budowania na tę platformę potrzebny jest dodatkowy programik, Teensyduino, który nieco modyfikuje Arduino Studio i nie jest kompatybilny z VS Code. Tym niemniej, większość developmentu zrealizowałem w tym ostatnim, jedynie budując i wrzucając aplikację na urządzenie przy pomocy AS. Zastosowałem też własny mechanizm podwójnego buforowania: cały obraz buduję w RAMie i wyświetlam go hurtem w jednym kroku. Ogranicza to trochę lag związany z wyświetlaniem poszczególnych elementów, opóźnienie wciąż jest, ale jest znacznie mniej zauważalne. Kosztowało mnie to prawie połowę pamięci RAM, ale z uwagi na to, że pozostałe komponenty aplikacji prawie go nie zużywają, mogłem sobie na to pozwolić. Źródła trzymam na Gitlabie, chcecie obejrzeć - sklonujcie sobie repo. Efekt W chwili obecnej płytka w trybie stacji pogodowej wyświetla klasycznie: temperaturę, ciśnienie i wilgotność, a także wykresy: bieżący (jasna linia - jeden piksel to 15 sekund) i historia - 1h, 8h lub 24h wstecz.Tryby można przełączać po dotknięciu ekranu - pojawia się wtedy proste menu. Aplikacja wyposażona jest w wygaszacz ekranu, który zapobiega wypaleniu pikseli - szczególnie, że w trybie stacji pogodowej bardzo dużo z nich mogłoby być na to narażone. Po 30 sekundach ekran się wyłącza (wygasza), a włącza ponownie po dotknięciu. Niestety nie udało mi się wyłączyć jego podświetlenia - doczytałem, że w tym ekranie programowo nie da się tego zrobić. Szkoda. W trybie GPS wyświetlane są na razie podstawowe informacje wczytane z odbiornika: prędkość, wysokość n.p.m., kierunek jazdy, liczba satelit, z których odczytywane są dane oraz położenie geograficzne - długość i szerokość. Za jakiś czas dopiszę pewnie tryb "rowerowy", czyli przebyta droga, średnia i maksymalna prędkość i tak dalej. Aplikację mam napisaną na tyle modularnie, że teraz taki moduł mogę już sobie praktycznie niewielkim kosztem poskładać "z klocków". Problemy Człowiek uczy się na błędach, a mądry człowiek na błędach innych. Podczas rozwijania tego projektu nadziałem się na mnóstwo problemów, więc podzielę się moimi rozwiązaniami - być może komuś oszczędzi to czasu i nerwów. Po pierwsze, zasilanie - w dwóch odsłonach. Pierwsza odsłona - pierwotnie na płytce znajdowało się jeszcze radyjko NRF24, ponieważ jakiś czas temu zbudowałem sobie "satelitę" - małą płytkę z czujnikiem temperatury i wilgotności zasilaną z bateryjki CR2032: miałem nadzieję, że w trybie stacji pogodowej urządzenie będzie również ściągało informacje z tego źródła. Testy bez GPSu wypadły pozytywnie - to widać na zdjęciach - ale po podłączeniu wszystkiego i włączeniu zasilania, ekran rozbłysł na chwilę, a potem powolutku przygasł i zgasł całkowicie. Podejrzewałem od razu NRF, chciałem go rozlutować, ale w trakcie tego procesu oderwałem jeden pad na płytce, więc nie miałem już możliwości łatwego sprawdzenia, czy to on jest winien. Żeby uniknąć dalszych uszkodzeń, podrzuciłem płytkę ojcu, który w pracy ma wysokiej klasy rozlutownicę, a ja zaprojektowałem w międzyczasie drugą, tym razem już bez NRFa. Warto zawsze sprawdzić, czy uda się zasilić wszystkie komponenty - ja leniwie pociągnąłem zasilanie wszystkiego z płytki, a mogłem przecież wstawić na płytkę port microSD i pociągnąć dodatkowe ścieżki bezpośrednio z niego - wtedy obciążyłbym zasilacz (który pewnie nawet by nie sapnął), a nie samą płytkę. Druga kwestia - hulajnoga, którą kupiłem, ma w baterii wyjście USB - można (według instrukcji) doładowywać sobie na przykład komórkę (traktować akumulator jako powerbank). Również do tego portu podłączana jest latareczka, którą można sobie włączyć podczas jazdy. Niestety podczas prób terenowych okazało się, że przysiad napięcia podczas rozpędzania jest tak duży, że płytka gaśnie albo się zawiesza. Musiałem więc zasilić ją z osobnego powerbanku, w przyszłości pomyślę nad zasileniem płytki z osobnego akumulatora albo po prostu baterii. Teraz BME280 - małe ostrzeżenie. Układzik jest rewelacyjny, malutki i stosunkowo dokładny... ale się grzeje! Potrafi rozgrzać się na tyle, że zafałszowuje informacje o temperaturze o 1.5-2 stopnie. Rozwiązanie (proponowane nawet przez Boscha) polega na zmniejszeniu dokładności pomiaru (urządzenie może działać w trybie oversampling, czyli robi np. 8 pomiarów i uśrednia - należy to wyłączyć), wyłączeniu filtrowania (które również poprawia jakość pomiarów), przełączyć się w tryb "forced" - wtedy płytka robi pomiar tylko na życzenie - i ograniczyć pomiary do jednego na minutę (!). Ja nie ograniczyłem się tak bardzo, pomiary mam co 15 sekund, ale mimo wszystko układ się grzeje i na początku na wykresie widać delikatny skok temperatury, który po chwili się stabilizuje. Jest również prawdopodobne, że grzeje się nie tyle BME, co wyświetlacz - podobny problem miałem w innej stacji pogodowej z wyświetlaczem znakowym. Tam temperatura skakała do góry o kilka stopniu po uruchomieniu. Kwestia estetyki - nie zadbałem o to, żeby schować obramowanie ekranu z widocznymi ścieżkami panelu dotykowego. W innych projektach nie popełniam już tego błędu, widoczny jest sam ekran (wygląda to o niebo lepiej). Ogólnie jednak jestem zadowolony, płytka jest rozwojowa, a nowe funkcjonalności mogę dodawać programowo, więc podejrzewam, że wzbogacę ją jeszcze o kilka dodatkowych trybów pracy.
×