Skocz do zawartości

Przeszukaj forum

Pokazywanie wyników dla tagów 'Raspberry Pi'.

  • Szukaj wg tagów

    Wpisz tagi, oddzielając przecinkami.
  • Szukaj wg autora

Typ zawartości


Kategorie forum

  • Elektronika i programowanie
    • Elektronika
    • Arduino, ESP
    • Mikrokontrolery
    • Raspberry Pi
    • Inne komputery jednopłytkowe
    • Układy programowalne
    • Programowanie
    • Zasilanie
  • Artykuły, projekty, DIY
    • Artykuły redakcji (blog)
    • Artykuły użytkowników
    • Projekty - roboty
    • Projekty - DIY
    • Projekty - DIY (początkujący)
    • Projekty - w budowie (worklogi)
    • Wiadomości
  • Pozostałe
    • Oprogramowanie CAD
    • Druk 3D
    • Napędy
    • Mechanika
    • Zawody/Konkursy/Wydarzenia
    • Sprzedam/Kupię/Zamienię/Praca
    • Inne
  • Ogólne
    • Ogłoszenia organizacyjne
    • Dyskusje o FORBOT.pl
    • Na luzie
    • Kosz

Szukaj wyników w...

Znajdź wyniki, które zawierają...


Data utworzenia

  • Rozpocznij

    Koniec


Ostatnia aktualizacja

  • Rozpocznij

    Koniec


Filtruj po ilości...

Data dołączenia

  • Rozpocznij

    Koniec


Grupa


Znaleziono 33 wyników

  1. Czy mógbym prosić o pomoc w konfiguracji domoticza v4.9700 z czujnkiem BME280 podłączonym do Raspberry Pi. Czujnik wyświetla wartości w Raspberry lecz nie mogę ich odczytać w domoticzu. Wybór w domoticzu i2c sensors lub wirtualnego czujnika nie przynosi efektów, w logach wyświetla się "Error: I2C_BME280: Error Writing to I2C register". Jaki plik należy stworzyć i gdzie aby odczyty pojawiły się w domoticzu? Dziękuję za pomoc.
  2. Proponuję rozpocząć kolejną "luźną" dyskusję. Arduino i Raspberry Pi zdecydowanie zrewolucjonizowały elektronikę w kontekście hobbystów i twórców, ale oczywiście znalazły one również swoje miejsce przy bardziej profesjonalnych zastosowaniach. Następnym przełomem, który się wydarzył (a właściwie trwa teraz) była popularyzacja ESP, czyli taniego, niezwykle wydajnego układu, który pozwala tworzyć urządzenia IoT. Moje pytanie brzmi więc następująco: jakie cechy Waszym zdaniem, będzie miała kolejna platforma, która przebojem wejdzie na rynek? Czego według Was brakuje? Większej wydajności, energooszczędności, a może jeszcze niższej ceny? Jak wiadomo przełomem będą np. komputery kwantowe, ale nie o takie rzeczy mi teraz chodzi. Pytam o realne (na dzisiejsze czasy) platformy, które mogłyby łatwo zagościć w warsztacie każdego majsterkowicza. Czego brakuje Wam w aktualnie popularnych platformach?
  3. Hej, przeinstalowałem dziś całą moją malinkę, jako, że nie chcę nadawać użytkownikowi pi uprawnień roota, za co mnie ostatnio zjechaliście, proszę o pomoc przy konfiguracji. Chodzi o winSCP, jeśli zaloguję się jako pi to nie mogę nic zmieniać w katalogu var/www/html gdzie chcę umieścić swoją stronę www. Jak zmienić uprawnienia, próbowałem właściwości i zmiana praw dostępu, ale permission denied... jak to zrobić?
  4. Dzień Dobry. Jestem początkujący jeśli chodzi o elektronikę i bardzo proszę o radę. Chciałbym wykonać pewien projekt. Raspberry PI z wyświetlaczem + program w GTK+ lub Qt sterujący czterema silniczkami 24v. Aktualnie mam to zrobione prawie wyłącznie za pomocą płytki https://allegro.pl/oferta/telaml-xl4016-zasilacz-regulowany-max-10a-35v-7402283468 i w sumie działa. Zależy mi jednak na tym, aby nie kręcić śrubokrętem, tylko podłączyć wyświetlacz dotykowy, raspberry i dowolnie sterować obrotami silników (od 0 do 24v) za pomocą programu z GUI. Jeśli chodzi o część z programem GUI to nie ma problemu. Pytanie jakich komponentów użyć oraz co i jak podłączyć do Raspberry aby to mogło zadziałać. Z góry dziękuję za wyrozumiałość i pomoc Mateusz
  5. Witam, Chciałbym przedstawić zbudowany ostatnio pojazd inspekcyjny. Założenia były następujące: dobra mobilność w nierównym terenie, sterowanie za pomocą aplikacji na Android'a oraz podgląd z wbudowanej kamery w czasie rzeczywistym. Mechanika W pojeździe zastosowano uproszczoną, 4-kołową wersję zawieszenia rocker-bogie stosowaną m.in. w łazikach marsjańskich. Główną zaletą tego rozwiązania jest niemal równomierny nacisk wszystkich kół na podłoże oraz możliwość uniesienia koła na przeszkodzie. Zastosowane koła pochodzą od "jakiejś" zabawki (znalezione na strychu ) i są zamocowane adapterem hex 12 mm (z małymi przeróbkami, aby schować przekładnię wewnątrz koła). Każde koło posiada własny silnik DC z przekładnią - prędkość wyjściowa: ok. 120 obr/min przy zasilaniu 12V. Silniki zostały zamocowane do aluminiowego profilu kwadratowego 10 mm za pomocą opasek zaciskowych. Profil ten stanowi część wahacza przymocowanego do osi kadłuba przez łożyska z wiertarek (średnica wewnętrzna 6 mm). Z tyłu pojazdu widoczna jest belka różnicowa łącząca wahacze po obu stronach. Dzięki niej kadłub utrzymywany jest w swojej pozycji a wychylenie jednego wahacza powoduje odchylenie drugiego o taki sam kąt przeciwnie skierowany. Jako kadłub wykorzystano obudowę z ABS. Do jej wieczka przymocowano również maszt z kamerą sterowany w dwóch osiach. Elektronika Komputerem sterującym w pojeździe jest Raspberry Pi Zero W z systemem Raspbian w wersji Jessie. Zastosowano ten model z powodu małych rozmiarów, stosunkowo niskiej ceny i małego poboru mocy. Z racji braku przetwornika ADC, zastosowano również arduino w wersji Pro Mini o napięciu 3.3V (aby było zgodne ze standardem w Raspberry). Są również 2 sterowniki silników na bazie modułu L298N (Sterownik ten ma dwa kanały i można było zastosować tylko jeden sterownik, jednak z powodu niewystarczającej wydajności prądowej zastosowano dwa), 2 przetwornice step-down 5V (osobno dla logiki, i osobno dla serwomechanizmów), dwa serwomechanizmy TowerPro SG90, kamera, oraz pakiet 3S ogniw li-ion w rozmiarze 18650. Z racji tego, że kamera jest podłączana przez taśmę FFC, zastosowano również przejściówki z FFC na goldpin, aby nie uszkodzić taśmy podczas obracania kamerą. Oprogramowanie Arduino w tym pojeździe odpowiedzialne jest za odczyt aktualnego napięcia zasilania oraz generowanie sygnałów PWM dla prędkości silników oraz serwomechanizmów pozycjonowania kamery. To jaki sygnał ma być generowany, jest wiadome dzięki połączeniu z Raspberry poprzez UART. Dodatkową funkcją Arduino jest wyłączenie PWM dla silników w przypadku braku komunikacji z Raspberry co zapobiega niekontrolowanej jeździe pojazdu w przypadku np. zerwania zasięgu. Raspberry komunikuje się z użytkownikiem poprzez sieć WiFi (Malinka działa w trybie hot-spot'u). Program działający na Raspberry został napisany w Python'ie i wykorzystuje również biblioteki Flask, w celu utworzenia serwera. Odpowiednie fragmenty kodu są wykonywane po wywołaniu przypisanego do niego adresu. Do transmisji wideo wykorzystano platformę Gstreamer, która pozwala na strumieniowanie w formacie H.264. Dzięki temu udało się uzyskać płynny obraz przy 30 FPS i rozdzielczości 800x600 cechujący się niewielkim opóźnieniem (ok. 300 ms). Powstała również dedykowana aplikacja na system Android, gdzie widoczny jest podgląd z kamery oraz sterowanie pojazdem. Podsumowanie Powstały pojazd zgodnie z założeniami dobrze radzi sobie w terenie, jednak pozostawia również spore możliwości rozbudowy. Można np. dodać zewnętrzną antenę WiFi aby poprawić zasięg (obecnie jest to ok 50m na otwartej przestrzeni), diodę doświetlającą, czy też różne czujniki. Najprawdopodobniej następnym krokiem będzie dodanie przetwarzania obrazów, aby pojazd był w stanie podążać za danym obiektem i być może omijać przeszkody. Na koniec krótka prezentacja działania:
  6. Ostatnio wpadłem na pomysł zbudowania termometru "bazującego" na Raspberry Pi. Nie zamierzałem stworzyć takiego "typowego" termometru tylko coś więcej ! Założenia Postawiłem sobie kilka założeń: możliwość odczytu obecnej temperatury za pomącą pierścienia led możliwość pracy na baterii możliwość odczytu temperatury na innym urządzeniu Połączenie wszystkich elementów Pierwszym krokiem było złożenie wszystkiego w całość co szczerze mówiąc było najprostszym etapem. Podpiąłem termometr do malinki (jak to zrobić to chyba oczywiste więc nie będę szczegółowo tego tłumaczył) Jedyne co ważne to to żeby termometr był podpięty na wystarczająco długim przewodzie, jeśli chcemy żeby termometr znajdował się daleko od malinki (np. na dworze) Trochę trudniej było w przypadku ledów, ale po paru chwilach podłączyłem je do malinki w ten sposób: IN - GPIO18 (istnieje możliwość podpięcia tego elementu pod inny port GPIO, ważne jest żeby później wpisać w napisanym przez nasz skrypcie pod który podłączyliśmy) VCC - wyjście 5V malinki (producent nie zaleca zasilania pierścienia z malinki w przypadku większych ilości ledów jednak w przypadku dwunastu nie powinno nic się stać) GND - GND Pierścień posiada jeszcze trzy wyjścia ale nie będą mi potrzebne (służą do łączenia większej ilości takich pierścieni) Jako zasilanie wewnętrzne wykorzystałem starty power bank Nokii. Raspberry PI pobiera bardzo mało prądu więc nie potrzebujemy jakiejś dużej pojemności. Całość po złożeniu wyglądała miej więcej tak ⬆ Instalowanie Sterowników Największym wyzwaniem okazał się jednak Software. Sama instalacja wszelkich sterowników i bibliotek już na początku zajęła dość sporo czasu a później czekało mnie jeszcze napisanie programu i stworzenie witryny www co też było dosyć czasochłonne. Po aktualizacji oprogramowania najpierw zrobiłem: Pierwszym etapem było zainstalowanie sterowników do termometru komendą: sudo pip3 install w1thermsensor Następnie zainstalowałem Circut Pythona oraz biblioteki do ledów. pip3 install adafruit-blinka sudo pip3 install rpi_ws281x adafruit-circuitpython-neopixel Po sprawdzeniu czy wszystko działa prawidłowo zabrałem się za pisanie kodu Tworzenie Oprogramowania Pierwszym krokiem w tym kierunku było stworzenie programu który odpalał odpowiednie piksele jeśli temperatura była wyższa niż zakładana, w przeciwnym wypadku poszczególne ledy były wyłączane import board import neopixel import time from w1thermsensor import W1ThermSensor pixels = neopixel.NeoPixel(board.D18, 12) temp = W1ThermSensor() while times != 40: dtemp = temp.get_temperature() print(dtemp) time.sleep(4) times += 1 if dtemp >=18: pixels[2] = (0,0,10) else: pixels[2] = (0,0,0) if dtemp >= 19: pixels[3] = (2,2,8) else: pixels[3] = (0,0,0) if dtemp >= 20: pixels[4] = (4,4,6) else: pixels[4] = (0,0,0) if dtemp >= 21: pixels[5] = (6,6,4) else: pixels[5] = (0,0,0) if dtemp >= 22: pixels[6] = (8,8,2) else: pixels[6] = (0,0,0) if dtemp >= 23: pixels[7] = (10,10,0) else: pixels[7] = (0,0,0) if dtemp >= 24: pixels[8] = (10,8,0) else: pixels[8] = (0,0,0) if dtemp >= 25: pixels[9] = (10,6,0) else: pixels[9] = (0,0,0) if dtemp >= 26: pixels[10] = (10,4,0) else: pixels[10] = (0,0,0) if dtemp >= 27: pixels[11] = (10,2,0) else: pixels[11] = (0,0,0) if dtemp >= 28: pixels[0] = (10,0,0) else: pixels[0] = (0,0,0) time.sleep(5) print("end") pixels.fill((0, 0, 0)) Na początku zaimportowałem potrzebne moduły następnie zdefiniowałem liczbę pikseli oraz port GPIO Kolejnym krokiem była pętla while która wykonywała 40 pomiarów co 4 sekundy Na końcu "zgasiłem" wszystkie piksele, gdybym tego nie zrobił nadal pozostawały by włączone Następnie zabrałem się za postawienie serwera na mojej malince sudo apt install apache2 -y Oraz zainstalowałem potrzebne w tym wypadku PHP sudo apt install php -y Największym problemem było dla mnie pokazywanie temperatury w przeglądarce a zrobiłem to w bardzo prosty sposób: Dodałem do kodu w Pythonie fragment który zapisuje w pliku obecną temperaturę plik = open("dane.txt", "w") dtemp=str(dtemp) plik.write(dtemp) plik.close Ważne było aby zamienić typ zmiennej przechowującej dane o temperaturze na Str. Ten element kodu musi znajdować się na samym końcu pętli.Następnie stworzyłem stronę, gdzie na końcu dodałem polecenie odczytania tego pliku. W innym wypadku program wyrzuci błąd. Wybrałem "W" jako tryb otwierania pliku gdyż wraz z otwarciem kasuje on całą zawartość pliku co jest konieczne w tej sytuacji (dodatkowo jeśli plik nie istnieje skrypt utworzy go) Ostatnim już krokiem jest stworzenie witryny www. Ja postawiłem na pełną prostotę żeby nie obciążać za bardzo malinki, oprócz tego skraca to proces wczytywania strony na samym dole znajduje się fragment PHP który wczytuje zawartość pliku txt który zoistał wcześniej utworzony. Jeśli plik znajduje się w innym folderze trzeba podać dokładny adres tego pliku. Dodałem tam jeszcze funkcją automatycznego odświeżania strony co pięć sekund, dzięki czemu nie musi tego robić użytkownik. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Termometr</title> <style> #all {margin-left: auto; padding:0px; margin-right: auto;} #gora { margin:0px; text-align: center; color: black; padding: 10px; } </style> </head> <body bgcolor= "white"> <div id="all"> <div id="gora"> <h1>Termo Malinka</h1> </div> </div> <font size='24'> <?php echo "Temperatura: "; require_once('dane.txt'); echo " ℃"; header('refresh:5;'); ?> </font> </body> </html> Wszystko wstawiłem do folderu /var/www/html (zapisanie tam danych wymaga uprawnień roota)i uruchomiłem skrypt pythona. Ważne jest to żeby komputer na którym chcemy sprawdzić temperaturę był podłączony do tej samej sieci lokalnej co malinka. Istnieje też możliwość udostępnienia to każdemu użytkownikowi internetu ale nie widziałem takiej potrzeby. Wszystko włożyłem do obudowy z drewna Efekt możecie zobaczyć poniżej ⬇
  7. Kryptowaluty zdobyły już dość dużą popularność, dlatego pomyślałem, że ciekawym projektem byłoby zrobienie z pomocą maliny wyświetlacza aktualnego kursu kryptowalut. W paru krokach postaram się przedstawić jak wykonać taki projekt. Do budowy wykorzystałem: Raspberry pi 3B+ Wyświetlacz LCD 4x20 znaków niebieski + konwerter I2C Konwerter poziomów logicznych dwukierunkowy Płytka stykowa Przewody żeńsko-męskie Etapy wykonania projektu: 1. Podłączenie wyświetlacza 2. Uruchomienie I2C Użyłem Raspbiana Jessie Lite jako systemu do naszej maliny, pierwszym krokiem będzie uruchomienie I2C: - wpisujemy w konsolę sudo raspi-config - następnie wybieramy opcję numer 5 - później przechodzimy do zakładki I2C - i wybieramy yes - wychodzimy z configu maliny i wykonujemy restart wpisując: sudo reboot - jeśli wszystko wykonaliśmy poprawnie powinniśmy ujrzeć to po wpisaniu komendy (przedstawiony adres może się różnić od waszego) i2cdetect -y 1 3. Pobranie biblioteki do obsługi ekranu - po uruchomieniu I2C w Raspberry należy pobrać bibliotekę komendą: git clone https://gist.github.com/6ad9020b3c84bc65b53119b21a4bc37d.git i2c_lcd - po pobraniu wchodzimy do folderu z tą biblioteką: cd i2c_lcd - teraz należy ustawić adres I2C w pliku i2c_lcd.py: sudo nano i2c_lcd.py - w tym miejscu wpisujemy adres, który ujrzeliśmy wcześniej przy używaniu komendy i2cdetect: - nie wychodząc z edytora zmieniamy wyświetlaną szerokość na 20: - wychodzimy i zapisujemy plik naciskając kolejno CTRL+X Y Enter - uruchamiamy test naszego wyświetlacza: python i2c_lcd.py - jeśli wykonaliśmy wszystko poprawie to powinniśmy ujrzeć coś takiego: 4. Program do odczytu i wyświetlania kryptowalut - tworzymy plik crypto.py komendą: sudo nano crypto.py - i wklejamy kod: import i2c_lcd import requests import time space = 6 i2c_lcd.lcd_init() i2c_lcd.lcd_string("-Kursy kryptowalut-",i2c_lcd.LCD_LINE_1) i2c_lcd.lcd_string("---Aktualny kurs---",i2c_lcd.LCD_LINE_2) r = 0 while True: #btc print('btc') r = requests.get('https://api.cryptonator.com/api/ticker/btc-usd') crypto = r.json()['ticker']['price'] crypto = float(crypto) crypto = round(crypto, 2) crypto = str(crypto) i2c_lcd.lcd_string("------Bitcoina------",i2c_lcd.LCD_LINE_3) i2c_lcd.lcd_string(" $"+crypto,i2c_lcd.LCD_LINE_4) time.sleep(space) #eth print('eth') r = requests.get('https://api.cryptonator.com/api/ticker/eth-usd') crypto = r.json()['ticker']['price'] crypto = float(crypto) crypto = round(crypto, 2) crypto = str(crypto) i2c_lcd.lcd_string(" $"+crypto,i2c_lcd.LCD_LINE_4) i2c_lcd.lcd_string("------Ethereum------",i2c_lcd.LCD_LINE_3) time.sleep(space) #bch print('bch') r = requests.get('https://api.cryptonator.com/api/ticker/bch-usd') crypto = r.json()['ticker']['price'] crypto = float(crypto) crypto = round(crypto, 2) crypto = str(crypto) i2c_lcd.lcd_string(" $"+crypto,i2c_lcd.LCD_LINE_4) i2c_lcd.lcd_string("----BitcoinCasha----",i2c_lcd.LCD_LINE_3) time.sleep(space) #ltc print('ltc') r = requests.get('https://api.cryptonator.com/api/ticker/ltc-usd') crypto = r.json()['ticker']['price'] crypto = float(crypto) crypto = round(crypto, 2) crypto = str(crypto) i2c_lcd.lcd_string("------Litecoina------",i2c_lcd.LCD_LINE_3) i2c_lcd.lcd_string(" $"+crypto,i2c_lcd.LCD_LINE_4) time.sleep(space) #doge print("doge") r = requests.get('https://api.cryptonator.com/api/ticker/doge-usd') crypto = r.json()['ticker']['price'] crypto = float(crypto) crypto = round(crypto, 6) crypto = str(crypto) i2c_lcd.lcd_string(" $"+crypto,i2c_lcd.LCD_LINE_4) i2c_lcd.lcd_string("------Dogecoina-----",i2c_lcd.LCD_LINE_3) time.sleep(space) - po zapisaniu uruchamiamy program: sudo python crypto.py - jeśli wszystko wykonaliśmy poprawnie powinniśmy ujrzeć coś takiego: Gotowe możemy monitorować kurs kryptowalut
  8. Chciałbym przedstawić wam mój pierwszy projekt na Raspberry Pi – pojazd gąsiennicowy z kamerą i czujnikiem odległości sterowany przez przeglądarkę przy użyciu Bottle. Do tej pory używałem tylko Arduino i postanowiłem spróbować czegoś nowego. Przewagą pojazdu gąsienicowego nad kołowym jest możliwość poruszania się w trudnych warunkach. Podwozie jest amortyzowane co sprawia, że lepiej radzi sobie z nierównościami terenu. Robot bez problemu jeździ w ogrodzie i po śniegu. Podstawowe elementy wykorzystane do budowy robota: podwozie gąsiennicowe Raspberry Pi Zero W (ze względu na wbudowane wifi) sterownik silników L298N servo hat Sparkfun kamera do Raspberry Pi (z adapterem do Pi zero) przetwornica napięcia step down uchwyt do kamery + 2 serwomechanizmy czujnik ultradźwiękowy HC-SR 04 konwenter poziomów logicznych akumulator 7.4 V Budowa: Raspberry, sterownik silników, przetwornica i uchwyt kamery przykręcone są do postawy. Akumulator znajduje się pomiędzy podstawą a podwoziem. Raspberry i servo hat zasilane są kablem USB z przetwornicy podłączonym do hat’a. Sterownik silników i przetwornica podłączone są do akumulatora przez włącznik zasilania. Kamera i czujnik odległości są przymocowane do uchwytu z serwomechanizmami dzięki czemu można sterować ich położeniem. Czujnik odległości wysyła sygnał 5V, więc musi być podłączony do Raspberry przez konwenter stanów logicznych. Oprogramowanie: Do sterowania pojazdem przez przeglądarkę wykorzystałem projekt Bottle przy użyciu portu 8080. Obraz transmitowany jest przy użyciu pakietu Motion. Po wpisaniu w przeglądarkę: http://[ip_Raspberry]:8080/robocik/ pokazuje się panel do sterowania. Sterujemy robotem przy użyciu myszki wciskając odpowiednie przyciski. Możemy sterować kierunkiem jazdy, prędkością, ustawieniem uchwytu, a także włączyć i wyłączyć kamerę i Raspberry oraz zmierzyć odległość od przeszkody. Oprogramowanie składa się z dwóch części – programu w Pythonie i szablonu strony internetowej w html z rozszerzeniem .tpl. W szablonie możemy ustawić wygląd strony i funkcje przycisków, a program w Pythonie odpowiada za sterowanie GPIO. Pojazd_gasiennicowy_Raspberry_Bottle.zip
  9. Na wstępie od razu uwaga: o ile prezentowane rozwiązania mechaniczne (tzn. sposoby mocowania itd.) dotyczą Anet A8, o tyle elektronika i programy są uniwersalne - wymogiem jest tylko Raspberry Pi i OctoPrint. Zaczęło się od tego, że muszę w końcu skończyć parę robotopodobnych konstrukcji, a z racji niewielkich możliwości warsztatowych (nawet imadła nie mam gdzie przykręcić) postanowiłem kupić drukarkę 3d aby parę brakujących elementów dodrukować. Po przeanalizowaniu finansów (i przekonaniu mojej kochanej że bez drukarki życie mi niemiłe) na szafce stanęła śliczna chińska Anet A8. Krótko potem doszedłem do wniosku, że latanie na drugi koniec mieszkania z kartą aby coś wydrukować nie jest tym, co tygrysy lubią najbardziej. Oczywiście - istnieje kilka różnych opcji, ale postanowiłem wypróbować OctoPrinta (choćby dlatego, że mam starego lapka z połamanym zawiasem który idealnie nadawał się do prób - czyli w sumie mogłem próbować bezkosztowo). Po oswojeniu się z OctoPrintem postanowiłem dokupić jakiś jednopłytkowy komputerek, bo laptop zajmuje jednak trochę zbyt dużo miejsca. Niestety - wyjęty z szuflady NanoPi po uruchomieniu OctoPrinta (jeszcze przed podłączeniem drukarki) uprzejmie się usmażył (nie będę się nad tym rozwodził bo to nie ma nic wspólnego z tematem) - postanowiłem więc kupić Raspberry Pi. Po paru dniach przyszedł Raspberry. Postanowiłem nie instalować OctoPi a normalnego Raspbiana i doinstalować do niego OctoPrinta. Nie wiem czy to dobry wybór - ale wolę mieć normalny system, bo przecież raspberry może słuzyć nie tylko do drukowania! Przez kilka dni Raspberry leżał sobie na szafce w jakiejś prowizorycznej obudowie, w końcu miałem tego dość. Co prawda typowym miejscem do zamontowania RPi na Anetce jest lewa część ramy zaraz nad płytą główną - ale wydrukowałem sobie osłonę kabli osi Z, a pomysł mocowania jej do obudowy RPi uznałem za bezsensowny (niewygodny dostęp do komputerka). Natomiast idealnym miejscem zamontowania wydał mi się wyświetlacz - z tyłu za nim jest mnóstwo miejsca, nawet cztery śruby M3 mamy do dyspozycji. Na Thingiversie znalazłem gotowca (mocowanie), na szczęście najpierw przymierzyłem... i okazało sę, że albo gniazda USB będą z niewłaściwej strony (po prawej), albo wtyczka zasilania z kablem będzie wystawać pod ramą. Postanowiłem więc zrobić własny projekt, gdzie RPi byłby przesunięty maksymalnie do góry. Projekt wyszedł całkiem nieźle (pomijając jeden błąd, naprawiony zresztą w załączonych plikach - mianowicie zapomniałem zostawić otwór na kartę pamięci). Oto on: Montaż jest bardzo prosty - Należy po prostu przykręcić RPi do wydrukowanego mocowania za pomocą czterech śrubek M2 ze stożkowym łbem: Następnie należy odkręcić cztery nakrętki mocujące tylną osłonę wyświetlacza, odłączyć taśmę i na wystające śruby założyć mocowanie: Warto również zastąpić nakrętki trzymające płytę wyświetlacza po prostu podkładkami 2mm (wydrukowałem sobie takie przy okazji) - przybajmniej można będzie całość skręcić zwykłym śrubokrętem bez używania jakichś wymyślnych kluczy. Tak zamocowany RPi został podłączony do zasilacza i działa non stop, niezależnie od drukarki. Dość szybko okazało się, że OctoPrint ma wadę: nie potrafi automatycznie podłączyć się do drukarki po jej włączeniu czy resecie. Niby drobiazg - ale niewygodny. Postanowiłem zrobić jakiś automat, który będzie to robił za mnie. Jak to powinno działać? Program powinien wykryć moment włączenia czy resetu drukarki, i wywołać funkcję "connect" z API OctoPrinta. Tyle założeń, ale jak wykryć włączenie? Po włączeniu pojawi się przecież nowe urządzenie USB, a wykrycie takowego jest już proste; wystarczy odpowiednio oprogramować regułki w udev. Jest tylko jeden problem - co prawda demon udevd potrafi wywołać jakiś tam program, ale nusi być on wykonany jak najszybciej, przy czym oczekiwanie na wykonanie takiego programu blokuje całe działanie demona. Czyli nawet gdyby udało mi się jakoś skrócić czas działania programu - i tak zadziałałby zanim urządzenie pojawi się w systemie Teoretycznie można by było zrobić fork, w potomnym procesie poczekać parę sekund i dopiero wywołać funkcję OctoPrinta, ale wydało mi się to jakieś takie mało profesjonalne... Postanowiłem więc zrobić inaczej. Główny program uruchamiany jest przez systemd przy starcie systemu. Przez cały czas nie robi nic, po prostu czeka na sygnał. Po jego otrzymaniu odczekuje chwilę i wywołuje octoprintowy "connect". Nic nie sprawdza - to wywołanie jest o tyle bezpieczne, że jeśli drukarka już jest do octoprinta podłączona po prostu nic się nie stanie, a próba podłączenia wyłączonej drukarki skończy się po prostu błędem i tyle. Program wgrałem jako /usr/local/bin/watchprinter i nadałem mu prawa do wykonania. Cały kod programu jest tak krótki, że postanowiłem zamieścić go tutaj. #!/usr/bin/env python #coding: utf-8 import requests, json, re, signal, time, yaml class octonector(object): api = None def __init__(self): if not self.__class__.api: conf = yaml.safe_load(open('/home/pi/.octoprint/config.yaml')) self.__class__.api = conf['api']['key'] self._enabled = False def enable(self): self._enabled = True def enabled(self): rc = self._enabled self._enabled = False return rc def doPostRequest(self,cmd,data): path='http://127.0.0.1:5000/api/%s' % cmd rc=requests.post(path, headers={ 'Host': 'localhost', 'Content-Type': 'application/json', 'X-Api-Key' : self.__class__.api}, json=data) return rc.status_code in (204,200) watcher=octonector() def ena(*args): global watcher watcher.enable() signal.signal(signal.SIGUSR1, ena) while True: signal.pause() if watcher.enabled(): time.sleep(5); try: watcher.doPostRequest('connection',{'command':'connect'}) except: pass Jak widać użyłem tu dwóch bibliotek, których w systemie nie ma - requests i yaml. Co prawda mógłbym skorzystać z wirtualnego śwodowiska OctoPrinta (w którym te biblioteki są zainstalowane) - postanowiłem jednak doinstalować je do systemu (szczególnie, że zarówno python-requests jak i python-yaml są w repozytorium i instaluje się je zwykłym aptem). Teraz mogłem już przetestować działanie. Po uruchomieniu i wysłaniu do programu sygnału USR1 drukarka została podłączona do OctoPrinta. Mogłem więc zająć się zautomatyzowaniem wszystkich czynności. Na początek zgrałem swój program z systemd. W tym celu w /etc/systemd/system/ umieściłem plik watchprinter.service z zawartością: [Unit] Description=Autoconnect for octoprint [Service] Type=simple ExecStart=/usr/local/bin/watchprinter Restart=always RestartSec=15 [Install] WantedBy=multi-user.target Po wydaniu polecenia: sudo systemctl start watchprinter mogłem już nie bawić się w szukanie pid-u programu - polecenie sudo systemctl kill -s USR1 watchprinter załatwiało to za mnie. Tak więc nakazałem poprzez "sudo systemctl enable watchprinter" uruchamianie mojego programu przy starcie systemu - pozostało tylko automatyczne wysłanie sygnału po włączeniu drukarki. W tym celu musiałem znaleźć identyfikatory USB podłączonej drukarki. Przy włączonej drukarce polecenie "sudo lsusb" dało następujący wynik: Bus 001 Device 012: ID 1a86:7523 QinHeng Electronics HL-340 USB-Serial adapter Bus 001 Device 004: ID 0424:7800 Standard Microsystems Corp. Bus 001 Device 003: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub Bus 001 Device 002: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Stwierdziłem, że pierwsza na liście to moja Anetka. Tak więc pozostało mi jedynie dopisać regułki udeva. Do pliku /etc/udev/rules.d/55-printer.rules wpisałem zawartość: ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idProduct}=="7523", ATTRS{idVendor}=="1a86", RUN+="/bin/systemctl kill -s USR1 watchprinter" Jak widać, idVendor i idProduct pobrałem właśnie z informacji podanych przez lsusb. No i po restarcie udeva mogłem już cieszyć się działającym automatem podłączającym drukarkę do OctoPrinta po jej włączeniu. No ale cóż - apetyt rośnie w miarę jedzenia. Zamarzyły mi się dodatkowe klawisze pauzy i restartu przy druku z OctoPrinta oraz jakieś ledy sygnalizujące stan drukarki (a właściwie OctoPrinta). Zacząłem więc od przeglądu szuflady. Razem z Raspberrym kupiłem w Botlandzie wtyk do złącza GPIO (kątowy - żeby się nie pomylić przy wtykaniu) - tak więc połączenie z komputerkiem miałem z głowy. Do tego znalazłem kilka przycisków tact-switch w różnych kolorach i płytkę uniwersalną. Klika diod LED i parę rezystorów - i całą wielce skomplikowaną elektronikę miałem skompletowaną. Z zaprojektowaniem obudowy (a właściwie panela - bo pełna obudowa nie jest przewidziana) już nie było problemów. Kilka linijek w OpenSCADzie - skorzystałem z tego, że w poprzednim projekcie miałem już zwymiarowane przyciski - i gotowe. Otwory w panelu pasują do rastra płytki uniwersalnej, a wygląda tak: Sam panel przykręcony jest czterema śrubami M2 (wkręconymi bezpośrednio w plastik) do bocznych wsporników: Trzeba pamiętać, aby otwory pod ledy poprawić wiertłem 3mm (ja użyłem 3.2mm). Kolej na płytkę. Wyciąłem kawałek o wymiarach 35x32 (gdybym ciął wszystkie krawędzie wzdłuż otworów, byłoby to 35x27.5). Otwory pod śruby nawierciłem po prostu przykładając płytkę do panelu. Po przylutowaniu wszystkich elementów wystarczyło odpowiednio podgiąć nóżki od spodu płytki, aby wszystko bez potrzeby prowadzenia ścieżek połączyć według schematu: A tak wygląda płytka przed przykręceniem do panela: Pozostało już tylko przykręcić płytkę do panela (cztery śruby M2 z łbem stożkowym z nakrętkami) i zamocować całość na ramie Anety. W tym celu usunąłem prawą górną tulejkę dystansową między ramą a płytą wyświetlacza, i w to miejsce wsunąłem prawy wspornik. Po skręceniu i podłączeniu wtyku do GPIO całość prezentuje się tak: Teraz zaczęła się właściwa zabawa - czyli pisanie programu. Od razu założyłem, że obsługa klawiatury i obsługa led to będą dwa oddzielne programy - odpadła zabawa z oczekiwaniem na kilka zdarzeń jednocześnie, czyli z wątkami czy procesami. Zacząłem od sygnalizacji. Zasada działania programu jest prosta: program periodycznie odpytuje OctoPrinta o stan drukowania i zależnie od tego z apala odpowiednią kombinację led. W moim przypadku są to: brak - octoprint wyłączony czerwona - drukarka wyłączona niebieska - drukarka włączona, nic nie robi niebieska + żółta - wydruk niebieska + czerwona - pauza. Dodatkowo przy zmianie stanu OctoPrint sygnalizuje programowi że ma ponownie odpytać o stan. W tym celu skorzystałem z możliwości obsługi zdarzeń przez OctoPrinta - każda zmiana stanu powoduje wysłanie sygnału do programu i natychmiastowe odpytanie. Dodatkowo musiałem doinstalować bibliotekę gpio Pythona: sudo apt install python-rpi.gpio Program w /usr/local/bin/octoindic znów jest bardzo prosty: #!/usr/bin/env python #coding: utf-8 LED_Y = 40 LED_R = 38 LED_B = 36 import RPi.GPIO as GPIO import requests, json, re, signal, time, os GPIO.setmode(GPIO.BOARD) GPIO.setwarnings(False) GPIO.setup([LED_Y, LED_R, LED_B],GPIO.OUT); def led(p): p=p.lower() if p else '' GPIO.output([LED_Y,LED_R, LED_B], ('y' in p, 'r' in p, 'b' in p)) class ledLighter(object): api = None headers={'Host':'localhost'} def __init__(self): if not self.__class__.api: import yaml conf = yaml.safe_load(open('/home/pi/.octoprint/config.yaml')) try: self.__class__.api = conf['api']['key'] self.headers['X-Api-Key']=self.__class__.api except: pass self.getStatus() def getStatus(self): code = self._getStatus() led(code) return code def _getStatus(self): path='http://127.0.0.1:5000/api/printer?exclude=temperature,sd' try: rt=requests.get(path,headers = self.__class__.headers) except: return 'x' try: rt.raise_for_status() except: return 'r' return self.parseStatus(rt.content) def parseStatus(self, content): try: content = json.loads(content)['state']['flags'] except: return 'ry' if content['paused']: return 'rb' if content['printing']: return 'by' if content['ready']: return 'b' return 'ry' lt = ledLighter() def ena(*args): # tu można dodać kawałek kodu reakcji na sygnał pass signal.signal(signal.SIGUSR1, ena) while True: time.sleep(15) lt.getStatus() Jak widać, program sprawdza stan za każdym razem kiedy przyjdzie sygnał USR1 oraz 15 sekund po ostatnim sprawdzeniu. Znów wpis w /etc/systemd/system/octoindic.service jest krótki: [Unit] Description=Led status for octoprint [Service] Type=simple ExecStart=/usr/local/bin/octoindic Restart=always RestartSec=5 User=pi Group=pi [Install] WantedBy=multi-user.target Po wydaniu poleceń: sudo systemctl enable octoindic sudo systemctl start octoindic pozostaje jedynie dokonfigurowanie OctoPrinta. W tym celu do pliku konfiguracji ~/.octoprint/config.yaml dopisujemy: events: enabled: true subscriptions: - event: Startup command: sudo systemctl kill -s USR1 octoindic type: system - event: Shutdown command: sudo systemctl kill -s USR1 octoindic type: system - event: PrinterStateChanged command: sudo systemctl kill -s USR1 octoindic type: system - event: PrintStarted command: sudo systemctl kill -s USR1 octoindic type: system - event: PrintFailed command: sudo systemctl kill -s USR1 octoindic type: system - event: PrintDone command: sudo systemctl kill -s USR1 octoindic type: system - event: PrintCancelled command: sudo systemctl kill -s USR1 octoindic type: system - event: PrintPaused command: sudo systemctl kill -s USR1 octoindic type: system - event: PrintResumed command: sudo systemctl kill -s USR1 octoindic type: system Po restarcie OctoPrinta możemy cieszyć się dziarsko świecącymi diodami Pozostaje kwestia klawiszy - bardzo przydatnych np. przy zmianie koloru filamentu. Znów prosty program w /usr/local/bin/octopauser: #!/usr/bin/env python #coding: utf-8 KEY_PAUSE = 37 KEY_RESUME = 35 import RPi.GPIO as GPIO import requests, json, re, signal, time, os GPIO.setmode(GPIO.BOARD) GPIO.setwarnings(False) GPIO.setup([KEY_RESUME,KEY_PAUSE],GPIO.IN,pull_up_down=GPIO.PUD_UP); class myKeys(object): api = None headers={'Host':'localhost'} postheaders={'Host':'localhost'} def __init__(self): self.times=[0,0] if not self.__class__.api: import yaml conf = yaml.safe_load(open('/home/pi/.octoprint/config.yaml')) try: self.__class__.api = conf['api']['key'] self.__class__.headers['X-Api-Key']=self.__class__.api self.__class__.postheaders['X-Api-Key']=self.__class__.api except: pass def key_press_p(self,*args): if not GPIO.input(KEY_PAUSE): self.times[0] = time.time() elif time.time() - self.times[0] >= 0.2: self.cmdrun("pause") def key_press_r(self,*args): if not GPIO.input(KEY_RESUME): self.times[1] = time.time() elif time.time() - self.times[1] >= 0.2: self.cmdrun("resume") def doPostRequest(self,cmd,data): path='http://127.0.0.1:5000/api/%s' % cmd try: rc=requests.post(path, headers=self.__class__.postheaders, json=data) except: pass def cmdrun(self,cmd): if cmd == "pause": self.doPostRequest("job", {"command" : "pause","action" : "pause" }); elif cmd == "resume": s = self.getStatus() if s == "disconnected": self.doPostRequest("connection",{"command":"connect"}) elif s == "connected": self.doPostRequest("job", {"command" : "pause","action" : "resume" }); def getStatus(self): path='http://127.0.0.1:5000/api/printer?exclude=temperature,sd' try: rt=requests.get(path,headers = self.__class__.headers) except: return "off" try: rt.raise_for_status() except: return "disconnected" return "connected" def arm(self): GPIO.add_event_detect(KEY_PAUSE, GPIO.BOTH, callback = self.key_press_p) GPIO.add_event_detect(KEY_RESUME, GPIO.BOTH, callback = self.key_press_r) ks = myKeys() ks.arm() while True: try: signal.pause() except KeyboardInterrupt: break except: pass I także krótki wpis do /etc/systemd/system/octopauser.service: [Unit] Description=Pause keys for octoprint [Service] Type=simple ExecStart=/usr/local/bin/octopauser Restart=always RestartSec=5 User=pi Group=pi [Install] WantedBy=multi-user.target Po wydaniu poleceń: sudo systemctl enable octopauser sudo systemctl start octopauser możemy już cieszyć się możliwością wygodnego operowania pauzą na poziomie OctoPrinta za pomocą klawiszy. I to wszystko. Oczywiście - całą tę konstrukcję można rozbudować. Przykładowo - czujnik filamentu można dodać równolegle do klawisza pauzy, lub poświęcić jeszcze jeden pin GPIO i lekko przerobić program. Niestety - na razie nie znalazłem czujnika, który zadziała zarówno z miękkim TPU jak i przezroczystym PMMA... Załączam komplet plików (scad, stl oraz kody programów). Octopauser.zip Przyjemnego pauzowania i wznawiania życzy ethanak
  10. 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>
  11. Sprzedam nowy, nieużywany i oryginalnie zapakowany zestaw "GoPiGo Robot Advanced Starter Kit" oraz "GoPiGo3 BalanceBot Extension Kit" za połowę ceny rynkowej! - 420 zł (do negocjacji). Zestawy te zawierają wszystkie potrzebne części i umożliwiają w szybki sposób budowę robotów w oparciu o Raspberry Pi. Jest to idealny prezent dla pasjonata nowych technologii. W skład zestawu wchodzi: - platforma robota (2 silniki z enkoderami, koła, podstawa, przewody) - Raspberry Pi 3 wraz z kartą z dedykowanym oprogramowaniem - zintegrowany sterownik robota dedykowany do do Raspberry pi (sterownik silników, we/wy analogowe i cyfrowe, mikrokontroler ARM) - servo - czujniki - laserowy odległości - IMU - odbiornik i pilot podczerwieni do zdalnego sterowania - zasilacz Dokładny opis można znaleźć po linkami: https://www.dexterindustries.com/shop/gopigo-advanced-starter-kit/ https://www.dexterindustries.com/shop/gopigo3-balancebot-kit/
  12. Tym razem projekt nie zachwyca wyglądem ale jest ciekawszy pod względem oprogramowania. Projekt składa się z wyświetlacza segmentowego do wyświetlania godziny, klawiatury numerycznej 12-klawiszy do wprowadzania danych i wyświetlacza LCD 2x16 do wyświetlania wszelkich informacji. Całość podłączona jest do Raspberry Pi. Wykorzystane części Raspberry Pi 3 model B Wyświetlacz alfanumeryczny 2x16 znaków Klawiatura numeryczna 12-klawiszy Moduł 4x wyświetlacz 7-segmentowy Płytka stykowa, potencjometr obrotowy, przewody połączeniowe Dostępne funkcje Wyświetlacz segmentowy wskazuje aktualną godzinę. Kolejnymi funkcjami są odtwarzanie radia internetowego (np. ESKA, RMF FM) oraz prognoza pogody na 9 dni realizowana za pomocą API OpenWeatherMap. Z poziomu klawiatury można również wyświetlić aktualną temperaturę i użycie CPU, czas działania maliny, zrestartować/zamknąć system, zrestartować wifi oraz ustawić jasność wyświetlacza segmentowego. Podłączenie Wyświetlacz LCD podłączamy następująco: Pin 1 wyświetlacza do GND, pin 2 do 5V, pin 3 do środkowej nóżki potencjometru, pin 4 go GPIO25, pin 5 do GND, pin 6 do GPIO24, pin 11 do GPIO23, pin 12 do GPIO17, pin 13 do GPIO18, pin 14 do GPIO22, pin 15 do GPIO4, pin 16 do GND, lewą nóżkę potencjometru do 5V, prawą do GND. W wyświetlaczu alfanumerycznym podłączenie wygląda następująco: GND do GND, VCC do 5V, CLK do GPIO21, DIO do GPIO20 Klawiaturę podłączamy zaczynając od lewej strony kolejno do pinów: 27, 5, 6, 13, 26, 12, 19 (pierwsze 4 odpowiadają wierszom, kolejne 3 kolumnom na klawiaturze) Oprogramowanie Kompletny kod znajduje się w załączniku, tutaj opiszę tylko niektóre ciekawsze, bardziej przydatne funkcje. Do obsługi wyświetlacza segmentowego używam tej biblioteki. Inicjujemy zegar wskazując użyte piny oraz jasność: self.display = tm1637.TM1637(CLK=21, DIO=20, brightness=1.0) Do obsługi wyświetlacza LCD 2x16 używam biblioteki Adafruit CharLCD. Także tutaj podajemy piny użyte w połączeniu. Tworzę również 8 dodatkowych znaków których będzie można użyć w wyświetlaczu (ą, ć, ę, ł, ń, ó, ż, ź). Znaki te można wykorzystać zamieniając polskie znaki w tekście do wyświetlenia na odpowiednie dodatkowe znaki np ą = \x00, ć = \x01 itd. self.lcd = LCD.Adafruit_CharLCD(25, 24, 23, 17, 18, 22, 16, 2, 4) self.lcd.create_char(0, [0,0,14,1,15,17,15,2]) self.lcd.create_char(1, [2,4,14,16,16,17,14,0]) self.lcd.create_char(2, [0,0,14,17,31,16,14,2]) self.lcd.create_char(3, [12,4,6,12,4,4,14,0]) self.lcd.create_char(4, [2,4,22,25,17,17,17,0]) self.lcd.create_char(5, [2,4,14,17,17,17,14,0]) self.lcd.create_char(6, [2,4,14,16,14,1,30,0]) self.lcd.create_char(7, [4,0,31,2,4,8,31,0]) Aby używać klawiatury korzystam z modułu pad4pi KEYPAD = [ ["1","2","3"], ["4","5","6"], ["7","8","9"], ["*","0","#"] ] ROW_PINS = [27, 5, 6, 13] COL_PINS = [26, 12, 19] factory = rpi_gpio.KeypadFactory() self.keypad = factory.create_keypad(keypad=KEYPAD, row_pins=ROW_PINS, col_pins=COL_PINS) To wystarczy aby móc korzystać z podłączonych elementów. Funkcja gówna wygląda następująco: self.display.StartClock() # Uruchomienie zegara w tle self.readSettings() # Odczyt ustawień jasności z pliku self.keypad.registerKeyPressHandler(self.keyPress) # Obsługa zdarzenia naciśnięcia klawisza na klawiaturze while True: # Miganie dwukropkiem na zegarze co 1s self.display.ShowDoublepoint(True) sleep(1) self.display.ShowDoublepoint(False) sleep(1) Wszelkie funkcje urządzenia realizowane są w metodzie keyPress wykonującej się po wciśnięciu klawisza na klawiaturze. Po wciśnięciu dowolnego klawisza, jeśli podświetlenie wyświetlacza jest wyłączone to zostaje włączone i wyświetlony zostaje ekran główny - pierwsza linia to data, w drugiej wyświetlane są informacje o braku internetu lub aktualnie odtwarzanej stacji radia. Podświetlenie wyświetlacza jest automatycznie wyłączane po 20s bezczynności co realizowane jest kodem na początku metody keyPress: if(self.timerActive == True): # Zatrzymanie odliczania jeślie trwa self.timer.cancel() else: self.timerActive = True self.timer = threading.Timer(20.0, self.clear) # Po zakończeniu odliczania zostanie wykonana metoda która wyczyści i wyłączy wyświetlacz self.timer.start() # Rozpoczęcie odliczania od nowa Z poziomu ekranu głównego można przejść do menu(#) lub opcji(*). W menu dostępne opcje to radio(1) oraz pogoda(2). Stream radia odtwarzany jest za pomocą modułu python-vlc. Odtwarzanie radia z adresu url za pomocą vlc może wyglądać tak: self.Instance = vlc.Instance() self.player = self.Instance.media_player_new() # Utworzenie nowego odtwarzacza self.rmffm = self.Instance.media_new("http://195.150.20.9:8000/rmf_fm") # Dodanie źródła # Następnie w w miejscu w kodzie gdzie chcemy uruchomić radio wystarczy wybrać źródło i rozpocząć odtwarzanie self.player.set_media(self.rmffm) self.player.play() Niestety w niektórych stacjach adres streamu nie jest stały i konkretny adres url działa tylko przez kilka godzin np. radio ESKA. W takich sytuacjach pomóc może napisanie skryptu który automatycznie pobierze aktualny adres url. Ponieważ często słucham radia ESKA napisałem taki skrypt dla tego radia (plik znajduje się w załączniku). W tym przypadku skrypt jest napisany w PHP i umieszczony na darmowym hostingu ale równie dobrze mógłby być napisany w Pythonie jako część tego programu. Aby pobrać aktualny adres streamu za pomocą swojego skryptu używam kodu: self.eska_rzeszow = self.Instance.media_new(urllib2.urlopen("http://programista3.000webhostapp.com/inne/pi/radioapi.php?id=0").read()) Kolejną funkcją dostępna z poziomu menu jest pogoda. Po wejściu w ten tryb wyświetlana jest aktualna pogoda a za pomocą klawiszy 1-9 można wyświetlać prognozę na kolejne dni. Wyświetlana jest temperatura, ciśnienie, wilgotność oraz słowny opis pogody. Naciskając * można wyświetlić szczegóły dla danego dnia. W widoku szczegółowym wyświetlane są temperatura minimalna i maksymalna, ciśnienie, wilgotność, zachmurzenie oraz prędkość wiatru. Prognoza pogody pobierana jest za pomocą darmowego API OpenWeatherMap. Do pobrania prognozy wykorzystuję kod: self.forecast = self.getUrl("http://api.openweathermap.org/data/2.5/forecast/daily?q=rzeszow&mode=json&units=metric&lang=pl&cnt=10&appid=xxx") self.forecast = json.loads(self.forecast) if self.forecast else False Wyświetlanie podstawowych informacji o pogodzie wygląda następująco: description = (self.forecast['list'][int(key)]['weather'][0]['description'].encode('utf-8').capitalize()) self.writelcd(str(int(self.forecast['list'][int(key)]['temp']['day']))+chr(223)+"C "+str(int(self.forecast['list'][int(key)]['pressure']))+"hPa "+str(self.forecast['list'][int(key)]['humidity'])+"%\n"+description) W opcjach urządzenia znajdują się natomiast funkcje takie jak restart wifi, restart systemu i wyłączenie systemu które są realizowane za pomocą funkcji call z biblioteki subprocess, ustawienia jasności wyświetlacza segmentowego, czas działania, temperatura CPU oraz użycie CPU. Czas działania pobierany jest za pomocą biblioteki psutil: def uptime(self): time_sec = time.time()-psutil.boot_time() if(time_sec > 172800): return str(round(time_sec/86400))+" dni" elif(time_sec > 3600): return str(round(time_sec/3600))+" h" elif(time_sec > 60): return str(round(time_sec/60))+" min" else: return str(time_sec)+" s" Użycie CPU to również biblioteka psutil: self.writelcd(" Użycie CPU\n "+str(psutil.cpu_percent())+"%") Natomiast temperaturę CPU można odczytać z pliku: file = open("/sys/class/thermal/thermal_zone0/temp") temp = file.read() file.close() self.writelcd("Temperatura CPU\n "+temp[:2]+chr(223)+"C") Jestem otwarty na pomysły jak mógłby wyglądać projekt i jakie funkcje można do niego dodać. Kompletny kod znajduje się w załączniku. rpiha.rar
  13. Cześć, mam problem z moją maliną mianowicie po kilku dniach korzystania z niej jako mała jednostka centralna pojawił się problem! Podczas włączania, zapala się dioda czerwona i mruga dioda zielona tak jak niby powinna ale nie wyświetla się obraz z HDMI. Sytuacja pojawiła się po zamontowaniu maliny w obudowie i naklejeniu radiatorów (obie rzeczy oczywiście montowałem z wyłączonym sprzętem, dodam że nie korzystałem z portu GPIO ani innych wyprowadzeń prócz myszki i klawiatury. Wcześniej wszystko było ok, dodam że próbowałem z inną kartą SD i na nowo wgranym systemem ale to nic nie zmienia. Proszę o pomoc bo nie wiem co zrobić!
  14. Współczesne drukarki mają w standardzie komunikację poprzez sieć Wi-Fi, stanowi to dużą korzyść dla użytkownika, ilość przewodów w pomieszczeniu ograniczona jest do minimum, ale również swoboda ulokowania sprzętu drukującego niezależnie od położenia routera czy komputera wnosi dozę komfortu. Coraz częściej spotykane są nawet możliwości instalacji na smartfony wtyczek drukarek danych producentów i zdolność mobilnego drukowania. Co w przypadku kiedy nasza drukarka nie narodziła się w drugiej dekadzie XXI wieku i nie posiada takich dogodności komunikacyjnych? Tym właśnie chciałbym się zająć w moim projekcie. Przy pomocy Raspberry Pi udostępnić drukarkę w usłudze Google Cloud Print zyskując możliwość wydruku z dowolnego miejsca na świecie, a stosując przekaźnik włączyć ją automatycznie kiedy ma zadanie do wydrukowania, wyłączyć zaś po określonym czasie, po wykonaniu tego zadania. Czym jest usługa Google Print? „Google Cloud Print (GCP) to usługa umożliwiająca drukowanie z dowolnego urządzenia połączonego z siecią. Przekazuje ona zadania drukowania z komputera, smartfona lub tabletu do drukarki połączonej z internetem. Ułatwia też użytkownikom wykrywanie drukarek i drukowanie z własnych urządzeń bez konieczności przeprowadzania skomplikowanej konfiguracji i instalowania sterowników.” Czytamy na stronie producenta. Jak to działa? Osoba drukująca loguje się na stronie Google Cloud Print / wybiera Google Cloud Print w usługach wydruku (system Android). Wybranie elementu do wydruku. Ustawienie parametrów wydruku (rozmiar, jakość itp.). Wybór drukarki z listy dostępnych oraz przesyłanie zadania. Drukarka otrzymuje zadanie i dodaje je do bufora wydruku. Skrypt ustawia stan wysoki na pinie przekaźnika (drukarka zostaje uruchomiona). Następuje wydruk. Skrypt wyłącza dopływ prądu do drukarki po określonym czasie. Potrzebne elementy Starałem się ograniczyć ilość użytych elementów do minimum, zależało mi na stworzeniu niedużego dodatku do drukarki, który ma poszerzyć jej funkcjonalność przy niewielkich rozmiarach. Wykorzystane elementy: Raspberry Pi Zero Karta Wi-Fi USB Ładowarka po starym telefonie 5V/0,7A Karta pamięci 8GB Hub USB 4 posty Przejściówka USB <-> micro USB Przekaźnik SSR 5V, może być dowolny inny przekaźnik załączany stanem wysokim Puszka natynkowa 60 x 30 x 60 mm Puszka natynkowa 290 x 25 x 80 mm Śruba 3mm wraz z nakrętką Przewody Wtyczka elektryczna Gniazdo elektryczne W projekcie użyto Raspberry Pi Zero bez wbudowanej komunikacji Wi-Fi, w przypadku użycia wersji Zero W tego urządzenia przestaje być konieczne posiadanie dodatkowej karty sieciowej oraz huba USB. Do Raspberry Pi podłączone jest zasilanie, hub USB poprzez przejściówkę USB <-> micro USB, przewody do przekaźnika SSR (sterowanie poprzez GPIO4), całość zamknięta jest w puszcze elektrycznej z wyciętą uprzednio listwą zaciskową. Do huba wpięta jest karta sieciowa i przewód od drukarki. Przekaźnik ukryty w mniejszej puszcze natynkowej (przykręconej śrubą do puszki zawierającej Raspberry Pi) posiada wpięte kable sieci elektrycznej zakończone wtyczką elektryczną oraz gniazdkiem elektrycznym (do niego podłączony zostaje kabel zasilający drukarkę). Całość przymocowana została do obudowy drukarki taśmą dwustronną. Instrukcja instalacji Przygotowanie Raspberry Pi. Wgrywam czysty system „Raspbian Stretch Lite” pobrany z oficjalnej strony. Użyłem wersji z 2018-11-13. Wprowadzam ustawienia pozwalające na komunikację UART oraz SSH z maliną (na partycji „boot” dodanie w pliku „config.txt” linijki „enable_uart=1”; utworzenie na partycji pliku „ssh” bez rozszerzeń). Podłączam Raspberry do komputera przy pomocy programatora. GND <-> GND TX <-> RX RX <-> TX Podłączam zasilanie, hub USB oraz kartę sieciową. Instalacja sterowników karty sieciowej. Jako, iż system Raspbian nie posiadał wbudowanych sterowników mojej karty musiałem je ręcznie doinstalować. Loguję się do systemu. Wykonujemy polecenie lsusb w wyniku czego otrzymujemy listę urządzeń podłączonych do portu USB Raspberry Pi. Jako, że malina nie jest podłączona do Internetu pakiet ze sterownikiem muszę pobrać sam na komputerze. Przechodzę na stronę pakietów Debian i wyszukuję paczkę, w moim przypadku ZD1211. Wybieram stabilny pakiet i pobieram klikając przycisk „all” poniżej strony. Wyłączam Rasberry Pi, wyciągam kartę pamięci i podłączam ją do komputera. Na dostępną partycję „boot” przenoszą pobraną paczkę z rozszerzeniem ".deb", dla dalszej wygody zmieniam nazwę pliku „firmware-zd1211_1.5-4_all.deb” na „wifi.deb”. Instalacja pakietów debian odbywa się przy pomocy polecenia „ dpkg -i” oraz podania ścieżki do pliku. Dane przesłane na partycję „Boot” są dostępne w Raspberry Pi w katalogu „boot”. Wykonuję polecenie sudo dpkg -i /boot/wifi.deb Po zainstalowaniu pakiet usuwam poleceniem sudo rm /boot/wifi.deb oraz wykonuje restart urządzenia sudo reboot Konfiguracja dostępu do sieci Po uruchomieniu przechodzę do konfiguracji połączenia WiFi polecenie sudo raspi-config następnie wybór „Network Options” > „Wi-fi”. Wybieram z listy kraj na terenie którego będziemy korzystać z sieci > wprowadzam dokładną nazwę swojej sieci domowej „SSID” > wprowadzamy hasło. Instalacja programu CUPS Wydaję polecenie sudo apt-get install cups nastąpi pobranie i zainstalowanie programu CUPS. Dodaję użytkownika „pi” do grupy administratorów, aby mógł zarządzać funkcjami CUPS sudo usermod -aG lpadmin pi Daję zgodę na dostęp do usługi wszystkim w sieci lokalnej sudo cupsctl --remote-any Restartuję usługę CUPS sudo /etc/init.d/cups restart Jeśli wszystko wykonało się poprawnie po wpisaniu w przeglądarkę [adres IP Raspberry Pi]:631 (przykładowo 192.168.0.2:631; adres IP można sprawdzić poleceniem „ifconfig”, jest on widoczny w sekcji „wlan0”) powinien być widoczny panel administracyjny aplikacji zajmującej się obsługą drukarek podpiętych do maliny (CUPS). Instalacja i konfiguracja drukarki w programie CUPS. Podłączam uruchomioną drukarkę do portu USB Raspberry Pi, następnie uruchamiam malinę. Otwieram w przeglądarce stronę CUPS, przechodzę do zakładki „Administration” a następnie wybieram „Add Printer”. W moim przypadku w tym momencie wyskoczył problem z zabezpieczeniem strony, nie udało mi się go rozwiązać dla CUPS, więc po skonfigurowaniu drukarki wystarczy tylko, dla bezpieczeństwa, zmienić hasło użytkownika uprawnionego do konfiguracji (w moim przypadku „pi”). Aby obejść problem (dla przeglądarki Mozilla FireFox) wybieram „Zaawansowane” > „Dodaj wyjątek” > „Potwierdź wyjątek bezpieczeństwa”. Pojawia się okno logowania, podaje dane takie jak dla użytkownika dodanego podczas konfiguracji usługi CUPS. Z listę możliwy do podłączenia drukarek. Wybieram „Local Printers” i przechodzę dalej. Pojawia się okno edycji opisu wybranej drukarki. Można zmienić jej oznaczenie, opis, czy określić lokalizację. Uzupełniam dane względem potrzeb i przechodzę dalej. Wyświetlone zostaje okno wyboru sterowników. Jako, iż na rynku dostępnych jest bardzo wiele modeli, a nie wszystkie są domyślnie wbudowane w serwis, może się zdarzyć, że na liście po prostu zabraknie jakiegoś modelu, jak w moim przypadku. W takim przypadku mogę spróbować wybrać z listy drukarkę podobną. Wyszukać w Internecie lub na płycie dołączonej do drukarki pliku PPD dla swojego modelu lub zainstalować w Raspberry Pi sterowniki dla większości modeli danej marki. Wybrałem ostatnią możliwość. Loguję się do terminala maliny i wykonuję polecenie zależne od producenta drukarki. splix – Samsung gutenprint - Canon, Epson, Lexmark, Sony, Olympus hplip – HP Moja drukarka to Samsung, więc wykonuję sudo apt-get install -y printer-driver-splix Po pomyślnym zakończeniu instalacji wracam do strony konfiguracyjnej, przechodzę do „Administration” > „Add Printer” > wybieramy drukarkę > uzupełniamy ewentualny opis i przechodzę dalej. Sterownik mojej drukarki znajdował się w zainstalowanym pakiecie i został wyświetlony na liście. Zaznaczam go i dodaję drukarkę. Ostatnim etapem są ustawienia preferencji drukowania, rozmiar papieru, wybór zasobnika papieru, rozdzielczość wydruku itp. Po poprawnym dodaniu drukarka powinna znaleźć w zakładce „Printers” Po wybraniu drukarki z listy istnieje możliwość między innymi jej ponownej edycji, czy sprawdzenia działania drukując stronę testową. Instalacja usługi Google Cloud Print Instaluję niezbędne pakiety sudo apt-get install -y build-essential python-dev libcups2-dev libavahi-client-dev sudo apt-get install -y python-setuptools sudo easy_install pip sudo pip install pycups sudo pip install cloudprint sudo pip install cloudprint[daemon] sudo apt-get update Wykonuję polecenie. sudo cloudprint Zostanie wyświetlony adres pod który należy wejść w przeglądarce. Loguję się do swojego konta Google i wybieram „zarejestruj”. Jeżeli wszystko zostało wykonane poprawnie w terminalu powinien widnieć taki komunikat Przerywam działanie usługi skrótem Ctrl+C. Skrypt sterujący przekaźnikiem. Skrypt najwygodniej zapisać w lokalizacji uwzględnionej w zmiennej PATH, ja wybrałem katalog „bin”. Otwieram edytor w lokalizacji „bin” oraz nadaję nazwę plikowi ze skryptem ( „printerPower”). sudo pico /bin/printerPower Umieszczam w nim skrypt. #!/bin/sh $(sudo echo 4 > /sys/class/gpio/export) $(sudo echo out > /sys/class/gpio/gpio4/direction) $(sudo echo 0 > /sys/class/gpio/gpio4/value) powerOffTime=0 waitTime=5 status="off" echo "status: $status" while [ true ] do if [ `lpstat -o | wc -l` != 0 ] then echo on status="on" $(sudo echo 1 > /sys/class/gpio/gpio4/value) powerOffTime=$(( $(date +%s)+$waitTime*60 )) fi if [ "$status" = "on" -a $(date +%s) -ge $powerOffTime ] then echo off $(sudo echo 0 > /sys/class/gpio/gpio4/value) status="off"; fi sleep 20 done Na początku skryptu wykonywane są czynności konieczne do obsługi GPIO do którego podłączony zostanie przekaźnik (w tym przypadku pin 4). Deklaruje go, ustawiam na wyjściowy oraz przypisuje stan niski. Następnie deklarujemy zmienną czasu która przechowuje godzinę wyłączenia drukarki („powerOffTime”). Zmienna „waitTime” przechowuje czas w minutach po jakim nastąpi wyłączenie drukarki po ostatnim drukowaniu. W nieskończonej pętli while występują dwie pętle warunkowe. Pierwsza pętla sprawdza czy kolejka drukowania jest różna od 0. Jeżeli tak następuje zmiana stanu pinu 4 na wysoki oraz przypisanie do zmiennej „powerOffTime” obecnego czasu w sekundach powiększonego o zmienną „waitTime” zamienioną na sekundy. Druga pętla warunkowa sprawdza obecny status przekaźnika i porównuje czas obecny z czasem określonym na. Jeżeli status jest równy „on”, a obecna godzina większa, bądź równa godzinie wyłączenia warunek zostanie spełniony, nastąpi ustawienie stanu niskiego na pinie 4 oraz zmiana statusu na „off”. Na końcu każdego cyklu pętli while następuje uśpienie na okres 20 sekund. Zapisuję skrypt kombinacją Ctrl+o i zatwierdzam przyciskiem enter (wychodzę z edytora kombinacją Ctrl+x, uprzednio zatwierdzając zmiany „y”). Ustawiamy plik skryptu na typ wykonywalny. sudo chmod +x /bin/printerPower Dodaję usługę cloudprint oraz skrypt do autostartu. Otwieram i edytuję plik autostartu. sudo pico /etc/rc.local Dodaję przed linią „exit 0” polecenia wykonania skryptu „printerPower” bezpośrednio po nazwie dodając znak & [ampersand] (umożliwi to uruchomienie skryptu w tle). W następnej linijce dodaję polecenie uruchomienia usługi „cloudprint”. Mogę je wykonać z argumentem -d sudo cloudprint -d usługa uruchomiona zostanie w trybie daemon, umożliwi to swobodne korzystanie z urządzenia podczas pracy desktopowej (nie poprzez terminal), ale zaobserwowałem, że czas jej uruchomienia po restarcie maliny jest dłuższy, a stabilność połączenia z serwerem Google bywa różna. Natomiast uruchomienie usługi bez argumentu -d pozwoli na swobodną pracę z Raspbianem tylko poprzez dostęp terminalowy, za to stabilność połączenia z Google Cloud Print jest większa. sudo printerPower& sudo cloudprint Ctrl+x > zatwierdzamy zmiany „y” > zatwierdzamy enterem. Restartuję urządzenie sudo reboot now
  15. Raspberry Pi jest często używane jako kontroler automatyki domowej. Inteligentne sterowanie roletami i oświetleniem to mały wycinek tego co potrafi malinka z Domoticzem. Na początku warto zacząć od ustawień systemu, podłączenia czujników temperatury (DS18B20) oraz konfiguracji powiadomień. [blog]https://forbot.pl/blog/kurs-raspberry-pi-projekty-domoticz-ds18b20-maile-id27526[/blog]
  16. Chciałbym przedstawić swój kolejny projekt. Jest nim 4-kołowy robot oparty na Raspberry Pi. Robot posiada kamerę i ultradźwiękowy czujnik odległości oraz jest zasilany z powerbanka. Jako wygodną metodę sterowania robotem wybrałem sterowanie przez przeglądarkę www. Raspberry Pi łączy się z wifi i każde urządzenie w tej sieci może sterować robotem. Konstrukcja Podstawowe części z których składa się robot to: Raspberry Pi 3 model B 4-kołowe podwozie robota Kamera ArduCam dla Raspberry Pi Ultradźwiękowy czujnik odległości HC-SR04 Power Bank ADATA 12500mAh sterowniki silników L293D Raspberry Pi oraz powerbank umieszczone są na zrobionych własnoręcznie kartonowych podstawkach przykręconych do podwozia. Kamera również umieszczona jest na kartonowej konstrukcji. Robot zasilany jest z dwóch gniazd USB (jedno dla Raspberry Pi, drugie dla pozostałej elektroniki). Powerbank 12500mAh jest w stanie zapewnić kilka godzin pracy robota na jednym ładowaniu. Kamera oraz czujnik odległości umieszczony jest na przodzie robota. Sterowanie przez przeglądarkę www Sterowanie robotem odbywa się poprzez Node.js. Obraz z kamery streamowany jest za pomocą modułu raspivid-stream z wykorzystaniem WebSocketów na porcie 3000. Pozostała komunikacja odbywa się za pomocą socket.io na porcie 80. Serwer kontroluje klientów w taki sposób, że w danym momencie tylko jeden z nich może sterować robotem (według kolejności połączenia z serwerem). Pozostali klienci mogą tylko oglądać obraz transmitowany z kamery. Do sterowania wykorzystywana jest klawiatura (WSAD lub strzałki). Serwer przesyła do klientów również informację o odległości z czujnika ultradźwiękowego co sekundę. Widok z poziomu operatora: Widok z poziomu widza: Kod jest dość prosty. Po stronie serwera jest to podstawowe wykorzystanie modułu raspivid-stream i odczytu z czujnika odległości za pomocą modułu pigpio oraz trochę bardziej skomplikowane użycie socket.io do komunikacji i rpi-gpio do odpowiedniego sterowania silnikami. Po stronie klienta jest to głównie komunikacja za pomocą socket.io oraz obsługa zdarzeń klawiatury. Pliku projektu znajdują się w załączniku. Prezentacja wideo RaspberryPi_robot.rar
  17. Witam wszystkich. Mam do sprzedania duza kolekcje sensorow/modulow do Raspberry Pi i Arduino. Moduly zakupilem podczas studiow, jednak studiuje sieci komputerowe i nie mialem czasu sie nimi pobawic. Uzylem doslownie paru, dlatego sprzedaje wszystko jako nowe. Ponizej zawartosc paczki od Soundfounder cena rynkowa 330zł: 1x Double Color LED 1x RGBLED 1x Auto-Flash LED 1x Relay module 1x Laser Emitter 1x Button 1x Tilt Switch 1x Vibration Switch 1x IR Receiver 1x Active Buzzer 1x Passive Buzzer 1x Reed switch 1x Photo-interrupt 1x AD/DAConvert-PCF8591 1x RainDrop Sensor 1x Joystick PS2 1x Potentiometer 1x Analog Hall Sensor 1x Hall Switch 1x Analog Temperature Sensor 1x Thermistor 1x Sound Sensor 1x Photoresistor 1x Flame Sensor 1x Gas Sensor 1x Remote Control 1x Touch Switch 1x HC-SR04 1x Temperature Sensor-DS18B20 1x Rotary Encode 1x Humiture Sensor 1x IR Obstacle 1x 1602 I2C 1x Barometer-BMP280 1x MPU6050 1x RTC-DS1302 1x Tracking Sensor 1x Breadboard 1x GPIO Extension Board 1x 40-pin Ribbon Cable for GPIO Board 2x Pin Anti-Reverse Cable 5x Pin Anti-Reverse Cable 5x Pin Anti-Reverse Cable 5x Pin Anti-Reverse Cable 1x pin ribbon cable 20x Jumper wires (Male to Female) 10x Jumper wires (Male to Male) 1x User Manual ARDUINO UNO REV3 [A000066] cena: 130 ARDUINO WIFI Shield cena: 200 Ethernet Shield cena: 40 Reszta zostala zakupiona ze strony botland.com.pl Podstawy elektroniki 2 - zestaw elementów + bezpłatny kurs ON-LINE cena: 69,90 Arduino poziom 2 - zestaw elementów + bezpłatny kurs ON-LINE cena: 139,90 Zestaw do budowy robota + bezpłatny kurs ON-LINE cena: 249 Zestaw światłolub cena: 14,90 I DUZO DUZO Wiecej. Prosze sprawdzic zdjecia w celu weryfikacji reszty. W razie pytan prosze dzwonic, pozdrawiam. Zapraszam do ogloszenia: https://www.olx.pl/oferta/sensory-moduly-do-arduino-raspberry-pi-elektronika-mechatronika-CID99-IDy0HAJ.html
  18. AJAX umożliwia przekazywanie danych pomiędzy klientem a serwerem WWW bez konieczności przeładowania strony. Dodając do tego timer w JavaScript możemy uzyskać świeże dane na stronie generowanej przez ESP8266. Na początek stwórzmy w PHP najprostszą stronę WWW prezentującą aktualną godzinę pobieraną z serwera (nie "JavaScriptovy" czas z przeglądarki - w końcu docelowo chcemy pobierać dane z czujników podłączonych do "serwera WWW" postawionego na ESP8266): <? if ($_REQUEST["time"]) { echo date("G:i:s"); exit; } ?> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title>AJAX test</title> </head> <body> <span id="time"></span> <script> myTimer(); var myVar = setInterval(myTimer, 1000); function myTimer() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { document.getElementById("time").innerHTML = this.responseText; } }; xhttp.open("GET", "test.php?time=1", true); xhttp.send(); } </script> </body> </html> Timer co sekundę przywołuje funkcję myTimer(); var myVar = setInterval(myTimer, 1000); a ta pobiera zawartość podstrony test.php?time=1 i wstawia ją do elementu o nazwie "time" (document.getElementById("time").innerHTML = this.responseText;). Mamy tu tylko jeden przekazywany parametr. Co zrobić, by aktualizować kilka różnych wartości? Przywykłem do podstrony przekazującej parametry rozdzielone znakiem ; (odczyt1;odczyt2;odczyt3). Dzięki JavaScriptowej funkcji split możemy podzielić taki ciąg znaków na tablice, a potem przydzielić jej części do elementów SPAN o określonym ID. <? if ($_REQUEST["time"]) { echo date("d.m.y;G:i:s"); exit; } ?> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title>AJAX test</title> </head> <body> Date: <span id="date"></span><br> Time: <span id="time"></span> <script> myTimer(); var myVar = setInterval(myTimer, 1000); function myTimer() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var str = this.responseText; var values = str.split(";"); document.getElementById("date").innerHTML = values[0]; document.getElementById("time").innerHTML = values[1]; } }; xhttp.open("GET", "test.php?time=1", true); xhttp.send(); } </script> </body> </html> Działa! PHP po zainstalowania serwera np NGINX możemy uruchomić na Raspberry Pi. W połączeniu (komenda system()) z zewnętrznym skryptem Python lub programem w C korzystającym z WiringPi otrzymamy stronę z odczytami czujników hostowaną na Raspberry Pi! Spróbujmy wreszcie zaprogramować mikrokontroler ESP8266. Podłączmy najpierw czujnik BME280. /* I2C D4 - SCL D3 - SDA */ #include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <Wire.h> #include <Adafruit_Sensor.h> #include <Adafruit_BME280.h> char* ssid = "Weather"; //const char *password = ""; ESP8266WebServer server(80); Adafruit_BME280 bme; void setup() { Serial.begin(9600); Wire.begin(D3, D4); Wire.setClock(100000); if (!bme.begin(0x76)) //changed from default I2C adress 0x77 { Serial.println("Nie odnaleziono czujnika BMP085 / BMP180"); while (1) { } } IPAddress apIP(192, 168, 1, 1); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); // WiFi.softAP(ssid, password); WiFi.softAP(ssid); server.on("/", handleRoot); server.on("/sensors", handleSensors); server.begin(); } void loop() { server.handleClient(); } void handleRoot() { String content = "<html> <head><title>Weather</title></head><body>"; content += "<DIV style=\"display:table; font-size: large;\"><DIV style=\"border-style: solid;\">BME280:<BR>Temperature: <span id=\"tempBME\"></span>C<br>Humidity: <span id=\"humBME\"></span>%<br>Pressure: <span id=\"presBME\"></span>Pa<br></DIV>"; content += "<script>myTimer();var myVar = setInterval(myTimer, 1000);function myTimer() {var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var str = this.responseText; var values = str.split(\";\"); document.getElementById(\"tempBME\").innerHTML = values[0]; document.getElementById(\"humBME\").innerHTML = values[1]; document.getElementById(\"presBME\").innerHTML = values[2];} }; xhttp.open(\"GET\", \"sensors\", true); xhttp.send();}</script>"; content += "</body></html>"; server.send(200, "text/html", content); } void handleSensors() { String content = String(bme.readTemperature()) + ";" + String(bme.readHumidity()) + ";" + String((int)bme.readPressure()) + ";"; server.send(200, "text/html", content); } Rozdzielanie danych przecinkiem czy średnikiem nie jest sposobem "zbyt profesjonalnym". Przy dużej ilości zmiennych łatwo też o pomyłkę. Dlatego lepiej wtedy stosować bardziej odpowiednie formaty danych: JSON czy XML. Ze względu na "bliskość" JSON z JavaScript skupię się tylko na nim. Gotowa biblioteka ArduinoJson wygeneruje dane za nas. Więcej informacji znajdziemy w rozdziale Serialize with ArduinoJson dokumentacji technicznej. /* I2C D4 - SCL D3 - SDA */ #include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <Wire.h> #include <Adafruit_Sensor.h> #include <Adafruit_BME280.h> #include <ArduinoJson.h> //https://github.com/bblanchon/ArduinoJson char* ssid = "Weather"; //const char *password = ""; ESP8266WebServer server(80); Adafruit_BME280 bme; void setup() { Serial.begin(9600); Wire.begin(D3, D4); Wire.setClock(100000); if (!bme.begin(0x76)) //changed from default I2C adress 0x77 { Serial.println("Nie odnaleziono czujnika BMP085 / BMP180"); while (1) { } } IPAddress apIP(192, 168, 1, 1); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); // WiFi.softAP(ssid, password); WiFi.softAP(ssid); server.on("/", handleRoot); server.on("/sensors", handleSensors); server.begin(); } void loop() { server.handleClient(); } void handleRoot() { String content = "<html> <head><title>Weather</title></head><body>"; content += "<DIV style=\"display:table; font-size: large;\"><DIV style=\"border-style: solid;\">BME280:<BR>Temperature: <span id=\"tempBME\"></span>C<br>Humidity: <span id=\"humBME\"></span>%<br>Pressure: <span id=\"presBME\"></span>Pa<br></DIV>"; content += "<script>myTimer();var myVar = setInterval(myTimer, 1000);function myTimer() {var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var str = this.responseText; var values = JSON.parse(str); document.getElementById(\"tempBME\").innerHTML = values.temp; document.getElementById(\"humBME\").innerHTML = values.hum; document.getElementById(\"presBME\").innerHTML = values.press;} }; xhttp.open(\"GET\", \"sensors\", true); xhttp.send();}</script>"; content += "</body></html>"; server.send(200, "text/html", content); } void handleSensors() { String content; StaticJsonBuffer<400> jsonBuffer; JsonObject& root = jsonBuffer.createObject(); root["temp"] = bme.readTemperature(); root["hum"] = bme.readHumidity(); root["press"] = (int)bme.readPressure(); root.printTo(content); server.send(200, "text/html", content); } Strona prezentować się będzie tak samo jak poprzednio. Zyskamy za to wygodny i czytelny sposób dopisywania nowych danych root["temp"] = bme.readTemperature(); oraz ich odczytywania w kodzie źródłowym strony: document.getElementById(\"tempBME\").innerHTML = values.temp; Nie przejmujemy się już numeracją elementów długiej tablicy. Sytuację możemy też odwrócić tworząc stronę WWW, której formularz sterować będzie pracą naszego fizycznego urządzenia. Oczywiście nadal niekonieczne będzie przeładowanie strony, by wysłać dane. #include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> char* ssid = "Weather"; //const char *password = ""; int ledValue = 0; ESP8266WebServer server(80); void setup() { Serial.begin(9600); IPAddress apIP(192, 168, 1, 1); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); // WiFi.softAP(ssid, password); WiFi.softAP(ssid); server.on("/", handleRoot); server.begin(); pinMode(D1, OUTPUT); analogWriteRange(100); //http://esp8266.github.io/Arduino/versions/2.0.0/doc/reference.html analogWriteFreq(500); } void loop() { server.handleClient(); analogWrite(D1, ledValue); } void handleRoot() { if (server.hasArg("ledVal") && server.arg("ledVal").toInt() >= 0 && server.arg("ledVal").toInt() <= 100) { ledValue = server.arg("ledVal").toInt(); Serial.print("ledVal "); Serial.println(ledValue); server.send(200, "text/html", ""); return; } //https://www.w3schools.com/howto/howto_js_rangeslider.asp //https://stackoverflow.com/questions/9713058/send-post-data-using-xmlhttprequest String content = "<!DOCTYPE html>" "<html>" "<head>" "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" "<title>Luminosity setter</title>" "<style>" ".slidecontainer {" "width: 1024px;" "margin: 0 auto;" "text-align: center;" "}" ".slider {" " -webkit-appearance: none;" " width: 100%;" " height: 25px;" " background: #d3d3d3;" " outline: none;" " opacity: 0.7;" " -webkit-transition: .2s;" " transition: opacity .2s;" // " margin-left: 20px;" // " margin-right: 20px;" "}" ".slider:hover {" " opacity: 1;" "}" ".slider::-webkit-slider-thumb {" " -webkit-appearance: none;" " appearance: none;" " width: 25px;" " height: 25px;" " background: #4CAF50;" " cursor: pointer;" "}" ".slider::-moz-range-thumb {" " width: 25px;" " height: 25px;" " background: #4CAF50;" " cursor: pointer;" "}" "</style>" "</head>" "<body>" "<div class=\"slidecontainer\">" " <input type=\"range\" min=\"0\" max=\"100\" value=\"" + String(ledValue) + "\" class=\"slider\" id=\"myRange\">" " <p>Value: <span id=\"demo\"></span>%</p>" "</div>" "<script>" "document.getElementById(\"demo\").innerHTML = document.getElementById(\"myRange\").value;" "document.getElementById(\"myRange\").oninput = function() {" " document.getElementById(\"demo\").innerHTML = this.value;" "var data = new FormData();" "data.append('ledVal', this.value);" "var xhr = new XMLHttpRequest();" "xhr.open('POST', '/', true);" "xhr.onload = function () {" " console.log(this.responseText);" "};" "xhr.send(data);" "}" "</script>" "</body>" "</html>"; server.send(200, "text/html", content); } W tym przypadku przesunięcie suwaka na stronie sterującej wywoła natychmiastową zmianę jasności diody LED podłączonej odpowiednim rezystorem do pinu D1.
  19. 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ęć
  20. Witam forumowiczów, na wstępie wspomnę iż dopiero rozpoczynam swoją przygodę z elektroniką toteż proszę o wyrozumiałość jeśli pojawi się taka potrzeba. Mam do przygotowania urządzenie którego opis przygotowałem poniżej. Urządzenie ma posiadać wbudowaną pamięć w której przechowywane będą pliki audio oraz pliki tekstowe (.txt, .xlsx, .csv, itp.) które powinny mieć możliwość łatwej aktualizacji (np. podłączenie tabletu lub telefonu przez bluetooth lub usb / wifi / wbudowany slot na kartę SIM i aktualizacja przez internet (preferowane jeśli cena nie okaże się zaporowa)). Pamięć nie powinna być potrzebna większa niż 100 megabajtów. Urządzenie ma być zamknięte w niesprecyzowanej jeszcze obudowie z jednym przyciskiem zewnętrznym po naciśnięciu którego na podstawie zawartości plików tekstowych oraz aktualnej godziny odtwarzany ma być wybrany przez algorytm plik audio. Urządzenie powinno być dostosowane do działania w warunkach zewnętrznych. Zasilanie urządzenia ma pochodzić z sieci energetycznej, ewentualnie z baterii połączonej z panelem fotowoltaicznym. W pierwszej kolejności muszę ustalić platformę która będzie najlepsza (najtańsza ze spełniających założenia). Po wstępnym sprawdzeniu rozwiązań doszedłem do wniosku, że najlepszą platformą do realizacji powyższego zadania będzie Arduino ewentualnie Raspberry pi‎. Oba rozwiązania wydają się oferować wszystko czego potrzebuję. Czy mam rację i powinienem wybrać jedno z tych rozwiązań? Może coś przeoczyłem i wspomniane rozwiązania uniemożliwiają realizację któregoś z opisanych wymagań? Czy może jest jakieś inne które spełni wymagania a w realizacji okaże się tańsze? Z góry dziękuję za zainteresowanie i odpowiedź. W przypadku zainteresowania wątkiem chętnie będę go kontynuować ponieważ wybór rozwiązania to dopiero pierwszy krok na długiej drodze do powstania prototypu.
  21. Jednym z moich pierwszych "poważniejszych" projektów, jakie zrealizowałem w ramach nauki programowania mikrokontrolerów był EtherGeiger - sieciowy detektor promieniowania jonizującego. Projekt stworzony w 2014 roku miał być swoistym żartem, nawiązującym swoją tematyką do Fallouta i post-apokaliptycznych filmów science-fiction. Wszystko zaczęło się od zakupionego na Allegro radzieckiego detektora Geigera typu STS-5. Przyrząd ten wymaga do działania wysokiego napięcia, w okolicy 400V. Jest ono wytwarzane przez przetwornicę typu flyback, pracującą na popularnym układzie MC34063A. Sercem pierwszej wersji urządzenia był mikrokontroler Atmega328, a za komunikację ze światem zewnętrznym odpowiadał układ ENC28J60, zwykle stosowany w popularnych modułach, tutaj przylutowany bezpośrednio do płytki urządzenia. Oprogramowanie sterujące pracą urządzenia zostało napisane w języku C, przy pomocy środowiska Atmel Studio. Poza pomiarem promieniowania tła urządzenie zbierało także informacje o temperaturze otoczenia, ciśnieniu atmosferycznym i wilgotności względnej. Wykorzystane zostały w tym celu moduły BMP280 oraz DHT11/DHT22. Wyniki pomiarów były przekazywane do serwera odpalonego na Raspberry Pi, który był odpowiedzialny za ich zapisywanie. W przypadku wykrycia wartości przekraczającej ustaloną wcześniej normę, oprogramowanie wysłałoby powiadomienie na telefon, za pośrednictwem serwisu Pushover. Projekt będący w zamierzeniach jedynie żartem (o pewnej wartości dydaktycznej) dwukrotnie okazał się być bardzo przydatny, gdy do mediów społecznościowych zaczęły trafiać fake newsy mówiące o awariach elektrowni atomowych i chmurze radioaktywnego pyłu, zmierzającego rzekomo nad Polskę. W ubiegłym roku projekt doczekał się nowej wersji, opartej na mikrokontrolerze PIC32MX270F265B. Pozwoliło to na dodanie nowych funkcji, m.in. wyposażenie urządzenia w PenDrive'a do lokalnego zapisywania pomiarów (w przypadku jego braku dane są zapisywane we wbudowanej pamięci SPI Flash). Zastosowany został także pojedynczy czujnik temperatury, ciśnienia i wilgotności, typu BME280. Planowana jest także rozbudowa tej wersji o dodatkową płytkę z wyświetlaczem LCD.
  22. Historia tego projektu sięga kilka lat wstecz, kiedy to zetknąłem się z tematem wykorzystania routerów na OpenWRT do budowy odbiorników internetowej radiofonii (link, link, link). Początkowo planowałem wykonanie własnej wersji takiego urządzenia w oparciu o przerobiony router i kartę dźwiękową na USB, w międzyczasie jednak pojawiło się pierwsze Raspberry Pi. W oparciu o "Malinę" powstał niezbyt elegancki prototyp, wykorzystujący płytkę stykową, wyświetlacz HD44780 oraz kilka przycisków. Zdjęcia tej wersji niestety nie zachowały się. Radio pracowało w oparciu o Raspbiana Wheezy oraz program MPD. Odtwarzaniem można było sterować za pomocą przycisków, interfejsu WWW albo z poziomu telefonu z Androidem, za pomocą klienta MPD. Niestety dały o sobie znać problemy wynikające z kiepskiej jakości sygnału na wyjściu audio RasPi - szczególnie przy niskim poziomie głośności słyszalne były "artefakty" w postaci wysokich, przerywanych pisków. Stało się dla mnie jasne, że w finalnej wersji projektu konieczne będzie zastosowanie lepszego układu DAC. W tym momencie projekt został na jakiś czas odłożony na półkę. W międzyczasie w moim domu na rynku pojawił się Chromecast audio, co spowodowało dodatkowy spadek motywacji do skończenia projektu, który utracił sporo praktycznej użyteczności. Wraz z premierą Raspberry Pi Zero zero postanowiłem mimo wszystko dokończyć go, z myślą o efekcie "dydaktycznym". W efekcie powstała płytka wyposażona w złącze do podłączenia RasPi Zero oraz kolorowego ekranu LCD na magistrali SPI. Ponieważ pierwsza wersja RasPi Zero nie była wyposażona w moduł WiFi, na PCB zostało przewidziane miejsce pod gniazdko ethernetowe oraz kontroler ENC28J60. Po premierze RPi Zero W płytka została podmieniona, dzięki czemu obecnie urządzenie może korzystać z łączności bezprzewodowej. W roli przetwornika audio wykorzystany został układ WM8731. Na płytce znalazło się także miejsce dla odbiornika podczerwieni, dzięki czemu możliwe jest sterowanie za pomocą pilota. Nazwa eMPeDocles jest oczywiście nawiązaniem do programu MPD, który odpowiada za odtwarzanie internetowych streamów oraz plików muzycznych z lokalnego dysku sieciowego. Na chwilę obecną do zrobienia pozostało już tylko wykonanie obudowy (skłaniam się ku wycinanej laserowo z pleksi) oraz napisanie prostego interfejsu graficznego w Qt. W planach jest także dodanie funkcji prostego menadżera podcastów.
  23. Hej, co jest grane jeśli mam włączone WinSCP, polaczony bez problemu, a kiedy próbuję utworzyć nowy katalog lub plik do tego zadania ze stroną html to w winSCP wyskakuje mi kod błędu 3, acces denied...?? a druga sprawa, skąd tu ten "blog"?: sudo mkdir /var/www/html/forbot cd /var/www/html/blog/forbot sudo nano index.html EDIT: ok, poszperałem i tak: wpisałem komendę sudo su co sprawiło, że stałem sie rootem, cokolwiek to znaczy, ale "mogłem wszystko", wpisalem sudo adduser nick, udało się utworzyć nowego użytkownika, wpisałem sudo visudo i w pliku skopiowalem uprawnienia roota na uzytkowników pi i nick, teraz bez problemu mogę pracować w winSCP, nie ma błędu braku dostępu. Kto mi teraz powie czy strasznie namieszałem, czy nie...
  24. Świetnie, wszystko pieknie działa, nastepny odcinek za mną... Powiedzcie mi, taką rzecz, jak ustawić nowego użytkownika, po prostu nie chcę "pi" tylko inaczej, ale tak z pelnymi uprawnieniami jak"pi", żeby nie było później problemów...
  25. Witam, mam problem zastanawiam się czy mam inną kamerę. Ponieważ nie działa mi polecenie: sudo modprobe bcm2835-v4l2 wciskając enter po tej komendzie nic się nie dzieje. Reszta poleceń przechodzi jednak nie mam obrazu wpisując adres mojej maliny ;/ Wydaje mi się żę Motion nie widzi mojej kamery. Komunikat z przeglądarki: unable to open device Czy mogę liczyć na pomoc? Dodam, że z poziomu linuxa zdjęcia mogę robić z kamery wiec działa.
×