Przeszukaj forum
Pokazywanie wyników dla tagów 'Raspberry Pi'.
Znaleziono 138 wyników
-
Dzień dobry, mam problem z ustawieniem wejść cyfrowych na raspberry Pi4b, chciałbym skorzystać z funkcji WiringPiISR aby odczytywać tylko zbocze narastające. 1. Sensor przepływu cieczy wysyła sygnał cyfrowy na pin "1", 2. Odczytuje kolejne zbocza narastające na danym pinie i zliczam je w zmiennej. Tutaj rozumiem, że ta funkcja WiringPiISR wymaga podania:pinu, rodzaju zbocza, oraz co ma być wykonane 3. Po zliczeniu np. 100 sygnałów ma się wykonać funkcja wyłączająca pompkę Problem napotykam w samej składni, ponieważ nie jestem doświadczonym programista, i niewiele wiem o qt, Etap na którym ugrzązłem: W konstruktorze: WiringPiISR (pin_1,INT_EDGE_RISING,isrInput); Oraz zadeklarowałem void(*isrInput)(void); Tylko nie wiem co to tak naprawdę oznacza. Chciałbym jedynie aby dany pin odczytywał zbocze narastające Przepraszam za brak kodu, bo nie mam aktualnie możliwości wstawienia, oczywiście mogę podesłać plik .h oraz .cpp aczkolwiek jest to goła aplikacja w qt creator jedynie z kodem inicjalizujacym bibliotekę wiringPi oraz dane piny. Dziękuję z góry za każdą wskazówkę jak dany problem rozwiązać
- 1 odpowiedź
-
- WiringPi
- WiringPiISR
- (i 2 więcej)
-
Dzień dobry, mam problem z ustawieniem wejść cyfrowych na raspberry Pi4b, chciałbym skorzystać z funkcji WiringPiISR aby odczytywać tylko zbocze narastające. 1. Sensor przepływu cieczy wysyła sygnał cyfrowy na pin "1", 2. Odczytuje kolejne zbocza narastające na danym pinie i zliczam je w zmiennej. Tutaj rozumiem, że ta funkcja WiringPiISR wymaga podania:pinu, rodzaju zbocza, oraz co ma być wykonane 3. Po zliczeniu np. 100 sygnałów ma się wykonać funkcja wyłączająca pompkę Problem napotykam w samej składni, ponieważ nie jestem doświadczonym programista, i niewiele wiem o qt, Etap na którym ugrzązłem: W konstruktorze: WiringPiISR (pin_1,INT_EDGE_RISING,isrInput); Oraz zadeklarowałem void(*isrInput)(void); Tylko nie wiem co to tak naprawdę oznacza. Chciałbym jedynie aby dany pin odczytywał zbocze narastające Przepraszam za brak kodu, bo nie mam aktualnie możliwości wstawienia, oczywiście mogę podesłać plik .h oraz .cpp aczkolwiek jest to goła aplikacja w qt creator jedynie z kodem inicjalizujacym bibliotekę wiringPi oraz dane piny. Dziękuję z góry za każdą wskazówkę jak dany problem rozwiązać
-
- WiringPi
- WiringPiISR
- (i 2 więcej)
-
Błąd przy otwarciu udostępnianego pliku libmmal.so (picamera)
Pawelitto opublikował temat w Raspberry Pi
Siema. Mam problem z kamerką w raspberry pi 3, ponieważ występuje błąd przy importowaniu biblioteki 'picamera'. Kod python: from picamera import PiCamera from time import sleep camera = PiCamera() camera.start_preview() sleep(5) camera.stop_preview() Kod błędu: Traceback (most recent call last): File "/home/pi/Desktop/camera.py", line 1, in <module> from picamera import PiCamera File "/usr/local/lib/python3.9/dist-packages/picamera/__init__.py", line 72, in <module> from picamera.exc import ( File "/usr/local/lib/python3.9/dist-packages/picamera/exc.py", line 41, in <module> import picamera.mmal as mmal File "/usr/local/lib/python3.9/dist-packages/picamera/mmal.py", line 49, in <module> _lib = ct.CDLL('libmmal.so') File "/usr/lib/python3.9/ctypes/__init__.py", line 374, in __init__ self._handle = _dlopen(self._name, mode) OSError: libmmal.so: cannot open shared object file: No such file or directory- 2 odpowiedzi
-
- Raspberry Pi
- picamera
-
(i 3 więcej)
Tagi:
-
Dzień dobry, Chciałbym się zapytać czy istnieje na urządzeniu Raspberry Pi 3B+ polski lektor(męski) ?- Python, biblioteka: pyttsx3. Szukam już po internecie od dłuższego czasu i na nic nie mogę trafić. Id polskiego głosu jaki pokazuje mi program brzmi "polish" jednak po wybraniu go brzmi on dość mało "polsko". Chciałbym się dowiedzieć czy ten głos po prostu taki jest, czy ja coś robię nie tak. Jeśli istnieje jakiś inny sposób dodania polskiego głosy do maliny to prosiłbym o podpowiedź gdzie i jak mogę go znaleźć. (Sorki za jakoś zdjęcia, ale zależy mi na czasie)
- 1 odpowiedź
-
- Raspberry Pi
- Pyton
-
(i 2 więcej)
Tagi:
-
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>
- 5 odpowiedzi
-
- 5
-
-
-
- Arduino
- Raspberry Pi
-
(i 3 więcej)
Tagi:
-
MacintoshPi - Mac OS 7, 8 i 9 oraz Commodore dla Raspberry Pi
jaromaz opublikował temat w Projekty - DIY
MacintoshPi to mój mały projekt pozwalający na uruchomienie pełnoekranowych systemów Apple: Mac OS 7, Mac OS 8 i Mac OS 9 z dźwiękiem, aktywnym połączeniem internetowym i emulacją modemu pod Raspberry Pi OS Lite. Wszystko bez menadżera X.org, a wyłącznie za pomocą multimedialnej biblioteki SDL2 i z poziomu CLI – Raspberry Pi OS Lite. Dzięki temu emulatory wykorzystują pełną moc Raspberry Pi, są stabilniejsze i użyteczne w połączeniu z dodatkowym retro-oprogramowaniem. Instalacja wymaga uruchomienia jednego skryptu na czystym systemie Raspberry Pi OS Lite i poczekania około dwóch godzin na kompilację i instalację pakietów. Dodatkowo, dzięki zawartemu w projekcie dokumentowi, w dual-boot możliwe jest umieszczenie najszybszego (bare-metal) emulatora Commodore 64/128/PET BMC64 budując w ten sposób ciekawy retro pakiet na jednej karcie SD. Cały projekt MacintoshPi działa z urządzeniami Raspberry Pi Zero 2W, 2, 3, 3B, 3B+ (na razie nie działa z wersją 4). Poniżej film przedstawiający możliwości projektu MacintoshPi: Istnieje system Mac OS 7 dla maliny, ale do tej pory system Mac OS 9 w pełnoekranowym trybie (bez zbędnego menadżera okien - dla Raspberry Pi OS Lite) nie był dostępny. To chyba pierwsza taka implementacja pozwalająca na korzystanie z Mac OS 9 i SDL2 w trybie pełnoekranowym, z dźwiękiem i połączeniem internetowym – nawet na małym Raspberry Pi 2 W (a działa również na Raspbery Pi 2). Zajmuję się tematem emulacji Mac OS kilka lat (bo bardzo lubię retro systemy Apple'a) i temat jest dość złożony (gdyby zaczynać wszystko od początku): problemy z konfiguracją obrazów systemów pod obsługę Internetu, z właściwymi konfigami do emulatorów, z właściwymi opcjami do kompilacji emulatorów oraz (oddzielnie) SDL2 (bo oczywiście SDL z paczki nie zadziała w emulacji), z właściwą kompilacją NetDrivera, z wersjami bibliotek (bo muszą być legacy), problemy z dźwiękiem itp. itd. Oczywiście mówię o problemach, które napotkają początkujący, po czym zniechęcą się i rzucą te emulatory w kąt. Ponieważ rozwiązałem te wszystkie problemy na swoim Raspberry-Macintoshu, to postanowiłem udostępnić to rozwiązanie dla Wszystkich, tak żeby wystarczyło odpalić jeden skrypt i otrzymać wszystkie trzy systemy w jednym pakiecie bez żadnego wysiłku - i tym właśnie jest ten mały projekt. Składniki projektu W skład projektu wchodzą poniższe skrypty bash'owe auto-kompilujące i instalujące dla Raspberry Pi: Emulator Macintosh 68K Basilisk II obsługujący systemy Mac OS 7 (System 7.5.5) i Mac OS 8, Emulator PowerPC SheepShaver obsługujący system Mac OS 9, Emulator Commodore 64/128/Pet VICE, Wirtualny Modem wykorzystujący projekty tty0tty i tcpser, działający z powyższymi dwoma emulatorami dla produktów Apple, Commodore oraz z samym Raspberry Pi OS i pozwalający na łączenie się dowolnym oryginalnym retro-oprogramowaniem terminalowym z dzisiejszymi telnetowymi BBSami, Emulator CD-ROM, DVD-ROM CDEmu, pozwalający na montowanie pod Linuxem obrazów CD (iso, toast, cue/bin, mds/mdf itp.) – działa z emulatorami BasiliskII i SheepShaver oraz z Raspberry Pi OS. Emulatory są automatycznie skonfigurowane do obsługi tego wirtualnego napędu CD-ROM. Spójny launcher uruchamiający te wszystkie systemy w różnych rozdzielczościach (po restarcie) i w różnych konfiguracjach, SyncTERM – program do łączenia się z BBSami z poziomu Raspberry Pi OS kompilowany w połączeniu z biblioteką SDL, Informacje jak uruchomić w dual-boot Raspberry Pi OS z najszybszym emulatorem Commodore dla Raspberry Pi BMC64 – (bare metal/low latency emulator). O moim Macintoshu opartym na Raspberry Pi Moją wersję MacintoshPi napędza Raspberry Pi 3B+. Obudowę Macintosh Classic II zakupiłem na eBay-u – była kompletnie żółta, ale do stanu zgodnego z fabrycznym przywróciłem ją korzystając z wody utlenionej 18% i odpowiedniego naświetlania. Ekran LCD IPS 10,1'' 1024x600px HDMI Waveshare 11870 (zakupiony w Botlandzie) jest obrócony o 180°, aby okablowanie nie przeszkadzało górnej, wąskiej ramce Macintosha. Przestrzeń między płaskim ekranem, a pozostałościami kształtu CRT uzupełniłem wydrukiem 3D, który został zaprojektowany na potrzeby projektu twórców kanału YouTube 2GuysTek. Ekran Waveshare jest nieco zbyt szeroki, ale odpowiednie operowanie plikiem config.txt pozwala software'owo obrócić ekran i ustalić dokładną pozycję wyświetlanego obrazu, dla każdego z systemów lub programów oddzielnie (po każdorazowym restarcie). Ekran jest też nieco zbyt niski, dlatego puste przestrzenie wypełniłem czarnym bristolem i praktycznie nie widać tych elementów (wyglądają jak czarne tło ramki otaczającej ekran) – zalecam jednak wykorzystanie innego monitora, którego rozmiar będzie nieco większy, a dopiero software'owo zmniejszyć rozmiar i ustalić właściwą pozycję wyświetlanego obrazu. Klawiatura i mysz to zestaw Logiteh MK295 Silent Wireless Combo – są tylko nieznacznie zbliżone stylem do dostarczanych w tamtych latach peryferii, ale są też bezprzewodowe i korzystają z pojedynczego dongla bluetooth. Dodałem dwa głośniki podłączone do wejścia audio analog/jack Raspberry Pi 3B+ i do rozgałęźnika. Wszystkie te elementy są zintegrowałem wewnątrz obudowy komputera Apple Macintosh Classic II. Więcej informacji o MacintoshPi można znaleźć na GitHubie projektu: https://github.com/jaromaz/MacintoshPi lub po polsku na mojej stronie prywatnej: https://jm.iq.pl Wzmianka o tym projekcie pojawiła się w serwisie Hackster.io - również na ich Twitterze, chociaż nie wysyłałem im żadnych informacji 🙂 : https://www.hackster.io/news/jaromaz-s-macintoshpi-turns-a-raspberry-pi-into-a-fast-feature-packed-classic-apple-mac-emulator-be88c17f66a8 -
Witajcie. Mam pytanie. Czy wejście żeńskie tych przewodów (https://botland.com.pl/przewody-polaczeniowe/6174-przewody-polaczeniowe-zensko-meskie-10cm-40szt-5904422304423.html) pasuje do wyjść GPIO RPi 4B (4GB RAM)? Będę wdzięczny za odpowiedź. 🙂
- 9 odpowiedzi
-
Cześć, mam pomyśł na nowy kurs. Co powiecie na kurs RPi Pico? Myślę, że są osoby, które są zainteresowane tym tematem. 🙂
-
Cześć, Zaprezentuje swojego robota albo raczej prototyp, który powstał głównie do stworzenia i testowania oprogramowania do zarządzania robotem. Natomiast drugim celem było zebranie danych dla sieci neuronowej. Zadaniem robota było jeżdżenie miedzy liniami i zebranie danych z kamery i sterownika silników. I ostatnim celem było wykorzystanie nauczonej sieci prowadzania robota między liniami. Elektronika i zasilanie: Raspberry Pi 4B WiFi 4GB RAM kamera HD OV5647 5Mpx sterownik silników TB6612FNG siniki i przekładnia (344,2:1) to jakaś stara Tamiya power bank 20000mAh,2A MAX chyba Części mechaniczne i konstrukcyjne: gąsienice i koła wydrukowane (pobrane ze strony: www.printables.com) mocowanie do kamery drukowane Oprogramowanie: https://github.com/noxgle/pt2 Problemy: Sterownik silników jest zasilany bezpośrednio z Raspberry co powdowiało że przy pełnym wypełnieniu pwm "brakowało prądu" i następował restart. Zmniejszenie wypełnienia pwm do 50% pomogło do momentu większego rozładowanie baterii. Robotem podczas zbierania danych sterowała aplikacja uruchomiona na laptopie po sieci WiFI. Dane z kamery były obrabiane były wykorzystaniem biblioteki OpenCV. Sterowanie robotem z nauczoną siecią też odbywało się z wykorzystaniem laptopa. Do nauki została wykorzystana biblioteka fast.ai
-
Przeglądając listę możliwych integracji Home Assistant w większości jest tam to czego potrzebujemy. Sam byłem w szoku, gdy aplikacja podpowiedziała mi integrację z tunerem audio, o którym bym nawet nie pomyślał że się do tego nadaje. Problem pojawia się, gdy wymyślimy sobie własne DIY, które robi coś unikatowego i chcemy to podłączyć pod automatykę domowa. Jedną z metod jest użycie Template Switch (czyli takiego wirtualnego przełącznika) i powiązanie go funkcją lambda z np. komponentem magistrali UART i komunikowanie się z naszym DIY. Problem w tym, że będziemy musieli poświęcić cały układ WiFi na pomost pomiędzy DIY, a centralką HA. W tym artykule postaram się nakreślić, jak zacząć pisać własne komponenty do ESPHome. Przygotowanie Niby jest do tego instrukcja (custom sensor i custom generic component), ale mimo wszystko po przeczytaniu tego co tam zamieszczono, przejrzeniu przykładów, zapytaniu na oficjalnym kanale na Discordzie, odpowiedź znalazłem dopiero na szarym końcu internetu. Wyjdźmy od tego jak tworzymy aplikacje (wsad np. do ESP8266 tu ESP-01S). Mając postawiony HA i zainstalowany dodatek ESPHome, dodajemy nowy sprzęt: Wybieramy nazwę: Rodzaj płytki: Pomijamy wgrywanie, bo trzeba zmienić coś w konfiguracji. Wybieramy więc edit: Mamy tu kilka domyślnych ustawień: esphome: name: spectrum-display esp8266: board: esp01_1m # Enable logging logger: # Enable Home Assistant API api: ota: password: "xxx" wifi: ssid: !secret wifi_ssid password: !secret wifi_password # Enable fallback hotspot (captive portal) in case wifi connection fails ap: ssid: "xxx" password: "xxx" captive_portal: Nazwa - potrzebna do mDNS jako hostname. Niestety po aktualizacji coś słabo działa. Na stronie ESPHome sugeruje się używanie statycznego IP, które ma też przyspieszać łączenie: Dlatego dodajemy fragment dotyczący stałego IP: wifi: ssid: !secret wifi_ssid password: !secret wifi_password manual_ip: static_ip: 192.168.0.102 gateway: 192.168.0.1 subnet: 255.255.255.0 # Enable fallback hotspot (captive portal) in case wifi connection fails ap: ssid: "xxx" password: "xxx" Co w efekcie zobaczymy w logu: Hasło do WiFi mamy ustawione w ESPHome (secrets). Zapisujemy, wgrywamy podłączając urządzenie przez USB lub wgrywając używając OTA, oczywiście jeżeli znamy hasło i adres urządzenia. W moim przypadku jest ta druga opcja: Gotowe komponenty Komponent to pewna funkcjonalność (nawet bardzo rozbudowana). Np. może to być przełącznik światła dołączonego do konkretnego wyprowadzenia (patrz przykład). W przypadku bardziej rozbudowanych komponentów, np. obsłudze protokołu komunikacji, obsługa zawęża się do wskazania urządzenia (np. DS18B20 1-wire) i podania wyprowadzenia. Takich komponentów możemy dodawać wiele i będą działać niezależnie od siebie. To tak jakbyśmy uruchomili wiele współbieżnych procesów. W praktyce wygląda to bardziej jak kod Arduino, w którym przy pomocy funkcji millis() wykonujemy "współbieżnie" wiele funkcji. Podstawy za nami. Teraz konfigurację można wzbogacić o jakieś podstawowe komponenty. Przykładowo kod do sterowania diodą na płytce pod pinem 2: # Enable Home Assistant API api: light: - platform: binary name: "Onboard LED" output: onboard_led_out output: - id: onboard_led_out platform: gpio pin: GPIO2 Id posłuży nam do dodania tzw. encji czyli kontrolki na panelu widocznym w Home Assistant. Wgrywamy i przechodzimy do głównej strony i edytujemy widok panelu: Od razu widzimy encję: Modyfikujemy wedle uznania: i gotowe! Możemy poklikać w nowo dodany przycisk. Najpewniej logika przycisku będzie odwrócona ze względu na sposób podłączenia LED na płytce ESP-01S. Możemy zmienić to w konfiguracji: output: - id: onboard_led_out platform: gpio pin: GPIO2 inverted: true I działa 🙂 Własny komponent Napisanie własnego komponentu nie jest aż tak trudne. Autorzy uznali, że będą wzorować się na koncepcji kodu Arduino , w którym można wyróżnić bloki setup() i loop(). Problem w tym, że nie wiadomo gdzie zapisać te pliki. Na stronie z poradnikiem tworzenia własnych komponentów jest co prawda informacja: Ale niewiele to wnosi, bo nigdzie nie jest napisane gdzie jest ten katalog... Z pomocą przychodzi test przykładowej konfiguracji, w której chcę dodać jakiś plik: Czyli jest to ścieżka: /config/esphome/ Sprawdźmy jak wygląda zawartość tego katalogu, tylko zanim do teog przejdziemy, potrzeba nam SSH - bo w systemie HA nie ma domyślnie takich udogodnień. W repozytorium znajdujemy SSH, ustawiamy hasło i możemy się zalogować. Użytkownik root, hasło własne, port 22. Używam polecenie ls z dopiskiem -a, aby wyświetlić ukryte pliki i katalogi: la -a /config/esphome/ W tym miejscu można umieścić własne pliki bibliotek - tuż obok widzimy plik yaml konfiguracji. Ja swoje pliki umieszczam w katalogu custom_components tak by nie tworzyć bałaganu: Wewnątrz katalogu tworzę plik fps_meter.h: touch fps_meter.h Do pracy korzystam z WinSCP i VSC - zapis pliku w VSC od razu zdalnie go aktualizuje: Dla testu wpisuję kod służący do wyznaczania częstotliwości odświeżeni: #include "esphome.h" class FPSCounter : public Component, public Sensor { private: const unsigned long INTERVAL = 5000; unsigned long counter; unsigned long last_millis; float fps; public: float get_setup_priority() const override { return esphome::setup_priority::LATE; } void setup() override { last_millis = millis(); int last_button_state = HIGH; } void loop() override { if(millis() - last_millis > INTERVAL) { fps = (1000.0 * counter) / (millis() - last_millis); ESP_LOGD("FPS_COUNTER", "%lu sec passed, FPS: %f", INTERVAL, fps); publish_state(fps); counter = 0; last_millis = millis(); } ++counter; } }; I zapisuję. Do tego jak działa ten kod jeszcze wrócimy, ale na razie istotne jest, że w pętli wykonywane jest sprawdzenie częstości odświeżania i okresowo informacja wysyła jest do HA. Komponent jest czujnikiem (dziedziczy po klasie Sensor) ponieważ zwraca pewne informacje do centralki. Teraz trzeba użyć naszą klasę. Przechodzimy do konfiguracji i najpierw dodajemy plik biblioteki: esphome: name: spectrum-display includes: - custom_components/fps_meter.h oraz tworzymy komponent: api: sensor: - platform: custom lambda: |- auto fps_meter = new FPSCounter(); App.register_component(fps_meter); return {fps_meter}; sensors: name: "FPS counter" light: - platform: binary name: "Onboard LED" output: onboard_led_out output: - id: onboard_led_out platform: gpio pin: GPIO2 inverted: true Dodajemy typ sensor (to po czym dziedziczy nasz komponent), a w funkcji lambda korzystamy ze zdefiniowanej klasy FPSCounter. Dodajemy też nazwę, która może nam się przydać. Wgrywamy nowy kod i w logu zobaczymy, że coś zostało wyznaczone - mamy częstotliwość odświeżania około 60Hz: Możemy też znaleźć nasz "czujnik" wśród encji: i nasze obie kontrolki działają, powiedzmy "równolegle" 🙂 Kod programu Wracając na chwilę do kodu programu: #include "esphome.h" class FPSCounter : public Component, public Sensor { public: float get_setup_priority() const override { return esphome::setup_priority::LATE; } void setup() override { } void loop() override { }; widzimy, że szablon jest dość prosty. Tworzymy klasę, która dziedziczy po komponencie i sensorze, aby móc wysyłać informacje (dostępna staje się wtedy metoda publish_state(). Jak wspomniałem setup i loop to metody, które możemy uzupełnić kodem wykonywanym odpowiednio przy starcie i cyklicznie. get_setup_priority() służy ustaleniu jaki priorytet ma nasza klasa. Możliwych narzędzi jest naprawdę wiele, zainteresowanych odsyłam do lektury dokumentacji i analizy przykładów. Kod klasy możemy rozbić na osobne pliki .cpp i .h. Warto jeszcze wspomnieć o czymś, co mnie bardzo mocno zmyliło - o bibliotekach/narzędziach deweloperskich ESPHome. W kodzie widzimy, że dodajemy plik esphome.h, ale nie jest to biblioteka: W pliku tym mamy podlinkowane biblioteki, które będziemy używać, a sam plik wygenerowany zostanie automatycznie... zostanie, ponieważ nasza biblioteka jest w katalogu /config/esphome, ale przed kompilacją jest kopiowana do katalogu projektu: Czyli dla powtórzenia: nasze pliki bibliotek trzymamy w głównym katalogu np. /config/esphome/custom_components/ w pliku .yaml dodajemy ścieżkę: custom_components/plik_biblioteki.h w kodzie pliku plik_biblioteki.h dodajemy esphome.h jakby był tuż obok, bo przed kompilacją zostanie tam przekopiowany tworząc kod pamietamy żeby podlinkować zawartość katalogu src Kuszące może być edytowanie zawartości katalogu src jednak tu uwaga w pliku README.txt: THIS DIRECTORY IS AUTO-GENERATED, DO NOT MODIFY ESPHome automatically populates the build directory, and any changes to this directory will be removed the next time esphome is run. For modifying esphome's core files, please use a development esphome install, the custom_components folder or the external_components feature. Oznacza to, że ten katalog jest wygenerowany automatycznie na bazie pliku .yaml i nie powinniśmy w nim nic mieszać.
- 5 odpowiedzi
-
- 3
-
-
- Raspberry Pi
- Home Assistant
- (i 1 więcej)
-
Witajcie, Podczas korzystania z biblioteki PyTorch napotkałem na komunikat "Illegal instruction". Błąd powstaje podczas wykorzystania "torchaudio.transforms.Resample()", a dokładnie w liniach kodu: transform = torchaudio.transforms.Resample(orig_freq=sample_rate, new_freq=new_sample_rate) tensor = transform(tensor) Podana klasa ma za zadanie zmienić sample_rate nagrania dźwiękowego. Czy spotkał się ktoś z was, z czymś takim ? Pozdrawiam ! 😄
-
Cześć, Napotkałem pewien problem podczas próby instalacji biblioteki PyTorch: Could not find a version that satisfies the requirement torch Wie ktoś może czym jest to spowodowane i jak to naprawić ? Python 3.9.2 Raspbian GNU/Linux 11
-
Dzisiaj kolej na automatykę domową. Od razu na wstępie: nie cierpię określenia "inteligentny dom". Po pierwsze w większości jest ono nadużywane (co to za "inteligencja", która sprowadza się do możliwości zapalenia światła w łazience przez telefon), po drugie mój dom nie ma być inteligentny: ma być przede wszystkim wygodny. Nie wiem jak dla was - ale dla mnie ważniejsze jest zdublowanie wyłącznika oświetlenia przy moim ulubionym fotelu gdzie zwykłem popijać kawusię i oglądać telewizję, niż kombinowanie z pilotami (które zawsze leżą poza zasięgiem ręki) czy smartfonami (gdzie trzeba włączyć aplikację i wybrać odpowiednią funkcję przedzierając się przez jakieś menu). Podobny system już miałem, ale był on jakoś mało rozbudowywalny, poza tym sterowanie nie było zbyt wygodne. Postanowiłem więc zrobić nowy od zera. Cały system składa się z następujących komponentów: Centralka - czyli moduł główny, z klawiaturą, wyświetlaczem, głośnikiem i innymi bajerami, sterująca bezpośrednio kotłem; Wyłączniki oświetlenia - każdy steruje jednym lub dwoma punktami, wyposażone w różnej maści wyłączniki, czujki i takie tam; Kontroler termometrów - najprostszy ze wszystkich modułów, jego zadaniem jest wyłącznie odczyt temperatury z trzech DS18B20 i przesyłanie wyników do centralki. Najpierw może o centralce. Centralka składa się z dwóch głównych układów: Raspberry Pi Zero 2 W jako główny "mózg" urządzenia i ESP-12E do komunikacji ze światem zewnętrznym (poprzez esp-now z pozostałymi modułami oraz poprzez panel sterujący z użytkownikiem). Ponieważ mój kocioł pozwala jedynie na sterowanie typu "włącz/wyłącz", elektrycznie sprawa jest stosunkowo prosta. Do sterowania służą dwa przekaźniki. Pozostawiłem stary sterownik Euroster 2000, ale w czasie pracy jest on odłączony. Do sterowania służą dwa przekaźniki: jeden odpowiada za odłączenie Eurostera gdy centralka jest gotowa do pracy - tzn. RPi się ładnie zabootował, wie która jest godzina, odczytał dane z pamięci EEPROM i FRAM i ma informacje z przynajmniej jednego termometru. Drugi to właściwy przekaźnik sterujący piecem. Układ wygląda mniej więcej tak: Jak widać, aby centralka mogła sterować kotłem, na pinie READY musi pojawić się jedynka. Rezystor 10k zapewnia, że tranzystor będzie zatkany również w przypadku, gdy pin będzie w stanie wysokiej impedancji lub RPi w ogóle nie będzie zasilany. Diody sygnalizują stan przekaźników: READY (zielona) to gotowość do pracy, FAIL (czerwona) to sygnalizacja przełączenia na Euroster, FIRE (niebieska) zapala się przy włączeniu kotła. Oprócz tego do RPi podłączone są: Konwerter I2S ze wzmacniaczem i głośnikiem Moduł zegara DS3231 z pamięcią EEPROM 4 kB Moduł FRAM 32 kB ESP8266 wraz z układem RESET/BOOT Natomiast ESP oprócz komunikacji z pozostałymi modułami obsługuje: wyświetlacz klawiaturę czujnik odległości czujnik temperatury i ciśnienia fotorezystor (jeszcze nie wiem po co, ale miałem wolne wejście analogowe) ESP8266 jest podłączony tak, żeby umożliwić zarówno współpracę układu z malinką, jak i zdalne programowanie: Ponieważ do programowania ESP8266 używam frameworku Arduino, teoretycznie można zainstalować Arduino IDE na malince (tak też zrobiłem na początku). Uznałem jednak, że nie jesto to zbyt wygodne. Dlatego też kompiluję program na lokalnym pececie z linuksem na pokładzie, a do malinki przesyłam tylko gotowy plik bin. I tu mała uwaga: nie podaję pełnego rozwiązania choćby z tego względu, że w rzeczywistości skrypty są bardziej skomplikowane, ale jest to związane ze specyfiją jinjretnego urządzenia i jego możliwości. Ale zasada działania jest mniej więcej taka, jak niżej. Do przesłania skompilowanego pliku używam swojego wrappera. Zewnętrzne polecenie to: ssh centralka.local /usr/local/bin/esprmt a na wejście podaję plik BIN. Oczywiście połączenie ssh musi być skonfigurowane tak, aby nie trzeba było wpisywać hasła (czyli za pomocą kluczy). Na samej malince jest to już bardziej skomplikowane. Najpierw trzeba było napisać kawałek programu, który zresetuje ESP i wprowadzi w tryb programowania. W tym celu stworzyłem plik /etc/default/esp zawierający opisy pinów: boot=24 prog=23 Program w pythonie wygląda tak: #!/usr/bin/env python #coding: utf-8 cfg={} for line in open('/etc/default/esp'): line=line.strip().split('=') if len(line) == 2: cfg[line[0].strip()] = line[1].strip() bootp=int(cfg['boot']) progp=int(cfg['prog']) import sys arg = sys.argv[1] if arg not in ('boot','prog'): raise Exception('Bad arg') import RPi.GPIO as GPIO, time GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) GPIO.setup(bootp, GPIO.OUT) GPIO.setup(progp, GPIO.OUT) if arg == 'boot': GPIO.output(progp, 0) GPIO.output(bootp, 1) time.sleep(0.1) GPIO.output(bootp, 0) else: GPIO.output(bootp, 1) time.sleep(0.1) GPIO.output(progp, 1) time.sleep(0.1) GPIO.output(bootp, 0) time.sleep(0.1) GPIO.output(progp, 0) time.sleep(0.1) Po zapisaniu do /usr/local/bin/espy i nadaniu praw do uruchomienia mogę wykonać reset ESP poleceniem: espy boot lub wprowadzić w tryb programowania: espy prog Teraz tylko wystarczy wrzucić program do ESP. Do tego służy skrypt /usr/local/bin/esprmt: #!/bin/bash cat >/tmp/upload.bin /usr/local/bin/espy prog /home/pi/.arduino15/packages/esp8266/tools/python3/3.7.2-post1/python3 -I \ /home/pi/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/tools/upload.py \ --chip esp8266 --port /dev/ttyS0 --baud 230400 \ --before no_reset_no_sync --after soft_reset \ write_flash 0x0 /tmp/upload.bin No i trochę o możliwościach. Ponieważ prekursorem urządzenia był po prostu regulator temperatury (na RPI 1B), i tu kontrola temperatura pozostała głównym zadaniem. Piec może pracować w następujących cyklach: zwykły dzień (rano i wieczorem cieplej, w nocy i w typowych godzinach wyjścia temperatura spada, poza tym w dzień utrzymane 18°). Ze względu na specyfikę naszej pracy na każdy dzień tygodnia mogę ustalić oddzielny program. dzień świąteczny. Jest taki sam jak program "niedziela", w stosunku do zwykłego dnia przesunięta jest godzina poranna (trochę później się wstaje) i nie ma godzin wyjścia. jestem w domu - jak zwykły dzień, ale bez godzin wyjścia wychodzę od-do (godziny wyjścia ustalane indywidualnie) wyjazd (utrzymana stała temperatura nie mniej niż 13°) program indywidualny (tu mogę zaprogramować dokładnie kiedy chcę mieć jaką temperaturę). Cykl pieca na bieżący dzień mogę sobie ustawić z klawiatury wybierając po prostu właściwą pozycję z menu: Oczywiście mogę sobie ustawić dowolne cykle na przyszłość - do tego służy kalendarz. Wszystkie święta (stałe i ruchome) są uwzględniane automatycznie, jak na zdjęciu poniżej: Ważną funkcją jest również możliwość chwilowego włączenia/wyłączenia pieca. W tym trybie mogę ustawić na stałe stan zapalony/zgaszony. Po upływie 5 minut, jeśli nie wcisnąłem żadnego klawisza, urządzenie powraca do trybu automatycznego. Algorytm ustalania temperatury jest stosunkowo prosty, ale wymagał wprowadzenia empirycznych danych, uzyskanych na podstawie obserwacji z poprzedniego sezonu grzewczego. Po prostu: Jeśli temperatura jest niższa niż zadana minus histereza piec jest włączony (rozpoczynamy grzanie) Jeśli piec pracuje a temperatura nie przekracza wartości zadanej plus histereza, piec jest włączony (kontynuujemy grzanie) Jeśli z obliczeń wypada, że trzeba rozpocząć grzanie aby o określonej porze osiągnąć odpowiednią temperaturę piec jest włączony (rozpoczynamy grzanie). W przeciwnym razie piec jest wyłączony. Dochodzą tu dodatkowe czynniki - np. temperatura na zewnątrz wpływająca na czas konieczny do ogrzania mieszkania. Również mimo, iż program uruchamiany jest z crona co minutę, jeśli od ostatniego przełączenia pieca minęło mniej niż pięć minut piec nie będzie przełączany. Wszystkie parametry niedostępne z klawiatury mogę sobie ustawić przeglądarką w sieci domowej. Przykładowo ustawianie programu grzania na konkretny dzień wygląda tak: Oprócz kontroli temperatury centralka ma jeszcze kilka przydatnych funkcji. Wbudowany syntezator mowy (kedrigern) może oznajmić godzinę, temperaturę na zewnątrz czy wygłosić prognozę pogody na dwa dni. Czujnik odległości odpowiada za włączenie wyświetlacza tylko wtedy, gdy ktoś stoi przed centralką. Ponieważ część punktów oświetleniowych w mieszkaniu wyposażona jest w moduły ESP8266, może również zapalać/gasić światło (np. automatycznie zapalić lampkę w sypialni o właściwej porze). Zastosowałem tu czujnik ToF - może się to wydawać przesadą, ale cała centralka wisi w przejściu, i żaden czujnik odbiciowy nie zapewnił mi prawidłowego wykrycia (tzn. nie reagowania na ścianę naprzeciw przy jednoczesnym wykryciu czegoś w pewnej minimalnej odległości). Ale trochę więcej o tych modułach. Taki najprostszy moduł to sterowanie oświetleniem w tzw. "saloniku". Mam tu dwie linie oświetlenia górnego i boczny kinkiet. Z przyczyn technicznych moduły są dwa: jeden po stronie wersalki z przekaźnikiem sterującym kinkietem, drugi w miejscu oryginalnego wyłącznika oświetlenia. Ponieważ moduły komunikują się ze sobą (bez pośrednictwa centralki), każdy z nich może sterować dowolną z trzech linii. Każdy moduł posiada zasilacz, jeden lub dwa przekaźniki (użyłem tu subminiaturowych z dopuszczalnym prądem obciążenia 0.5A, mam nadzieję że przy mocy LED max. 14 W/linię i połączonych równolegle stykach to wystarczy) sterowanych przez BC-817 (takie miałem w szufladzie) oraz oczywiście ESP8266. Użyłem tu "gołych" ESP-12E. Trochę problemów miałem z wyłącznikami. Ponieważ są to przyciski chwilowe - aż chciałoby się użyć przycisków sterujących do rolet. Niestety - nigdzie nie znalazłem takich które mi odpowiadają (na trzy rolety) więc zrobiłem je sam ze zwykłych tact-switchy. Chyba nawet lepiej wyszło: wyłącznik ma grubość ok. centymetra - czyli mniej więcej tyle, ile wystaje ze ściany zwykły wyłącznik podtynkowy. A oto efekt: Jak widać, do wyłącznika doprowadzone są tylko dwa przewody - jest to po prostu typowa klawiatura rezystancyjna. Odpowiednio dobierając rezystory mógłbym teoretycznie osiągnąć niezależny odczyt wszystkich trzech kanałów, ale musiałbym za dużo kombinować. Pominąłem więc niektóre kombinacje (np. wciśnięcie wszystkich trzech) i udało mi się zrobić to na zwykłych rezystorkach (tych z szuflady). Wyłącznik ma następujące funkcje: pojedyncze wciśnięcie klawisza - zapalenie (na górze) lub zgaszenie (na dole) linii; jeśli zapalane jest oświetlenie sufitowe, automatycznie gaszony jest kinkiet na ścianie; podwójne wciśnięcie klawisza na górze - zapalenie linii i wygaszenie pozostałych; podwójne wciśnięcie dowolnego klawisza na dole - zgaszenie wszystkich linii; dłuższe wciśnięcie niebieskiego klawisza na górze - przełączenie lampki stojącej w sypialni (syntezator anonsuje stan zapalona/zgaszona); dłuższe wciśnięcie dowolnego klawisza na dole - zaanonsowanie godziny i temperatury na zewnątrz przez syntezator; dłuższe wciśnięcie lewego i prawego klawisza na dole - odczytanie pogody (przed godziną 16:00 na dziś i jutro, po 16:00 na jutro i pojutrze). Moduł oświetlenia przedpokoju ma trochę inne funkcje. Przede wszystkim wyposażony jest w czujkę PIR, automatycznie zapalając światło kiedy ktoś się w przedpokoju pojawia. Za pomocą wyłącznika można również ręcznie zapalić czy zgasić światło (przydaje się, bo czujka nie widzi całego pomieszczenia, niestety nie mogłem się pozbyć martwej strefy przy szafie) lub nawet całkowicie wyłaczyć czujkę. Sama obudowa do czujki jest chyba jedynym "uniwersalnym" rozwiązaniem, tak więc przy okazji zamieszczam pliki STL i SCAD: czujka.zip. I to chyba by było na tyle - nie chcę się rozpisywać, bo o tym można książkę napisać. W razie jakichś pytań jestem do dyspozycji.
-
Przedstawiam mój projekt: osiatkowany regał na kwiaty z programowalnym oświetleniem. Hardware Regał jest osiatkowany właśnie z powodu kotów, bo w środku są rośliny dla nich toksyczne. Osiatkowane są boki oraz przód - drzwi, natomiast tył nie ma siatki, bo regał jest wpasowany idealnie we wnękę okienną. Główna konstrukcja regału jest z desek o przekroju 2x5 cm, półki oraz drzwi z desek o grubości 1cm. Siatka stalowa przybita gwoździami. Drzwi regału po zamknięciu trzymają się na magnesy, można dodatkowo zabezpieczyć przed otwarciem przekręcając kółka wkręcone pomiędzy skrzydła drzwi. Warto to robić, bo kot wspinający się po siatce (a robią to, owszem) mógłby pewnie otworzyć drzwi, nawet niecelowo. Hardware raz jeszcze Każda półka oświetlona jest taśmą LED Philips (2500lm na metr, światło zimne, nie pamiętam dokładnej temperatury, ale generalnie rośliny potrzebują zimnego i mocnego), zamontowaną w aluminiowej rynience zapewniającej odprowadzanie ciepła. Zasilane zasilaczem PHILIPS CertaDrive 100W, i diody zużywają większość z tych 100W. Równolegle z zasilaczem do LEDów włączony jest mały zasilacz 5V dla Raspberry Pi Zero W odpowiedzialnego za sterowanie oświetleniem. Pi steruje oświetleniem przez wygodny w użyciu moduł przekaźników. Akurat miałem dwukanałowy moduł, więc chciałem sterować osobno górną i dolną połową regału, ale okazało się, że brakuje mi pół metra przewodu... więc jest inaczej: jeden kanał to dolna i górna półka razem, drugi to dwie środkowe. Teoretycznie można ustawić na jednych półkach rośliny bardziej światłolubne, a na drugich mniej. Zasilacze oraz Raspberry Pi przymocowane są do nogi regału z tyłu, więc nie widać ich bez specjalnego zaglądania, i dobrze, bo wyglądają nieco bałaganiarsko. Do szyby przyklejony jest czujnik światła skalibrowany tak, że wykrywa tylko bezpośrednie słońce padające na okno. Dodatkowo wyprowadzony jest mały panel sterowania w postaci DIP switcha, którym można wymusić stan włączony albo wyłączony na każdym z kanałów, a także wyłączyć czujnik słońca. Software Raspberry Pi zaprogramowany jest w języku Ruby. Program jest naprawdę prosty, czyta plik konfiguracyjny gdzie zapisane są godziny włączenia i wyłączenia obu kanałów, sprawdza stan panelu sterowania oraz czujnika światła, i steruje odpowiednio oświetleniem. Żeby zmienić godziny pracy oświetlenia trzeba się zalogować przez SSH i zmienić plik konfiguracyjny. Robię to parę razy w roku, żeby uniknąć efektu witryny sklepu ogrodniczego, jak na zdjęciu poniżej, który powstaje gdy oświetlenie jest włączone po zachodzie słońca. Oczywiście mógłbym zamiast tego sprawdzać godzinę zachodu słońca w jakimś serwisie, albo obliczać ją, albo mieć drugi światłomierz do wykrywania zmierzchu, ale aktualne prowizoryczne rozwiązanie działa dobrze już od ponad roku, więc chyba tak już zostanie...
-
Witam! Zgarnąłem za niewielkie pieniądze 2 zestawy CC2530(B) Eval Kit5 waveshare. Bawiłem się tym, żeby nawiązać w jakikolwiek sposób komunikację między urządzeniami poprzez program producenta. Jednak nic nie działało. Po wielu godzinach udało mi się rozpracować, jak się zmienia oprogramowanie modułu za pomocą Raspberry Pi, aby urządzenie było koordynatorem (nie mam pojęcia czy dobry plik wgrałem, było wiele prób). Ciężko cokolwiek znaleźć, aby móc sobie poradzić w miarę sensowny sposób. Czy ktoś by poratował jakimiś wskazówkami i namiarem na rzetelną literaturę? Może ktoś miał kontakt z tymi modułami i podzieliłby się widzą? Finalnie łączność ma odbywać się pomiędzy dwoma Raspberry Pi (najlepiej z wykorzystaniem języka PYTHON) za pośrednictwem owych modułów CC2530(B) podpiętych do magistral UART. Z góry przepraszam, ale jestem zielony trochę w tym temacie, muszę ogarnąć temat komunikacji jak najszybciej. Bardzo dziękuję za wszelką pomoc!
- 2 odpowiedzi
-
- CC2530
- Raspberry Pi
-
(i 2 więcej)
Tagi:
-
Witam wszystkich, bez zbędnych szczegółów mam do zrobienia układ który będzie musiał: Obsługa Xbee za pomocą magistrali UART Obsługa przetwornika A/C C/A (wstępnie myślałem nad układem pod magistralę I2C) Obsługa wyświetlacza OLED (również myślałem nad magistralą I2C ze względu na to że posiadam już takowy) Obsługa wyświetlacza LCD (chyba po magistrali SPI ok 3 cali) podgląd z kamery poprzez WiFi Obsługa WiFi jak w punkcie powyżej. Posiadać USB do kontrolera Ze względu na studencki budżet udało mi się kupić w bardzo dobrych pieniądzach (jak na obecną sytuację z "malinami") a są to Raspberry Pi zero wersje 1.3 oraz 1.1. Obawiam się, że jedna malinka nie podoła. Myślałem nad połączeniem dwóch za pomocą magistrali I2C ale wyczytałem, że Rasberry Pi może pracować tylko jako urządzenie typu Master i teraz nie wiem czy jedna dałaby rade podołać wszystkiemu czy dałoby radę jednak jakoś mądrze połączyć je i przesyłać proste informacje (może nawet kilka bitów) czy po prostu szukać czegoś mocniejszego aby obsługiwało wszystko. Miałby ktoś jakiś pomysł jak można by było ogarnąć temat z wykorzystaniem tego co mam, bądź co dałoby radę obsłużyć wszystkie potrzebne mi funkcje bez płacenia milionów monet? Zależy mi aby układ był stosunkowo niewielki i energooszczędny. Z góry dziękuję za wszystkie sugestie i pomysły oraz proszę o wyrozumiałość nie jestem aż tak biegły w temacie jak niektórzy doświadczeni użytkownicy i pasjonaci. Pozdrawiam!
-
Mam problem z VNC a natomiast od VNC dostaje komunikat "Cannot currlently show the desktop"
- 8 odpowiedzi
-
- vnc
- Raspberry Pi
-
(i 1 więcej)
Tagi:
-
Instalacja antenowa u młodego i początkującego krótkofalowca. Budowana od zera . Jest to worklog co może potrwać kilka lat. [na dzień pisania postu [25,12,2021] trwa to małymi krokami 1.5 roku gdyż nie kupuje elementów na raz [uwaga w tekście będę przemycał terminologie slangu na początku tekstu z wyjaśnieniem a potem zastępczo ] [i uproszczenia w terminologii i zasad działania ale jestem otwarty na pytania np jakiego narzędzia np praskę lub typ złącza użyłem czy innego elementu montażowego np. wspornik kominowy ] Po zgromadzeniu elementów trochę trwa. Ich obrobienie i zamontowanie . Każdy krótkofalowiec w pewnym monecie chce zamontować w swoim domu . [w swoim QTH domowym] Jakieś anteny i tu jest to mocno uwarunkowane gzie się mieszka. jak ktoś mieszka w bloku to pół biedy Jak na ostatnim piętrze i ma dostęp do dachu. To spoko ale np. w środku 11 piętrowego budynku. To mu zostaje rozciągnięcie anteny z balkonu lub parapetu do latarni lub drzewa . Zawsze taka osoba ma jeden problem . Znaleźć gzie można uziemić anteny. (temat naszej konstrukcji ) Tu czasami przychodzą sztuczki z instalacją ogrzewania lub wodną. Ja całe szczęście mieszkam w domku wolno stojącym. Więc tu mogę sobie wbijać różnorakie żelastwo w ziemie . U siebie mam na razie 2 anteny i w postępie tego workloga będzie ich więcej . Na dachu mam tak zwane X-30N Na tak zwany VHF i UHF. Czyli na częstotliwości około 145MHz i 440 MHz (w uproszczeniu ). Gzie to odpowiada długości fali radiowej 2Mmetrów i 70 CentyMetrów . po której zamontowaniu wyszły problemy z niską jakością instalacji telewizji naziemnej która miała 20 lat i była robiona na kolanie która została wymieniona ciekawostka przewód ,,antenowy '' bardzoo niskiej jakości dodawany w zestawie z anteną drugą z anten jest tak zwane LW long wire jak sama nazwa mówi to kawał drutu na fale krótkie od 3,5MHz 80Metrów do około 50MHz czyli 6Metrów (nie mam zdjęcia trudno jest ją na nim zmieścić tak żeby była widoczna antena żeby dobrze działała potrzebuje tak zwane przewody przeciw wagi i uziemienie najpierw zostało wstępnie wbite uziemienie a potem dodany element kompozytowy co się nazywa studzienka inspekcyjną/ uziomowa uziemienie będzie miało dobite kilka elementów dla poprawy parametrów i będzie nie wystawało no nad studzienkę więc gdy anteny nie będzie będzie można ją zamknąć pokrywką i np skosić trawę to już mamy wstępny zarys prac na 2 systemach anten ale co jak nam tych anten przybędzie 2 musimy zadbać o np ochronę odgromową instalacji więc jak mamy dużo anten lub przewodów pod anteny to musimy je gdzieś umieścić w jednym miejscu i opisać tu taki przykład jest ta listwa aluminiowa co na razie mi do tego służy taki odpowiednik pacz panelu w serwerowni ale docelowo do radio szaku (czyli biurka / pokoju gzie się bawisz radiami ) będą przychodziły przewody tylko te najbardziej potrzebne lub ewentualnie będzie jakiś przełącznik automatyczny na zewnątrz budynku więc tu się pojawia skrzynka na to wszystko na pacz panel na jakąś elektronikę skrzynka jest (bo już ją mam ) metalowa i będzie miała zrobione własne uziemienie skrzynka jest z użytku wtórnego posiada da niechlujną konserwacje lakierniczą kilka dławnic które nam się przydadzą posiada też buzzer / syrenę 24V kilka diod 24 v może nam się przydadzą posiada też wiele zbędnych otworów po osprzęcie ale trochę starego osprzętu przed malowaniem trzeba z niej wymontować więc do dzieła pomalujmy ją w między czasie omówmy sobie kolejne elementy co się pojawią w skrzynce jest w nich w pewnym sensie ochronnik antenowy głównej mierze jest to samo co warystor tak samo jak ochronnik w instalacji domowej 230v ma zabezpieczyć przed wystąpieniem za dużego napięcia na instalacji antenowej w tym przypadku uderzenia pioruna i po przez zwarcie żyły ,,ciepłej '' w przewodzie koncentrycznym z ekranem a ekran podłączony jest w tym przypadku z naszym przypadku z naszym pacz panelem a on jak wiemy z ziemią w skrzynce posiada on on wymienne nazwijmy to bezpieczniki ciąg dalszy nastąpi do usłyszenia w między czasie trwa dalsze malowanie i gromadzenie elementów i przygotowywanie pewnej kombinacji opartej o raspberry pi z RTL-SDR
- 15 odpowiedzi
-
- 1
-
-
- Elektronika
- elektryka
- (i 3 więcej)
-
ZeroTier - Bezpieczna sieć prywatna dla urządzeń IoT
Marcin648 opublikował temat w Artykuły użytkowników
Wstęp Jesteś na wakacjach albo posiadasz dwa mieszkania i bardzo chcesz podejrzeć wykresy z grafany, sprawdzić temperaturę w mieszkaniu czy zdalnie sterować światłem przez domoticz. Napotykasz pewien problem. Twoje urządzenia działają w twojej sieci domowej, nie masz możliwości bezpośredniego połączenia się z internetu. Możemy rozwiązać ten problem, odpowiednio konfigurując swoje urządzenia sieciowe, jednak to rozwiązanie ma wiele wad. Wystawienie swoich urządzeń IoT bezpośrednio do internetu może narazić je na ataki internetowe. W dzisiejszych czasach nie jest to niczym dziwnym. Przekonał się o tym chyba każdy kto udostępnił usługę SSH, a już po paru minutach mógł zobaczyć setki prób logowania z egzotycznych adresów IP w swoim /var/log/auth.log. Istnieje wiele metod zabezpieczeń tak wystawionych usług od stosowania trudnych haseł po wysyłanie serii pakietów na odpowiednie kombinacje portów sieciowych w celu autoryzacji (Port knocking) Wirtualna sieć prywatna Otwieranie portów do internetu wydaje się złym pomysłem i tutaj z pomocą przychodzą nam wirtualne sieci prywatne (VPN). Możliwe, że kojarzysz VPN z usługami anonimizujących twoje działania w internecie. Jest to jedna z korzyści, które może posiadać sieć VPN, ale to nie na tej funkcji skupimy się w tym artykule. Ten artykuł bierze udział w naszym konkursie! 🔥 Na zwycięzców czekają karty podarunkowe Allegro, m.in.: 2000 zł, 1000 zł i 500 zł. Potrafisz napisać podobny poradnik? Opublikuj go na forum i zgłoś się do konkursu! Czekamy na ciekawe teksty związane z elektroniką i programowaniem. Sprawdź szczegóły » Sieci VPN umożliwiają połączenie naszych urządzeń w jedną sieć, niezależnie od tego, gdzie aktualnie znajduje się dane urządzenie. Byle by miało połączenie do głównego serwera. Rozwiązanie to doskonale sprawdza się od wielu lat w sieciach firmowych, gdzie pracownicy są w stanie bezpiecznie podłączyć się do firmowego serwera bezpośrednio ze swojego domu. Rozwiązanie to jednak jest trudne w konfiguracji i wymaga serwera centralnego z dobrym połączeniem sieciowym, który musi być dostępny z internetu. Więc czy istnieje darmowe rozwiązanie, które będzie zawierać wszystkie zalety sieci VPN, nie będzie wymagać dodatkowego serwera oraz jednocześnie będzie omijać wszystkie przeszkody w postaci braku publicznego adresu ip? Istnieje! VPN peer to peer. Tak p2p, technologia znana zapewne użytkownikom protokołu BitTorrent jednak tutaj wykorzystujemy go do zestawiania połączeń sieciowych. ZeroTier Jednym z tego typu usług jest otwartoźródłowa aplikacja na licencji BSL o nazwie ZeroTier. Umożliwia ona tworzenie zdecentralizowanych wirtualnych sieci, czyli takich, które nie wykorzystują żadnego serwera centralnego. Dzięki zastosowaniu szyfrowanego połączenia bezpośrednio między naszymi urządzeniami, omijamy potrzebę wymiany naszych pakietów przez serwer pośredniczący, dzięki czemu ten nie ogranicza nas swoją przepustowością sieci. Połączenie takie nie tylko jest bezpieczne, ale też zapewnia niskie opóźnienia, co może mieć kluczowe znaczenie, jeśli chcemy wykorzystać to rozwiązanie do zastosowań rozrywkowych jak granie w gry czy udostępnianie pulpitu zdalnego. Rejestracja Na początku musimy zarejestrować się w usłudze ZeroTier. Przechodzimy na adres Centrali ZeroTier oraz wypełniamy formularz kryjący się pod przyciskiem Register. Alternatywą jest zalogowanie się, korzystając z kont usług Google, Github lub Microsoft. Tworzenie nowej sieci Po zalogowaniu się do Centrali ZeroTier w zakładce Network klikamy na przycisk z napisem Create A Network. Natychmiastowo na liście sieci pojawi się nam nasza nowo utworzona sieć. Wyróżnić możemy Identyfikator składający się z liter i cyfr, losowa nazwa, aktualna podsieć i ilość urządzeń online i offline. Przechodzimy do konfiguracji klikając w ID naszej sieci na liście. W zakładce Settings -> Basic możemy ustawić nazwę (Name) oraz opis (Description) dla naszej sieci. Jest to zabieg mający na celu łatwiejszą identyfikację na liście sieci, jeśli byśmy posiadali ich więcej. Ustawienie Access Control ustawiamy na PRIVATE, dzięki czemu każdy nowy klient w naszej sieci będzie musiał zostać zaakceptowany ręcznie. Zapobiegnie to niechcianym intruzom. Zakładka Advanced zawiera ustawienia dotyczące adresacji naszej sieci. Pewnie zaawansowani administratorzy poczują się tutaj jak ryba w wodzie. Opcji jest wiele, możemy zarządzać wirtualnym routerem, dowolnie przypisywać prywatne adresy ip lub zdecydować się na statyczne adresowanie. Dla mniej zaawansowanym użytkowników zalecam tutaj ustawić tylko zakres adresów IPv4 Auto-Assign na taki, który nie koliduje z naszymi lokalnymi sieciami domowymi. Na potrzeby tego artykułu wybrałem 10.144.*.* Zakładka Members zawiera listę aktualnie podłączonych użytkowników. Lista na razie jest pusta. Wszystkie zmiany są zapisywane na bieżąco. Dodawanie urządzeń do sieci Na potrzeby tego artykułu zestawimy sieć prywatną między dwoma urządzeniami. Raspberry Pi 2 Komputer z systemem Windows 10 ZeroTier można również z powodzeniem stosować na urządzeniach mobilnych z systemem Android lub iOS. Wszystkie możliwe opcje znajdziemy na stronie producenta. Linux (Raspberry Pi) Instalacja ZeroTier na urządzeniach z systemem Linux jest bardzo prosta. Ogranicza się ona do wykonania jednej komendy. Procedura może się różnić w zależności od dystrybucji systemu. Na początku musimy się zalogować do swojego urządzenia i uruchomić terminal. Warto upewnić się, że nas system jest aktualny. W tym celu wpisujemy w konsoli: sudo apt update; sudo apt full-upgrade; sudo apt autoremove; Następnie upewniamy się, że posiadamy w systemie narzędzie Curl poleceniem sudo apt install curl ZeroTier instalujemy poleceniem curl -s https://install.zerotier.com | sudo bash Po poprawnie wykonanej instalacji zobaczymy komunikat *** Success! You are ZeroTier address [ f2a983da64 ]. Interakcja z aplikacją odbywa się poprzez polecenie zerotier-cli Aby uzyskać informacje o dostępnych opcjach wpisujemy zerotier-cli --help Dołączamy do naszej sieci poprzez polecenie join oraz ID naszej sieci. sudo zerotier-cli join 565799d8f6699d5b Windows W systemie operacyjnym Windows w celu instalacji udajemy się na stronę producenta. Pobieramy instalator, klikając w ikonkę naszego systemu. Instalator następnie uruchamiamy i przeprowadzamy instalację. W czasie instalacji możemy zostać zapytani o dodanie wyjątku do zapory sieciowej, zgadamy się. Po instalacji program jest dostępny na pasku zadań, interakcje z nim przeprowadzamy poprzez przyciśnięcie prawego klawisza myszy na jego ikonie. Aby dołączyć do naszej sieci wybieramy opcję Join Network… Następnie podajemy ID naszej sieci z Centrali ZeroTier, w moim przypadku jest to 565799d8f6699d5b. Zatwierdzamy przyciskiem Join. Autoryzacja Ostatnim krokiem jest autoryzacja nowo dodanych urządzeń w panelu zarządzania naszą siecią. W tym celu w zakładce Members zaznaczamy pozycję Auth? Dla tych urządzeń, którym chcemy zezwolić na dostęp do sieci. Odczytujemy również jaki adres IP został przyznany z puli. Po tej czynności pozostało nam już tylko przetestować połączenie. W tym celu wykonujemy polecenie ping w konsoli systemu Windows z docelowym IP naszego Raspberry Pi w sieci zerotier ping 10.144.214.244 W systemach linux sprawdzamy, czy jest widoczna wirtualna karta sieciowa za pomocą polecenia sudo ifconfig Jeśli karta nie jest widoczna, spróbuj zresetować urządzenie. Podsumowanie Tym oto sposobem stworzyliśmy darmową bezpieczną sieć wirtualną dla naszych urządzeń IoT. Instalując aplikację na telefonie, jesteś w stanie kontrolować swoje urządzenia domowe, tak samo jakbyś był podłączony do swojej lokalnej sieci wifi. A jeśli będziesz kiedyś chciał pograć z kolegą “po lanie” to masz już alternatywę dla Hamachi. 😉- 16 odpowiedzi
-
- 12
-
-
- Sieci
- Raspberry Pi
- (i 3 więcej)
-
Witam, szukam dodatku/programu pozwalającego na ustawienie okienek w visual studio tak jak to wygląda w windows forms ale działającego na linuxe. W skrócie program ma komunikować sie przez modbus z arduino a te dane chce chce wyświetlić na monitorze i mieć klikalne przyciski i tym podobne. Ewentualnie jakieś inne rozwiązanie okienkowe chociaż najlepszy był by program klikalny najlepiej darmowy
- 2 odpowiedzi
-
- Raspberry Pi
- linux
- (i 3 więcej)
-
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]
-
Wstęp Od poprzednich właścicieli mieszkania dowiedziałem się, że w piwnicy zdarzają się kradzieże. W związku z tym postanowiłem zbudować fotopułapkę na złodzieja: urządzenie, które po wykryciu ruchu robi zdjęcia i wysyła je na serwer. Serwer natomiast powiadamia mnie mailowo (z załączonymi zdjęciami) o wykryciu ruchu. Finał tej historii był taki, że... nie zdążyłem. Jakiś miesiąc przed ukończeniem projektu było seryjne włamanie, którego także padłem ofiarą 😕 Wracając do meritum: Hardware - ESP32-CAM - Arduino Pro Mini (klon) - moduł czujnika ruchu PIR HC-SR501 - moduł czytnika RFID MRFC522 - akumulator żelowy 6 V 4,5 Ah - obudowa Kradex Z2AW - przetwornica mini step-down 360 - Raspberry Pi 4 (serwer) Zasilanie W piwnicy nie ma gniazdka, więc założyłem, że zasilanie będzie akumulatorowe. Nie chciałem kombinować z wpinaniem się w oprawę oświetleniową i ryzykować oskarżenie o kradzież prądu. Gdybym nawet miał dostęp do prądu, to i tak dobrze byłoby zastosować akumulator buforowy. No może uprościłoby to projekt o tyle, że ESP, które jest nadrzędnym modułem urządzenia, mogłoby pracować w sposób ciągły, bez usypiania. Napięcie akumulatora obniżane jest do 3,3 V przez małą, tanią, chińską przetworniczkę step-down i... w zasadzie tyle. Komunikacja Miałem to wielkie szczęście, że w piwnicy łapie mi zasięg domowego WiFi. Dzięki temu odpadło kombinowanie z LoRa czy GSM. ESP "gada" z serwerem z wykorzystaniem socketów TCP. Schemat działania ESP jest w trybie deepsleep z którego budzi się co 2 godziny w celu zaraportowania napięcia akumulatora, albo od przerwania wywołanego sygnałem z czujnika ruchu. Jeśli wybudzenie wywołał czujnik ruchu, ESP w pętli robi zdjęcia i wysyła je do serwera w mieszkaniu. Wszelki zapis na karcie SD odpadał już w założeniach, bo złodziej może urządzenie zniszczyć albo po prostu zabrać ze sobą. Zdjęcia przesłane na serwer są już bezpieczne. Wyjście z pętli następuje w dwóch przypadkach: gdy czujnik PIR przestanie wykrywać ruch, albo gdy obecność zostanie zautentyfikowana przez przyłożenie tagu RFID do czytnika. Serwer ma następujące zadania: stanowi sprzętowy watchdog układu - kontroluje, czy ESP skomunikuje się przynajmniej raz na 4 godziny oraz oczywiście wysyła powiadomienia mailowe o detekcji. Powiadomienia mailowe są dwa - pierwsze zawsze po wykonaniu dwóch zdjęć (chyba że wcześniej nastąpi autentykacja), kolejne to już transfer wszystkich zdjęć jakie urządzenie zdążyło wykonać i przesłać do wyjścia z pętli. Jeśli uwierzytelnię się przez zeskanowanie breloka RFID, to drugi mail ze zdjęciami nie jest wysyłany - obrazki usuwane są z serwera. Jaką rolę ma w tym Arduino, skoro wszystko ogarnia ESP? Prostą, zabrakło mi pinów w ESP, żeby podłączyć wszystkie peryferia 🙂 czytnik RFID komunikuje się po SPI, więc zabiera ich sporo. Arduino jest interfejsem, który odczytuje i przechowuje UID odczytanego tagu, a na żądanie (otrzymane z ESP przez Serial), podaje tę wartość. Dodatkowo mierzy napięcie baterii (również w odpowiedzi na komendę Serial) oraz daje akustyczny sygnał buzzerem, gdy odczyta tag. Nie bawiłem się nawet w usypianie Arduino. Jest ono podłączone do masy przez tranzystor i ESP załącza je sobie przy starcie i wyłącza gdy nie jest już potrzebne. Ciekawym problemem, który pojawił się pod koniec realizacji projektu było fałszywie pozytywne wzbudzanie czujnika ruchu, gdy ESP wybudzało się "z timera". W Internecie były dziesiątki wątków sygnalizujących ten problem z modułem HC-SR501. Dwa największe tropy to było zasilanie (spory pobór prądu przy starcie ESP, gdy łączy się z WiFi) oraz sam sygnał WiFi. To drugie było łatwiejsze do zdebugowania i okazało się od razu celnym strzałem. Pomogło stworzenie ekranu z folii aluminiowej wokół tylnej części modułu PIR. W montażu wspomagałem się drobnymi elementami, które sam projektowałem i drukowałem w 3D - panel przedni, tylna pokrywa PIR, uchwyt montażowy baterii, ramka montażowa czytnika RFID. Żeby było trochę druciarstwa to w niektórych miejscach wspomógł mnie hot-glue i... sznurek. Oprogramowanie ESP32-CAM - Micropython Raspberry Pi (serwer) - Python Arduino - C++ (Arduino) Jeśli ktoś jest zainteresowany, mogę upublicznić repozytorium na Githubie. Jeszcze tego nie zrobiłem, bo musiałbym wyczyścić kod z danych osobistych, a najlepiej przenieść te zmienne do oddzielnego pliku. Zdjęcia front wnętrze bonus - jednooki Project Manager Wydawało mi się że mam gdzieś zdjęcie z zamkniętą obudową, ale nie znalazłem. W sumie wygląda to prawie jak oryginalna obudowa, bo oprócz wystających z panelu frontowego obiektywu kamery i czujnika ruchu, na zewnątrz nie ma żadnych elementów. fot. Kradex
- 4 odpowiedzi
-
- 7
-
-
- fotopułapka
- alarm
- (i 3 więcej)
-
Dla zielonych 😉 (BASIC CRASH COURSE) O czym w tym artykule? W tym artykule poznasz podstawy pracy z Raspberry Pi Pico w języku C++. Obsłużysz podstawowe narzędzia - PWM, delay, piny, UART. Ten poradnik jest skierowany dla osób, które są kompletnie zielone i chcą postawić swoje pierwsze kroki z RP2040. Na wstępie Uwaga: jeżeli korzystasz z Windowsa zaparz sobie szklankę melisy, gdyż instalacja środowiska na Windowsie to horror 😉 Ten artykuł bierze udział w naszym konkursie! 🔥 Na zwycięzców czekają karty podarunkowe Allegro, m.in.: 2000 zł, 1000 zł i 500 zł. Potrafisz napisać podobny poradnik? Opublikuj go na forum i zgłoś się do konkursu! Czekamy na ciekawe teksty związane z elektroniką i programowaniem. Sprawdź szczegóły » Instalacja środowiska - Linux sudo apt update sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential // To powinieneś mieć sudo apt install gcc g++ // A to dla fanatyków Debiana :) sudo apt install libstdc++-arm-none-eabi-newlib Tutaj odeślę do oficjalnej dokumentacji 🙂 Instalacja środowiska - Windows Alternatywa: skrypt instalujący Pi Pico Toolchain na Windowsie 😉 (nie testowałem) README dla tego programu. Dobrze no to czas na tę tragedię - przede wszystkim zaparz sobie melisę... To jest bardzo ważne 🙂 Wybierz środowisko programistyczne Visual Studio Code Clion Zainstaluj CMake Pamiętaj by podczas instalacji zaznaczyć opcję "Add the CMake to system PATH for all users" Pobierz i zainstaluj Build Tools for Visual Studio, pamiętaj by zaznaczyć "C++ build tools" w sekcji "Workloads" Zainstaluj Python3, pamiętaj by zaznaczyć opcję by dodać go do PATH 😉 Zainstaluj Git - również pamiętając o dodaniu do PATH 😉 Zainstaluj kompilator ARM 😉 Tak samo należy pamiętać o dodaniu go do zmiennej PATH 😉 Zmienna PATH powinna wyglądać w sposób zbliżony: Myślałeś, że to koniec? 😄 Teraz czas pobrać i zainstalować SDK Pobieramy archiwum (zielony przycisk) Wypakowujemy do jakiegoś folderu na dysku Kopiujemy ścieżkę dysku Wchodzimy w "Mój komputer" > "Ten Komputer" > PPM > "Właściwości" > "Zaawansowane ustawienia systemu" > "Zmienne środowiskowe" Dodajemy nową zmienną o nazwie PICO_SDK_PATH i wartości skopiowanej ścieżki (np. "D:\SDK\Pi Pico") Dodajemy nową zmienną o nazwie PICO_TOOLCHAIN_PATH i ścieżce do ARM Toolchain (np. "C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2020-q4-major\bin") Do osobnego folderu pobieramy i wypakowujemy następujące narzędzie, znacząco ułatwi nam tworzenie projektów 😉 Wszystko powinno być już zainstalowane, jeżeli masz problemy odeślę Cię do tego poradnika, który opisuje wszystko krok po kroku dla Visual Studio 😉 Tworzenie projektu Tutaj nie będę poruszał tworzenia pliku Makefile, gdyż dla większości osób jest to strasznie skomplikowane, a ma być prosto nieprawdaż? Stąd pobraliśmy Pico Project Generator. Jest to bardzo wygodne narzędzie, dzięki któremu możemy tworzyć projekty w interfejsie graficznym. By uruchomić narzędzie uruchamiamy "Wiersz Poleceń" (cmd.exe) i przechodzimy do folderu, w którym znajduje się nasz Pico Project Generator (np. "D:\pico-project-generator-master"). Następnie używamy komendy: python pico_project.py --gui by uruchomić nasz program. Przykład: Używanie Pico Project Generator Pojawia nam się następujące okienko: W zakładce "Project Name" wpisujemy nazwę projektu, UWAGA: NAZWA PROJEKTU NIE MOŻE ZAWIERAĆ SPACJI W sekcji Location możemy wybrać folder nadrzędny dla naszego projektu (w nim znajdzie się folder z projektem), ja go wrzucę razem z generatorem. Oprócz tego możemy wybrać dodatkowe biblioteki, ustawienia konsoli czy ustawienia kodu. Jako, że ja będę korzystał z CLiona użyję standardowych ustawień i kliknę przycisk OK. Czekamy aż projekt się wygeneruje i zamykamy generator 😉 Teraz uruchamiamy środowisko... Jak widzimy nasz program wygenerował standardowy plik. Nie będę tutaj tłumaczył podstaw języka C, aczkolwiek powiem jak podmienić go na C++. W tym celu zmieniamy rozszerzenie pliku na .cpp Oraz musimy poprawić plik CMakeLists.txt - czasami nie jest on poprawiany automatycznie. Szukamy linijki "add_executable(Forbot Forbot.c)" i podmieniamy nazwę "Forbot.c" na "Forbot.cpp" - linijka powinna wyglądać tak: "add_executable(Forbot Forbot.cpp)" 😉 Uwaga: dla Visual Studio Code zalecane jest zainstalowanie pluginu CMake Tools. Dokumentacja Warto sobie dodać do zakładek dokumentację C++ dla Raspberry Pi Pico - jest tam wszystko dokładnie i elegancko opisane 😉 Warto sobie też zapisać gdzieś pinout, gdyż na górze płytki nie ma oznaczeń i jak ją wepniemy w płytkę stykową... będzie problem 😉 GPIO - Pierwszy program... No to co? Może napiszemy pierwszy program? W tym celu do GPIO16 podpinamy diodę LED z rezystorem ograniczającym prąd. Jak na załączonym obrazku 😉 Tak wiem, że rezystor nie jest najpiękniej zamontowany, ale kto by się przejmował... To teraz czas na miganie diodą. #include <stdio.h> #include "pico/stdlib.h" int main() { stdio_init_all(); // inicjacja STDIO gpio_init(16); // inicjacja pinu GP16 gpio_set_dir(16, true); // ustawienie pinu GP16 jako wyjścia // loop() while(true){ gpio_put(16, true); // ustaw stan wysoki na pinie GP16 sleep_ms(250); // Odczekaj 250ms gpio_put(16, false); // ustaw stan niski na pinie GP16 sleep_ms(250); // Odczekaj 250ms } return 0; } Jak widzimy kod w Pico nie ma loop() ani setup(), a ma wyłącznie main(). W tym celu sami musimy stworzyć sobie loop'a - po inicjacji I/O dodajemy niekończącą się pętlę while. W niej możemy wykonywać kod tak samo jak w loop() w Arduino, a wszystko powyżej będzie działało jak setup(). Funkcje gpio_init(<id>) - inicjuje pin, PIN MUSI BYĆ ZAINICJOWANY! gpio_set_dir(<id>, <wartość>) - ustawia kierunek pinu: false - wejście, true - wyjście gpio_pull_up(<id>) - podciągnięcie pinu do VCC gpio_pull_down(<id>) - podciągnięcie pinu do GND gpio_put(<id>) - ustawia stan pinu: false - niski, true - wysoki sleep_ms(<time>) - czeka określoną ilość milisekund sleep_us(<time>) - czeka określoną ilość mikrosekund 😉 gpio_set_function(<gpio>, <func>) - ustawia funkcję GPIO GPIO_FUNC_SPI GPIO_FUNC_UART GPIO_FUNC_I2C GPIO_FUNC_PWM GPIO_FUNC_SIO (default) - zwykły pin GPIO_FUNC_PIO0 GPIO_FUNC_PIO1 GPIO_FUNC_GPCK GPIO_FUNC_USB GPIO_FUNC_NULL - brak funkcji (wyłączone) GPIO_FUNC_XIP To są podstawowe funkcje, z których będziesz najczęściej korzystać. Wgrywanie programu. By wgrać program na Pi Pico musisz go skompilować. Jeżeli wygenerowałeś go przy użyciu podanego wcześniej narzędzia powinno to pójść bez większych problemów. Alternatywnie komenda do zbudowania programu wygląda następująco: cmake -DCMAKE_BUILD_TYPE=Debug -G "CodeBlocks - MinGW Makefiles" <ŚCIEŻKA> cmake --build <ŚCIEŻKA> --target all -- -j <ILOŚĆ RDZENI> // Pamiętaj by podmienić <ŚCIEŻKA> i <ILOŚĆ RDZENI> na odpowiednie parametry! // UWAGA: te komendy nie gwarantują poprawnego builda :( Niestety cmake to narzędzie, które jest toporne i zwykle wyrzuca miliard błędów zanim łaskawie coś skompiluje ;) // W przypadku powyższych komend musisz jeszcze ustawić kompilatory C/C++ i ASM ;) // Dlatego zalecam używanie CLion'a, który to wszystko automatyzuje // Alternatywa (często sypie błędami cmake) cd <ŚCIEŻKA>/build nmake Wreszcie gdy uda nam się to skompilować wgrywamy program. By to zrobić odłączamy Pi Pico od USB, przytrzymujemy przycisk BOOTSEL oraz podłączamy Pi Pico do USB (lub używamy przełącznika w HUBie) 😉 Następnie wchodzimy do folderu ze skompilowanymi plikami i szukamy pliku o nazwie <coś>.uf2 i ten plik przenosimy na dysk przenośny, który powstał z naszego Raspberry Pi Pico. Dysk powinien zniknąć, a nasza dioda powinna zacząć migać. UART Obsługa UART'a jest banalna. W Pi Pico mamy do dyspozycji dwa UART'y. uart0 i uart1. Przykład będzie korzystał ze standardowego uart0. #include <stdio.h> #include "pico/stdlib.h" int main() { stdio_init_all(); // Init GPIO gpio_init(0); gpio_init(1); // Ustaw funkcje GPIO jako UART gpio_set_function(0, GPIO_FUNC_UART); gpio_set_function(1, GPIO_FUNC_UART); // Zainicjuj uart0 z transmisją 115200 bps uart_init(uart0, 115200); // Jeżeli na uart0 są jakieś dane if(uart_is_readable(uart0)){ // tablica do odczytu pojedynczego bajtu uint8_t data[1]; // odczytaj bajt uart_read_blocking(uart0, data, 1); // wyślij bajt spowrotem na uart0 uart_write_blocking(uart0, data, 1); } return 0; } Funkcje uart_init(<uart>, <baud>) - inicjuje UART (Serial.begin(<baudrate>)) uart_set_baudrate(<uart>, <baud>) - zmiana baudrate UART'a w locie 😉 uart_is_writable(<uart>) - sprawdza czy bufor zapisu ma miejsce uart_is_readable(<uart>) - sprawdza czy w buforze odczytu są dane uart_write_blocking(<uart>, <data>, <length>) - wysyła bajty na UART, data to wskaźnik do tablicy z danymi uart_read_blocking(<uart>, <data>, <length>) - oczytuje bajty z UART, data to wskaźnik do tablicy z danymi uart_putc(<uart>, <char>) - wysyła znak na UART uart_puts(<uart>, <string>) - wysyła tekst na UART uart_getc(<uart>) - odczytuje znak z UART I2C (IIC) By zainicjować I2C musimy się chwilę natrudzić - po pierwsze musimy dodać bibliotekę hardware_i2c do pliku CMakeLists.txt. Szukamy linijki "target_link_libraries(<NAZWA_PROJEKTU> pico_stdlib)" i dodajemy pod nią "target_link_libraries(<NAZWA_PROJEKTU> hardware_i2c)". Teraz musimy wykonać całą procedurę inicjacji - zainicjować I2C, podłączyć pullupy oraz ustawić piny w tryb I2C. Przykład poniżej: // Inicjacja I2C i2c_init(i2c0, 1000*100); gpio_set_function(4, GPIO_FUNC_I2C); gpio_set_function(5, GPIO_FUNC_I2C); // I2C wymaga pull-upów na liniach ;) gpio_pull_up(4); gpio_pull_up(5); Jak widzimy moje I2C jest podłączone do pinów 4 i 5. Pin 4 to SDA, pin 5 to SCL. Baudrate to 100kbit. By odczytać dane używamy funkcji i2c_read_blocking, a by zapisać i2c_write_blocking. Widać podobieństwo do UARTa? #include <stdio.h> #include "pico/stdlib.h" #include "hardware/i2c.h" int main() { stdio_init_all(); printf("Witaj I2C!\n"); // Inicjacja I2C i2c_init(i2c0, 1000*100); gpio_set_function(4, GPIO_FUNC_I2C); gpio_set_function(5, GPIO_FUNC_I2C); // I2C wymaga pull-upów na liniach ;) gpio_pull_up(4); gpio_pull_up(5); uint8_t data[4]{'t', 'e', 's', 't'}; // Wyślij dane poprzez I2C i2c_write_blocking(i2c0, 0x01, data, 4, false); i2c_read_blocking(i2c0, 0x01, data, 4, false); } Przykładowy kod powyżej 😉 - wysyła słowo "test" na adres 0x01 po czym pobiera 4 bajty z adresu 0x01. Funkcje i2c_init (<i2c>, <baudrate>) - inicjuje I2C 😉 i2c_set_baudrate(<i2c>, <baudrate>) - zmiana baudrate w locie i2c_set_slave_mode(<i2c>, <status>, <adres>) - przełącza Pi Pico w tryb slave dla I2C - jeżeli status jest true to urządzenie jest slave, jeżeli false to jest masterem. Adres jest adresem urządzenia slave, na którym ma działać nasze Pico 😉 i2c_write_blocking(<i2c>, <adres>, <dane>, <długość>, <nostop>) - wysyła dane poprzez I2C - dane to wskaźnik do tablicy bajtów (uint8_t), jeżeli nostop jest true to po wysłaniu nie zostanie wysłany sygnał stop (z reguły false) i2c_read_blocking(<i2c>, <adres>, <tablica>, <długość>, <nostop>) - pobiera dane poprzez I2C - tablica to wskaźnik do miejsca, gdzie mają one zostać zapisane w pamięci (np. tablica uint8_t). Parametr nostop podobnie jak w górnym przypadku opisuje czy program ma pominąć sygnał STOP. i2c_get_write_available(<i2c>) - sprawdza ilość bajtów dostępnych do zapisu na danej magistrali I2C i2c_get_read_available(<i2c>) - sprawdza ilość bajtów dostępnych do odczytu na danej magistrali I2C. SPI Musimy na początku dołączyć bibliotekę hardware_spi. Po tym możemy stworzyć już swój program z obsługą SPI 😉 Przykład: #include <stdio.h> #include "pico/stdlib.h" #include "hardware/spi.h" int main() { stdio_init_all(); // Inicjacja SPI spi_init(spi0, 12000000); // 12 MHz spi_set_format(spi0, 8, SPI_CPOL_1, SPI_CPHA_1, SPI_MSB_FIRST); gpio_set_function(6, GPIO_FUNC_SPI); // SCK gpio_set_function(7, GPIO_FUNC_SPI); // TX gpio_set_function(8, GPIO_FUNC_SPI); // RX uint8_t write_data[4] = {'t', 'e', 's', 't'}; uint8_t read_data[4]; // Wyślij 4 bajty na SPI spi_write_blocking(spi0, write_data, 4); // Wyślij 4 razy 0 na SPI do odczytu 4 bajtów. spi_read_blocking(spi0, 0x0, read_data, 4); // Pełny duplex SPI ;) spi_write_read_blocking(spi0, write_data, read_data, 4); } Jak widzimy program inicjuje SPI z prędkością 12Mbit/s, po czym przesyła słowo 'test' na magistralę, odczytuje 4 bajty (przesyłając 4 razy 0x0 by odczytać treść) oraz dokonuje transmisji duplex - wysyła słowo 'test' oraz odczytuje w tym samym czasie 4 bajty. Funkcje spi_init(<spi>, <baudrate>) - inicjuje SPI spi_set_format(<spi>, <data_bits>, <cpol>, <cpha>, <order>) - ustawia tryb SPI - ilość bitów oraz parametry CPOL, CPHA i określa czy przesyłamy najpierw MSB czy LSB. spi_set_slave(<spi>, <aktywny>) - jeżeli parametr "aktywny" ustawimy na true to nasze Pi Pico stanie się "slave" SPI zamiast "masterem". spi_is_writable(<spi>) - sprawcza czy na SPI możemy zapisać dane (ilość miejsca w buforze) 😉 spi_is_readable(<spi>) - sprawdza ilość danych odczytanych przechowywanych w buforze SPI spi_read_blocking(<spi>, <bajt>, <dane>, <ilość>) - odczytuje dane z SPI jednocześnie przesyłając określoną ilość razy parametr "bajt" np. 0x0 (zgodnie z standardem SPI). Dane to wskaźnik do tablicy, gdzie dane mają zostać odczytane, a ilość to długość tej tablicy 🙂 spi_write_blocking(<spi>, <dane>, <ilość>) - przesyła dane na SPI - dane to wskaźnik do tablicy z danymi, ilość określa długość tej tablicy 🙂 spi_write_read_blocking(<spi>, <dane_zapis>, <dane_odczyt>, <ilość>) - połączenie read i write - transmisja duplex SPI. dane_zapis to wskaźnik do danych do wysłania na magistralę SPI, a dane_odczyt to wskaźnik do miejsca, gdzie dane mają zostać odczytane. Ilość określa ile danych ma być przesłane. PWM Pi Pico ma całkiem przyjazny w konfiguracji PWM (to jest półsarkazm). Jest on obsługiwany sprzętowo, więc potrzebujemy dodać bibliotekę do pliku CMakeLists.txt. Szukamy linijki "target_link_libraries(<NAZWA_PROJEKTU> pico_stdlib)" i dodajemy pod nią "target_link_libraries(<NAZWA_PROJEKTU> hardware_pwm)". Potem aktualizujemy plik CMake - w Clionie u góry pojawia się do tego ładny pasek z przyciskiem 😉 Klikamy reload changes i gotowe 😉 Teraz czas użyć PWM... #include <stdio.h> #include "pico/stdlib.h" #include "hardware/pwm.h" int main() { stdio_init_all(); gpio_init(0); // Init GPIO gpio_set_function(0, GPIO_FUNC_PWM); // Setup PWM // Pobierz dane PWM - slice i kanał uint slice_num = pwm_gpio_to_slice_num(0); uint channel = pwm_gpio_to_channel(0); // Ustaw długość trwania cyklu PWM na 65536 (granica licznika) pwm_set_wrap(slice_num, 65535); // Kanał PWM będzie miał stan wysoki tylko przez 32767 cykli licznika (ok 50%) pwm_set_chan_level(slice_num, channel, 32767); // Aktywuj PWM pwm_set_enabled(slice_num, true); return 0; } Efekt? Funkcje pwm_gpio_to_slice_num(<gpio>) - pobiera adres slice'a dla PWM pwm_gpio_to_channel(<gpio>) - pobiera adres kanału PWM pwm_set_wrap(<slice>, <cycles>) - ustala ilość cykli PWM dla danego kanału pwm_set_chan_level(<slice>, <channel>, <cycles>) - przez tyle cykli licznika PWM będzie miało stan wysoki pwm_set_enabled(<slice>, <stan>) - aktywuje PWM na danym slice 😉 ADC By odczytać ADC musimy załączyć odpowiednią bibliotekę - hardware_adc do pliku CMakeLists.txt. Szukamy linijki "target_link_libraries(<NAZWA_PROJEKTU> pico_stdlib)" i dodajemy pod nią "target_link_libraries(<NAZWA_PROJEKTU> hardware_adc)". Następnie musimy zainicjować adc, wybrać odpowiedni kanał oraz odczytać wartość. adc_init(); // zainicjuj ADC (wykonaj na początku main()) adc_gpio_init(gpio); // zainicjuj pin ADC adc_select_input(gpio - 26); // wybierz wejście ADC adc_read(); // Odczytaj wartość z ADC Piny ADC na Raspberry Pi Pico to: GP26, GP27, GP28 - odpowiednio wejście 0, 1, 2 Oprócz tego mamy również wejście 3 - wewnętrzny sensor temperatury, gdyby nasza płytka pracowała w mniej sprzyjających warunkach 😉 Funkcje adc_init() - inicjuje ADC, musi być wykonane przed odczytem adc_gpio_init(<gpio>) - inicjacja pinu GPIO adc_select_input(<id>) - wybiera wejście ADC (0 - GP26, 1 - GP27, 2 - GP28, 3 - sensor temperatury) adc_read() - odczytuje wartość z ADC (12-bit) Przerwania na GPIO GPIO obsługuje przerwania - podczas narastania, opadania zbocza oraz również przerwania "stanów" - wysokiego i niskiego. By dodać przerwanie na GPIO używamy funkcji gpio_set_irq_enabled_with_callback(<gpio>, <rodzaj przerwania>, true, <handler>); Oraz określamy handler przerwania np. void gpio_irq_handler(uint gpio, uint32_t events) { status = !status; gpio_put(15, status); } I wtedy nasza funkcja wygląda przykładowo: gpio_set_irq_enabled_with_callback(2, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, gpio_irq_handler); W tym przypadku przerwanie nastąpi podczas narastania lub opadania zbocza i wykona powyższą funkcję przerwania. Uwaga: na jeden rdzeń może być tylko jedno przerwanie SIO. Przykładowy program z przerwaniami: #include <stdio.h> #include "pico/stdlib.h" bool status = false; void gpio_irq_handler(uint gpio, uint32_t events) { status = !status; // Zmień status gpio_put(15, status); // Przełącz diodę } int main() { stdio_init_all(); // Zainicjuj pin z diodą gpio_init(15); gpio_set_dir(15, true); gpio_put(15, status); // Ustal przerwanie gpio_set_irq_enabled_with_callback(2, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, gpio_irq_handler); // Nieskończona pętla - WYMAGANA!!! while (1) { tight_loop_contents(); } } UWAGA: przerwania wymagają, by funkcja main wciąż się wykonywała - można to uzyskać w sposób podany powyżej! Alarm Alarm - jednorazowe przerwanie, Timer - powtarzające się wykonanie przerwania - tak można to najprościej opisać 😉 Timery omówimy w następnej sekcji, tymczasem zajmiemy się tematem tej... Przykładowe wywołanie alarmu: add_alarm_in_ms(1000, alarm, NULL, false); Alarm pozwala nam wykonać fragment kodu po określonym czasie - uwaga, dalszy kod wykonuje się pomimo ustawienia alarmu. W celu odczekania na wykonanie przerwania należy ustawić zmienną flagową i pętlę opartą na tej zmiennej. Przykładowy kod: #include <stdio.h> #include "pico/stdlib.h" // Alarm int64_t alarm(alarm_id_t id, void *user_data){ gpio_put(15, true); return 0; } int main() { stdio_init_all(); // Zainicjuj pin z diodą gpio_init(15); gpio_set_dir(15, true); gpio_put(15, false); // Dodaj alarm add_alarm_in_ms(1000, alarm, NULL, false); // Nieskończona pętla - WYMAGANA!!! while (1) { tight_loop_contents(); } } Jak widzimy w alarmie musimy podać czas (w tym przypadku ms, istnieje również wersja tej funkcji pracująca z mikrosekundami - add_alarm_in_us), callback naszego alarmu - funkcję, która będzie wykonana. Jej deklaracja musi być zgodna z tą powyżej (oprócz nazwy). Do tego możemy przesłać jakieś dane w formie wskaźnika do np. struktury. Ostatni parametr definiuje czy alarm ma się wykonać, kiedy jego czas już dawno minął. #include <stdio.h> #include "pico/stdlib.h" int64_t alarm(alarm_id_t id, void *user_data){ uint* pinId = (uint*) user_data; gpio_put(*pinId, true); return 0; } int main() { stdio_init_all(); // Zainicjuj pin z diodą gpio_init(15); gpio_set_dir(15, true); gpio_put(15, false); // Zmienna tymczasowa uint pinId = 15; add_alarm_in_ms(1000, alarm, &pinId, false); // Nieskończona pętla - WYMAGANA!!! while (1) { tight_loop_contents(); } } Jak widać na tym kodzie - możemy bez problemu przesłać np. numer gpio, które chcemy zmodyfikować w naszym alarmie 😉 Możemy również pobrać ID alarmu i w dowolnym momencie przerwać nasz alarm 😉 alarm_id_t id = add_alarm_in_ms(1000, alarm, &pinId, false); cancel_alarm(id); // Przerwij alarm o określonym ID Funkcje add_alarm_in_ms(<opóźnienie_ms>, <callback>, <user_data>, <aktywuj_przeszły>) - dodaje alarm opóźniony o podany czas wyrażony w milisekundach, ostatni parametr opisuje, że jeżeli dany moment nastąpi zanim ta metoda się wykona to callback również zostanie wykonany add_alarm_in_us(<opóźnienie_us>, <callback>, <user_data>, <aktywuj_przeszły>) - to samo co powyżej tylko zamiast ms są us 😉 cancel_alarm(<alarm_id>) - anuluje alarm o określonym ID pozyskanym z funkcji dodawania alarmu Oprócz alarmów są też timery... Teraz czas na timery - alarmy powtarzające się co pewien czas 😉 Timer inicjujemy podobnie do alarmu 🙂 struct repeating_timer timer; add_repeating_timer_ms(100, tCallback, &pin, &timer); Z tym, że na początku musimy stworzyć strukturę timera, potem możemy go dodać podając czas, przerwanie, dane użytkownika oraz adres naszej struktury. bool tCallback(repeating_timer_t * timer){ status = !status; uint* pinId = (uint*) timer->user_data; gpio_put(*pinId, status); return true; } Funkcja przerwania też jest ciut inna - ma inny typ zwracany i tylko jeden parametr - wskaźnik do timera. Jak widzimy w powyższym przykładzie dane są przechowywane wewnątrz naszego timera, więc by je uzyskać musimy odwołać się do pola zawartego wewnątrz struktury. Przykładowy kod: #include <stdio.h> #include "pico/stdlib.h" bool status = false; bool tCallback(repeating_timer_t * timer){ status = !status; uint* pinId = (uint*) timer->user_data; gpio_put(*pinId, status); return true; } int main() { stdio_init_all(); // Zainicjuj pin z diodą gpio_init(15); gpio_set_dir(15, true); gpio_put(15, false); uint pin = 15; struct repeating_timer timer; add_repeating_timer_ms(100, tCallback, &pin, &timer); // Nieskończona pętla - WYMAGANA!!! while (1) { tight_loop_contents(); } } Funkcje add_repeating_timer_ms(<czas_ms>, <callback>, <dane_usera>, <timer>) - uruchamia powtarzający się co określony w ms czas timer. Wcześniej należy stworzyć strukturę timera, patrz przykład. add_repeating_timer_us(<czas_us>, <callback>, <dane_usera>, <timer>) - to samo co powyżej tylko dla us 😉 cancel_repeating_timer(<timer>) - anuluje wykonywanie danego timera 😉 Wielowątkowość Miało nie być... jednak jest... Do obsługi wielowątkowości musimy dołączyć bibliotekę - pico_multicore do pliku CMakeLists.txt. Szukamy linijki "target_link_libraries(<NAZWA_PROJEKTU> pico_stdlib)" i dodajemy pod nią "target_link_libraries(<NAZWA_PROJEKTU> pico_multicore)". Następnie możemy stworzyć prosty program, który uruchomi kod na drugim rdzeniu - przykład? #include <stdio.h> #include "pico/stdlib.h" #include "pico/multicore.h" void core1_entry() { printf("To jest rdzeń 2!\n"); while (1) tight_loop_contents(); } int main() { stdio_init_all(); printf("Witaj wiele rdzeni!\n"); printf("To jest rdzeń 1!\n"); multicore_launch_core1(core1_entry); } Do uruchomienia funkcji na innym rdzeniu używamy multicore_launch_core1. W środku umieszczamy nazwę funkcji, którą chcemy uruchomić. Jej deklaracja musi się pokrywać z tą zaprezentowaną powyżej - musi to być funkcja nie zwracająca żadnej wartości i bez parametrów. Ale jak przekazać informacje między rdzeniami? Do tego należy wykorzystać funkcje multicore_fifo_push_blocking(<value>) oraz multicore_fifo_pop_blocking(). Pierwszą umieszczamy w kodzie wątku źródłowego, a drugą w docelowym. Pierwsza z nich ustawia wartość semafora, druga odczytuje. Dopóki semafor nie zostanie odczytany z wątku docelowego, wtedy kod na wątku źródłowym oczekuje (nie wykonuje innych operacji). Oprócz tego istnieje funkcja, która ustawia semafor z timeoutem, aczkolwiek tutaj odeślę do oficjalnej dokumentacji Pi Pico 😉 Przykład kodu z semaforami? #include <stdio.h> #include "pico/stdlib.h" #include "pico/multicore.h" #define FLAG_VALUE 123 void core1_entry() { multicore_fifo_push_blocking(FLAG_VALUE); uint32_t g = multicore_fifo_pop_blocking(); if (g != FLAG_VALUE) // Porównanie 1 printf("Semafor (0) nie jest OK :(\n"); else printf("Semafor (0) jest OK ;)\n"); while (1) tight_loop_contents(); } int main() { stdio_init_all(); printf("Witajcie rdzenie!\n"); multicore_launch_core1(core1_entry); sleep_ms(100); uint32_t g = multicore_fifo_pop_blocking(); if (g == FLAG_VALUE){ // Porównanie 2 multicore_fifo_push_blocking(FLAG_VALUE); printf("Semafor (1) jest OK ;)\n"); } else { printf("Semafor (1) nie jest OK :(\n"); } } Kod wykona się całkowicie poprawnie. Jego efekt na konsoli UART powinien wyglądać następująco: Działanie kodu w uproszczony sposób można opisać następująco: Rdzeń 1 Wyślij semafor o wartości FLAG_VALUE i oczekuj aż zostanie odebrany Oczekuj na semafor Jeżeli otrzymany semafor ma wartość FLAG_VALUE poinformuj, że semafor 0 JEST OK, w innym przypadku poinformuj, że semafor NIE JEST OK Zapętl się w nieskończoność Rdzeń 0 Wyświetl powitanie Uruchom funkcję core1_entry() na rdzeniu 1 Odczekaj 100ms Oczekuj na semafor Jeżeli semafor ma wartość FLAG_VALUE Poinformuj, że semafor 1 JEST OK Wyślij semafor o wartości FLAG_VALUE i oczekuj na odbiór Jeżeli punkt 5 nie został spełniony Poinformuj, że semafor 1 NIE JEST OK Jak można wywnioskować z powyższego opisu, gdy rdzeń 1 wyśle niepoprawny semafor do rdzenia 0, to kod z rdzenia drugiego się nie wykona. Możesz sam to sprawdzić podmieniając wartość FLAG_VALUE w funkcji multicore_fifo_push_blocking w core1_entry 😉 Wtedy rezultat w konsoli będzie wyglądał następująco: Funkcje multicore_launch_core1(<funkcja>) - uruchom funkcję na drugim rdzeniu (wyłącznie void bez parametrów) multicore_fifo_push_blocking(<wartość>) - wyślij semafor do drugiego rdzenia i oczekuj na jego pobranie multicore_fifo_pop_blocking() - pobierz wartość semafora (wykonywane z drugiego rdzenia) Zaawansowane Przerwania? Inne tematy? Temat zaawansowanych przerwań opisuje biblioteka hardware_irq. Tutaj odsyłam do oficjalnej dokumentacji Pi Pico 😉 Nie mogę przecież wszystkiego podawać na dłoni 😉 Ten poradnik miał omówić najbardziej podstawowe zagadnienia z pisania na Pi Pico. Powyższych do nich nie zaliczam... Dodatkowo warto też zapoznać się z wbudowanym RTC 🙂 Dodatkowe funkcje sleep_ms(<czas>) - odczekaj określony w ms czas sleep_us(<czas>) - odczekaj n mikrosekund Visual Studio - alternatywna metoda kompilacji (zalecana) Alternatywnie projekt można skompilować poprzez konsolę developerską (Developer Command Prompt). Wchodzimy do folderu z plikami, potem do podfolderu "build" np. cd C:\Users\nov11\Documents\Pico\pico-project-generator\Blinky\build Potem wpisujemy komendę generującą pliki cmake -G "NMake Makefiles" .. Potem możemy skompilować program nmake I gotowe 😉 Pytania? Po to jest sekcja komentarzy 😉
- 8 odpowiedzi
-
- 7
-
-
- PiPico
- Raspberry Pi
-
(i 1 więcej)
Tagi:
-
Witajcie. Mam pewien problem. Gdy chcę wejść na YT z RPi, Chromium jakby... zamyka się, a gdy ponownie go otwieram pokazuję się komunikat z pytaniem czy przywrócić strony bo Chromum nie został poprawnie zamknięty. Czy ktoś z was wie, jak to naprawić? Będę wdzięczny za odpowiedź.
-
- Raspberry Pi
- YouTube
- (i 3 więcej)
-
Witajcie. Kolejne pytanie 🙂. Czy ten brzęczyk (https://botland.com.pl/buzzery-generatory-dzwieku/2090-buzzer-z-generatorem-fy248-23mm-3-18v-w-obudowie-5904422349011.html) bez obaw mogę podłączyć do GPIO RPi 4B (4GB RAM)? Chciałbym zrealizować ten projekt (https://projects.raspberrypi.org/en/projects/leds-buzzers-scratch-games/0), a później może jakieś własne projekty. Więc czy powyższy brzęczyk mogę bez obaw mogę podłączyć do GPIO RPi 4B (4GB RAM)? Będę wdzięczny za odpowiedź. 🙂
- 12 odpowiedzi
-
- Raspberry Pi
- głośniki
-
(i 1 więcej)
Tagi: