Popularny post Karrol Napisano Marzec 19, 2021 Popularny post Udostępnij Napisano Marzec 19, 2021 Ten artykuł jest częścią serii "Firebase w zastosowaniach IoT" #1 - Czym jest Firebase? Jak zacząć? #2 - Firebase z ESP32 i ESP8266 #3 - Wyświetlanie danych użytkownikowi poprzez stronę internetową #4 - Projekt praktyczny, Hosting Potrafimy już obsługiwać Firebase za pomocą urządzeń ESP oraz przez JavaScript. Dzisiaj podsumujemy wiedzę zdobytą w poprzednich trzech częściach kursu i wykorzystamy ją w praktyce. Zbudujemy prosty system smarthome. Będzie on umożliwiał użytkownikowi na zdalne: sprawdzenie aktualnej temperatury w pokoju sprawdzenie kilku ostatnich pomiarów wilgotności ziemi w doniczce kwiatka sterowanie diodą led System ten jest bardzo prosty, ale umożliwi nam praktyczne wykorzystanie wielu funkcji Firebase. 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 » Potrzebny sprzęt: ESP8266 (ew. ESP32) cyfrowy czujnik temperatury DS18B20 czujnik wilgotności gleby dioda led rezystory 4.7kΩ , 330Ω płytka stykowa, przewody połączeniowe... Jeśli nie masz któregoś z czujników, to możesz użyć dowolnego innego. QUO VADIS? Najpierw musimy określić jak dokładnie będzie działać nasz system. Urządzenie będzie co określony czas wysyłać do bazy wartość temperatury, zastępując wcześniej zapisaną wartość, oraz wysyłać wartość wilgotności gleby tworząc nową gałąź (nie tracimy poprzednich wyników). Będzie nasłuchiwać zmian w gałęzi, w której jest zapisany stan diody led i odpowiednio ją zapalać lub gasić. Zalogowany użytkownik będzie mógł poprzez stronę internetową sprawdzić aktualną wartość temperatury oraz 4 ostatnie pomiary wilgotności, a także zobaczyć aktualny stan diody i zdecydować o jej włączeniu lub wyłączeniu. Nasza baza danych będzie przechowywała mało informacji, które nie będą bardzo zagnieżdżone, a my nie będziemy wykonywać skomplikowanych operacji na danych, więc w naszym projekcie użyjemy Realtime Database. Nie chcemy, aby sąsiad mógł się bawić naszą diodą, więc użyjemy Auth do uwierzytelniania użytkowników. KONFIGURACJA FIREBASE Na początku tworzymy projekt Firebase, konfigurujemy Auth (uwierzytelnianie za pomocą e-mail) i dodajemy użytkownika np. test@test.test, 123456 (jest to opisane w I części kursu). Tworzenie projektu i konfiguracja Auth. Następnie dodajemy do projektu Realtime Database i konfigurujemy zabezpieczenia, aby tylko zalogowany użytkownik mógł mieć dostęp do naszej bazy. Dodawanie RTDB, reguły zabezpieczeń. Teraz tworzymy strukturę danych w bazie: w gałęzi led będziemy przechowywać stan diody (true/false) w gałęzi temperatura będziemy przechowywać wartość ostatniego pomiaru temperatury i czas tego pomiaru (w czasie uniksowym) w gałęzi wilgotnosc będziemy przechowywać kolejne pomiary wilgotności. Klucz będzie generowany automatycznie, każdy wpis będzie zawierał zmierzoną wartość i czas pomiaru. Struktura naszej bazy danych. Musimy jeszcze dodać aplikację webową do naszego projektu i pozyskać dane dostępowe projektu (klucz API, adres URL). Dodawanie aplikacji, dane dostępowe. ESP8266 HARDWARE Mając skonfigurowany Firebase możemy wziąć się za budowę naszego urządzenia. Łączymy: wyjście czujnika wilgotności do pinu A0 ESP wyjście czujnika temperatury DS18B20 do pinu D2 oraz wpinamy rezystor 4.7kΩ między wyjściem, a dodatnią szyną zasilania (tak jak zostało to opisane w kursie Arduino) dłuższą nożkę diody do pinu D2, a krótszą przez rezystor 330Ω do GND Całość prezentuje się następująco: Połączenie. SOFTWARE Teraz zajmiemy się pisaniem oprogramowania dla naszego ESP. Tutaj przyda się wiedza z II części kursu. Czujnik wilgotności jest najczęściej czujnikiem analogowym i wymaga kalibracji. W tym celu wgrywamy na płytkę przykład 01.Basics -> AnalogReadSerial z ArduinoIDE. Następnie sprawdzamy jakie wartości pokazują się w monitorze portu szeregowego, gdy czujnik znajduje się w suchym miejscu (zakładamy wilgotność 0%) oraz po zanurzeniu w wodzie (wilgotność 100%) i zapamiętujemy te wartości. U mnie wynoszą one odpowiednio 720 i 270. Tworzymy nowy projekt, importujemy biblioteki do czujnika temperatury i do Firebase oraz definiujemy obiekty Firebase. #include <OneWire.h> #include <DallasTemperature.h> // dołączamy bibliotekę WiFi w zależności od płytki #if defined(ESP32) #include <WiFi.h> #elif defined(ESP8266) #include <ESP8266WiFi.h> #endif // dołączenie biblioteki do obsługi Firebase #include <Firebase_ESP_Client.h> //Definiujemy obiekt Firebase Data FirebaseData fbdo; FirebaseData fbdo_stream; //Definiujemy FirebaseAuth, gdzie będziemy trzymać dane do uwierzytelniania FirebaseAuth auth; // Definiujemy FirebaseConfig, gdzie będziemy trzymać dane do konfiguracji FirebaseConfig config; Następnie definiujemy numery pinów, zmierzone przed chwilą wartości wilgotności oraz czas między pomiarami. #define LED_PIN D3 #define TEMP_PIN D2 #define SOIL_PIN A0 const int AirValue = 720; // wartość wilgotności w powietrzu - 0% const int WaterValue = 270; // watość wilgotności w wodzie - 100% const int TEMP_DELAY = 10000;//ms const int SOIL_DELAY = 20000;//ms Teraz konfigurujemy czujnik temperatury: OneWire oneWire(TEMP_PIN); DallasTemperature temp(&oneWire); Zdefiniujmy sobie funkcję, która będzie mierzyła wilgotność i zwróci wynik w procentach. Funkcja odczytuje wartość z czujnika, przeskalowuje ją z wartości maksymalnej i minimalnej, które zmierzyliśmy, do 0-100%. Dodatkowo wprowadzamy zabezpieczenie, żeby wynik zawsze mieścił się w przedziale 0-100% int getSoil() // pomiar wilgotnosci, zwraca % { int soilValue = analogRead(SOIL_PIN); int soilPercent = map(soilValue, AirValue, WaterValue, 0, 100); if(soilPercent > 100) soilPercent = 100; else if (soilPercent <0) soilPercent = 0; return(soilPercent); } Tworzymy funkcję do odczytu temperatury: float getTemperature() { temp.requestTemperatures(); return(temp.getTempCByIndex(0)); } Wewnątrz setup() ustawiamy pin diody jako wyjście, załączamy czujnik temperatury, łączymy się z wifi i konfigurujemy połączenie z Firebase (tak jak jest to opisane w II części kursu). void setup() { Serial.begin(115200); pinMode(D3, OUTPUT); temp.begin(); WiFi.begin("ssid", "haslo"); //zmienić Serial.print("Connecting to Wi-Fi"); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(300); } //Do ustawień uwierzytelniania zapisujemy dane do konta użytkownika auth.user.email = "test@test.test"; auth.user.password = "123456"; //Do ustawień konfiguracji (config) zapisujemy klucz API i adres projektu config.host = "https://xxxxx-default-rtdb.firebaseio.com"; // zmienić config.api_key = "xxxxxxxx"; // zmienić //Inicjalizujemy połączenie z Firebase używając ustawionych danych Firebase.begin(&config, &auth); //Ustawiamy automatyczne łączenie z WiFi po zerwaniu połączenia Firebase.reconnectWiFi(true); } Wewnątrz funkcji loop() tworzymy dwa warunki (pomiar temperatury i wilgotności), które wykonują się co określony wcześniej czas. Korzystamy do tego z millis(), tak jak zostało to opisane w kursie Arduino. W każdym z tych warunków mierzymy odpowiednio temperaturę lub wilgotność i wysyłamy do RTDB. void loop() { static unsigned long prevTempMillis = 0; static unsigned long prevSoilMillis = 0; if(millis()-prevTempMillis >= TEMP_DELAY) { prevTempMillis = millis(); float temp = getTemperature(); Serial.print("T: "); Serial.println(temp); Serial.println(); if(Firebase.RTDB.setFloat(&fbdo, "/temperatura/wartosc", temp)) { //sukces Serial.println("Set v1 - sukces"); }else{ //blad Serial.print("Blad w set v1: "); Serial.println(fbdo.errorReason()); } Firebase.RTDB.setTimestamp(&fbdo, "/temperatura/czas"); } if(millis()-prevSoilMillis >= SOIL_DELAY) { prevSoilMillis = millis(); int soil = getSoil(); Serial.print("W: "); Serial.print(soil); Serial.println(" %"); Serial.println(); FirebaseJson json; json.add("wartosc",soil); if(Firebase.RTDB.pushJSON(&fbdo, "/wilgotnosc/", &json)) { //sukces Serial.println("Push - sukces"); }else{ //blad Serial.print("Blad w push: "); Serial.println(fbdo.errorReason()); } String path = "/wilgotnosc/"; path = path + fbdo.pushName(); path = path + "/czas"; Firebase.RTDB.setTimestamp(&fbdo, path.c_str()); } } Zostało nam tylko ustawić nasłuchiwanie zmian w gałęzi led. W tym celu tworzymy funkcję streamCallback(), która wykonuje się po wykryciu zmiany w bazie, oraz pustą funkcję streamTimeoutCallback(), która wykonuje się po przekroczeniu limitu czasu połączenia (my zostawiamy ją pustą, ale musimy ją stworzyć). void streamCallback(FirebaseStream data) { if (data.dataType() == "int") digitalWrite(LED_PIN, data.intData()); else if (data.dataType() == "boolean") digitalWrite(LED_PIN, data.boolData()); } void streamTimeoutCallback(bool timeout) { ; } Ostatnim krokiem jest włączenie nasłuchiwania wewnątrz setup(): Firebase.RTDB.beginStream(&fbdo_stream, "/led"); Firebase.RTDB.setStreamCallback(&fbdo_stream, streamCallback, streamTimeoutCallback); Cały kod wygląda następująco: #include <OneWire.h> #include <DallasTemperature.h> // dołączamy bibliotekę WiFi w zależności od płytki #if defined(ESP32) #include <WiFi.h> #elif defined(ESP8266) #include <ESP8266WiFi.h> #endif // dołączenie biblioteki do obsługi Firebase #include <Firebase_ESP_Client.h> //Definiujemy obiekt Firebase Data FirebaseData fbdo; FirebaseData fbdo_stream; //Definiujemy FirebaseAuth, gdzie będziemy trzymać dane do uwierzytelniania FirebaseAuth auth; // Definiujemy FirebaseConfig, gdzie będziemy trzymać dane do konfiguracji FirebaseConfig config; #define LED_PIN D3 #define TEMP_PIN D2 #define SOIL_PIN A0 const int AirValue = 720; // watość wilgotności w powietrzu - 0% const int WaterValue = 270; // watość wilgotności w wodzie - 100% const int TEMP_DELAY = 10000;//ms const int SOIL_DELAY = 20000;//ms OneWire oneWire(TEMP_PIN); DallasTemperature temp(&oneWire); int getSoil() // pomiar wilgotnosci, zwraca % { int soilValue = analogRead(SOIL_PIN); //put Sensor insert into soil Serial.print("Wilgotnosc: "); //później zakomentować Serial.println(soilValue); int soilPercent = map(soilValue, AirValue, WaterValue, 0, 100); if(soilPercent > 100) soilPercent = 100; else if (soilPercent <0) soilPercent = 0; return(soilPercent); } float getTemperature() { temp.requestTemperatures(); return(temp.getTempCByIndex(0)); } void streamCallback(FirebaseStream data) { if (data.dataType() == "int") digitalWrite(LED_PIN, data.intData()); else if (data.dataType() == "boolean") digitalWrite(LED_PIN, data.boolData()); } void streamTimeoutCallback(bool timeout) { ; } void setup() { Serial.begin(115200); pinMode(D3, OUTPUT); temp.begin(); WiFi.begin("xxxx", "xxxx"); //zmienić Serial.print("Connecting to Wi-Fi"); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(300); } //Do ustawień uwierzytelniania zapisujemy dane do konta użytkownika auth.user.email = "test@test.test"; auth.user.password = "123456"; //Do ustawień konfiguracji (config) zapisujemy klucz API i adres projektu config.host = "https://xxxxx-default-rtdb.firebaseio.com"; //zmienić config.api_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxx"; //zmienić //Inicjalizujemy połączenie z Firebase używając ustawionych danych Firebase.begin(&config, &auth); //Opcjonalnie ustawiamy automatyczne łączenie z WiFi po zerwaniu połączenia Firebase.reconnectWiFi(true); Firebase.RTDB.beginStream(&fbdo_stream, "/led"); Firebase.RTDB.setStreamCallback(&fbdo_stream, streamCallback, streamTimeoutCallback); } void loop() { static unsigned long prevTempMillis = 0; static unsigned long prevSoilMillis = 0; if(millis()-prevTempMillis >= TEMP_DELAY) { prevTempMillis = millis(); float temp = getTemperature(); Serial.print("T: "); Serial.println(temp); Serial.println(); if(Firebase.RTDB.setFloat(&fbdo, "/temperatura/wartosc", temp)) { //sukces Serial.println("Set v1 - sukces"); }else{ //blad Serial.print("Blad w set v1: "); Serial.println(fbdo.errorReason()); } Firebase.RTDB.setTimestamp(&fbdo, "/temperatura/czas"); } if(millis()-prevSoilMillis >= SOIL_DELAY) { prevSoilMillis = millis(); int soil = getSoil(); Serial.print("W: "); Serial.print(soil); Serial.println(" %"); Serial.println(); FirebaseJson json; json.add("wartosc",soil); if(Firebase.RTDB.pushJSON(&fbdo, "/wilgotnosc/", &json)) { //sukces Serial.println("Push - sukces"); }else{ //blad Serial.print("Blad w push: "); Serial.println(fbdo.errorReason()); } String path = "/wilgotnosc/"; path = path + fbdo.pushName(); path = path + "/czas"; Firebase.RTDB.setTimestamp(&fbdo, path.c_str()); } } Po wgraniu programu na płytkę nasze urządzenie zacznie wysyłać do bazy co 10 sekund wartość temperatury i co 20 sekund wartość wilgotności oraz włączać lub wyłączać diodę, w zależności od wartości gałęzi led. Działanie. STRONA INTERNETOWA Teraz zajmiemy się interfejsem dla naszego użytkownika. Nasz projekt Firebase możemy zintegrować z zewnętrznymi narzędziami (np. NodeRed, Sketchware, ...), napisać aplikację mobilną lub stworzyć własną stronę w html+css+js. My skorzystamy z tej ostatniej ścieżki. Tworzenie własnej strony od zera daje nam dużo możliwości, to my decydujemy o każdym szczególe naszego serwisu. Niestety wymaga to umiejętności programowania w html+css+js, aby osiągnąć zamierzony efekt. W tym kursie skupiamy się na poznawaniu Firebase, więc nasza strona będzie bardzo prosta, ale nic nie stoi na przeszkodzie, aby dodać do niej więcej funkcji. Oto nasz dzisiejszy cel: Widok gotowej strony. Na początku tworzymy plik index.html. Robimy w nim szkielet naszej strony i konfigurujemy połączenie z Firebase. Zamieszczamy w nim identyfikatory wskazujące: miejsce na formularz logowania panel diody, miejsce na aktualny stan diody, przycisk do zmieniania stanu diody panel temperatury, miejsce na aktualną wartość temperatury, miejsce na czas ostatniego pomiaru panel na listę ostatnich pomiarów wilgotności Dane w tych miejscach będziemy uzupełniać z poziomu JavaScript. Cały kod wygląda następująco (zwróć uwagę na komentarze): <!DOCTYPE HTML> <html lang="pl"> <html> <head> <meta charset="utf-8" /> <title>Panel użytkownika</title> <!-- Importujemy skrypty do obsługi Firebase --> <script src="https://www.gstatic.com/firebasejs/8.2.9/firebase-app.js"></script> <script src="https://www.gstatic.com/firebasejs/8.2.9/firebase-auth.js"></script> <script src="https://www.gstatic.com/firebasejs/8.2.9/firebase-database.js"></script> <!-- Dołączamy plik css odpowiadający za wygląd naszej strony --> <link rel="stylesheet" href="styles.css"> </head> <body> <h1>Firebase w zastosowaniach IoT</h1> <div id="formularz_logowania"></div> <!-- Miejsce na formularz logowania --> <div> <!-- Miejsca na informacje o diodzie, temperaturze i wilgotności --> <div id="panel-led" class="panel"> Stan diody: <span id="wartosc-led">[stan]</span> <button id="przycisk-led">Zmień</button> </div> <div id="panel-temperatura" class="panel"> Temperatura: <b><span id="wartosc-temperatura">[wartość]</span></b> (Ostatni pomiar: <span id="czas-temperatura">[czas]</span>) </div> <div id="panel-wilgotnosc" class="panel"></div> </div> <script> // Konfigurujemy dane dostępowe do Firebase var config = { apiKey: "xxxxxxxxx", authDomain: "xxxxx.firebaseapp.com", databaseURL: "https://xxxxx-default-rtdb.firebaseio.com", projectId: "xxxxx", storageBucket: "xxxxx.appspot.com", messagingSenderId: "xxxxxxxx", appId: "xxxxxxxxxxxxxxxxxxxx" }; firebase.initializeApp(config); // Inicjalizujemy Auth const auth = firebase.auth(); // Inicjalizujemy RTDB const rtdb = firebase.database(); </script> <!-- Dołączamy nasze pliki .js --> <script src="login.js"></script> <script src="temperatura.js"></script> <script src="wilgotnosc.js"></script> <script src="led.js"></script> </body> </html> Przygotowałem prosty plik .css, który zadba o trochę ładniejszy wygląd naszej strony (nie jestem mistrzem designu 🙃). .panel { width: 90%; padding: 5px; margin: 5px; border: 3px solid gray; float: left; border-radius: 15px; } .kafelek-wilgotnosc { width: 90%; padding: 5px; margin: 5px; border: 3px solid gray; float: left; border-radius: 15px; } Teraz zajmijmy się mózgiem naszej strony, czyli kodem js. Dla lepszej czytelności postanowiłem, że stworzymy 4 pliki, ale równie dobrze wszystko mogłoby się znaleźć w jednym. Są to: login.js - obsługa funkcji logowania i wylogowywania użytkownika temperatura.js - nasłuchiwanie i aktualizowanie wartości temperatury wilgotnosc.js - nasłuchiwanie i aktualizowanie wartości wilgotności led.js - nasłuchiwanie i aktualizowanie stanu diody, obsługa przycisku LOGIN W pliku login.js umieścimy logikę odpowiedzialną za logowanie i wylogowywanie użytkownika. Na początku sprawdzamy czy użytkownik jest już zalogowany - jeśli tak to w miejscu o id="formularz_logowania" wstawiamy przycisk służący do wylogowania, jeśli nie to wstawiamy tam formularz do logowania. Wykorzystamy do tego poznaną w III części kursu funkcję auth.onAuthStateChanged(). Użytkownik zalogowany / niezalogowany. Gdy zalogowany użytkownik kliknie przycisk “Wyloguj się” zostanie wywołana funkcja auth.signOut() oraz wypisany alert "Wylogowano pomyślnie". Wtedy zadziała także funkcja auth.onAuthStateChanged() wewnątrz pozostałych plików js, która zmieni wartości diody, temperatury i wilgotności na pusty tekst "", a przycisk “Wyloguj się” na formularz logowania. Gdy niezalogowany użytkownik uzupełni i zatwierdzi formularz, zostanie wywołana funkcja auth.signInWithEmailAndPassword(email, haslo), która spróbuje zalogować użytkownika podanymi danymi. Jeśli się to uda zostanie wyświetlony alert “Pomyślnie zalogowano”. Wtedy zadziała także auth.onAuthStateChanged() wewnątrz pozostałych plików js, która zacznie pobierać i wyświetlać wartości z bazy danych, a formularz logowania zamieni na przycisk “Wyloguj się”. Jeśli przy próbie logowania wystąpi błąd to zostanie on wypisany użytkownikowi. Cały kod wygląda następująco: const uchwyt_formularz_logowania = document.querySelector('#formularz_logowania'); // uchwyt do miejsca na formularz logowania auth.onAuthStateChanged(user => { if (user) { //zalogowany // zamiast formularza logowania umieszczamy na stronie przycisk do wylogowania uchwyt_formularz_logowania.innerHTML = '<button id="wyloguj-button">Wyloguj się</button>'; const wyloguj_button = document.querySelector('#wyloguj-button'); // dodajemy funkcję wywoływaną po naciśnięciu przycisku wyloguj_button.addEventListener('click', (e) =>{ e.preventDefault(); auth.signOut().then(() => {alert("Wylogowano pomyślnie.")}) }); } else { //wylogowany //umieszczamy formularz logowania na stronie uchwyt_formularz_logowania.innerHTML =`<form id="formularz"> <input type="email" name="email" placeholder="email"> <input type="password" name="haslo" placeholder="haslo"> <button>Zaloguj</button> </form>`; const formularz = document.querySelector('#formularz'); // dodajemy funkcję wywoływaną po zatwierdzeniu formularza formularz.addEventListener('submit', (e)=>{ e.preventDefault(); //odczytujemy dane wpisane przez użytkownika const email = formularz['email'].value; const haslo = formularz['haslo'].value; auth.signInWithEmailAndPassword(email, haslo).then((cred) =>{ alert("Pomyślnie zalogowano"); }).catch(function(error) { // jeśli coś się nie udało to sprawdzamy powód var errorCode = error.code; var errorMessage = error.message; if (errorCode === 'auth/wrong-password') { alert('Hasło nieprawidłowe!'); } else if (errorCode === 'auth/user-not-found') { alert('Nie ma takiego użytkownika!'); } else if (errorCode === 'auth/invalid-email') { alert('Wpisz poprawny adres email!'); } else { alert(errorMessage); } }); }); } }) TEMPERATURA Analogicznie jak w przypadku login.js, w temperatura.js zawartość widziana przez użytkownika będzie zależeć od jego stanu zalogowania. Jeżeli użytkownik jest wylogowany to w miejscach o id “wartosc-temperatura” i “czas-temperatura” będzie pustka “”. Jeśli jest zalogowany to uruchamiamy nasłuchiwanie zmian temperatury w bazie danych za pomocą funkcji on(). Po wykryciu zmiany zamieniamy wyświetlone wartości “wartosc-temperatura” i “czas-temperatura” na nowe. Cały kod wygląda następująco: const uchwyt_wartosc_temp = document.querySelector('#wartosc-temperatura'); const uchwyt_czas_temp = document.querySelector('#czas-temperatura'); auth.onAuthStateChanged(user => { if (user) { //zalogowany // nasłuchujemy zmian rtdb.ref('temperatura').on('value', (snapshot) => { uchwyt_wartosc_temp.textContent = snapshot.val()['wartosc']; // wypisujemy na stronie nową wartość temperatury var d = new Date(); d.setTime(snapshot.val()['czas']); uchwyt_czas_temp.textContent = d.toLocaleString("pl-PL"); // wypisujemy datę/czas w polskim formacie }); } else //wylogowany - pokazujemy pusty szkielet strony - bez danych { uchwyt_wartosc_temp.textContent = ''; uchwyt_czas_temp.textContent = ''; } }); WILGOTNOŚĆ W wilgotnosc.js nasłuchujemy zmian wilgotności analogicznie do temperatura.js, ale zamiast jednego wyniku odczytujemy ostatnie 4 pomiary i dla każdego z nich tworzymy kafelek, który umieszczamy w miejsce starych kafelków na liście o id “panel-wilgotnosc”. Kod: const uchwyt_panel_wilgotnosc = document.querySelector('#panel-wilgotnosc'); auth.onAuthStateChanged(user => { if (user) { //zalogowany //funkcja tworząca kafelek z wartością wilgotności oraz czasem pomiaru i umieszczająca go na liście function dodajWilgotnosc(wartosc, czas){ // tworzymy kafelek let kafelek = document.createElement('div'); kafelek.className = "kafelek-wilgotnosc"; //tworzymy elementy kafelka let elWartosc = document.createElement('div'); let elCzas = document.createElement('div'); // zapisujemy odczytaną z bazy wartość wilgotności do kafelka elWartosc.textContent = wartosc; // zapisujemy czas pomiaru do kafelka var d = new Date(); d.setTime(czas); elCzas.textContent = d.toLocaleString("pl-PL"); // dodajemy elementy do kafelka kafelek.appendChild(elWartosc); kafelek.appendChild(elCzas); // dodajemy kafelek na górze listy pomiarów uchwyt_panel_wilgotnosc.insertBefore(kafelek, uchwyt_panel_wilgotnosc.childNodes[0]); } // nasłuchiwanie zmian/nowych pomiarów rtdb.ref('wilgotnosc').orderByKey().limitToLast(4).on('value', (snapshot) => { //sortujemy po id wpisu i wybieramy ostatnie 4 (najnowsze) uchwyt_panel_wilgotnosc.textContent=''; for (const pomiar in snapshot.val()) { dodajWilgotnosc(snapshot.val()[pomiar]['wartosc'], snapshot.val()[pomiar]['czas']); } }); } else //wylogowany - pokazujemy pusty szkielet strony - bez danych uchwyt_panel_wilgotnosc.textContent=''; }); LED W led.js nasłuchujemy zmian stanu diody analogicznie jak temperatury. Oprócz tego kodujemy obsługę wciśnięcia przycisku. Aktualny stan diody przechowujemy w zmiennej stan i po wciśnięciu przycisku zapisujemy do bazy stan przeciwny niż aktualny za pomocą funkcji update(). Kod: const uchwyt_wartosc_led = document.querySelector('#wartosc-led'); const uchwyt_przycisk_led = document.querySelector('#przycisk-led'); auth.onAuthStateChanged(user => { if (user) { //zalogowany var stan = false; // nasłuchujemy zmian stanu diody rtdb.ref('led').on('value', (snapshot) => { uchwyt_wartosc_led.textContent = snapshot.val(); //wypisujemy na stronie nowy stan diody stan = snapshot.val(); }); // funkcja wywoływana po kliknięciu przycisku function zmienLed() { if(stan == true || stan == 1) { var json = { led : false }; rtdb.ref('/').update(json); } else { var json = { led : true }; rtdb.ref('/').update(json); } } uchwyt_przycisk_led.onclick = zmienLed; } else //wylogowany - pokazujemy pusty szkielet strony - bez danych { uchwyt_wartosc_led.textContent=''; } }); DZIAŁANIE Nasza strona jest już gotowa. Możemy ją przetestować otwierając plik index.html w przeglądarce. HOSTING Nasza strona działa, ale jest dostępna tylko z poziomu naszego komputera. Takie rozwiązanie nas nie zadowala, bo chcielibyśmy sterować naszym system z dowolnego urządzenia z dowolnego miejsca na świecie. Aby było to możliwe musimy zapewnić naszemu projektowi hosting. Możemy stworzyć własny domowy hosting (np. na Raspberry Pi), skorzystać z hostingu innej firmy lub wykorzystać Hosting Firebase. Tak jak całe Firebase jest on dostępny za darmo w ramach limitów. Aby rozpocząć, przechodzimy do zakładki Hosting w konsoli Firebase i klikamy “Get started”. Wyświetli się nam instrukcja jak krok po kroku umieścić naszą stronę na serwerze. Na początku musimy zainstalować na komputerze narzędzia Firebase wpisując w wierszu poleceń: npm install -g firebase-tools Następnie w wierszu poleceń przechodzimy do pliku na komputerze, gdzie chcemy zainicjalizować nasz projekt. Działa to analogicznie do tworzenia repozytoriów Gita. W Windowsie służy do tego polecenie: cd "ścieżka_do_pliku" Następnie musimy się zalogować kontem google, na którym mamy projekt Firebase za pomocą polecenia: firebase login Gdy już jesteśmy zalogowani inicjujemy projekt wpisując: firebase init Naszym oczom powinien ukazać się piękny napis FIREBASE i zaczną wyskakiwać pytania. Spokojnie, co prawda będzie dużo pytań, ale nie jest to egzamin, więc nie musisz się stresować 😉 . W pierwszym pytaniu Firebase zapyta się nas czy na pewno chcemy w tym miejscu zainicjować projekt. Wpisujemy Yes. Pierwsze pytanie. Odpowiedź: Yes. Następnie musimy zaznaczyć, z których usług Firebase korzystamy w tym projekcie. Wybieramy Database i Hosting. Drugie pytanie. W kolejnym kroku wybieramy, z którym projektem w konsoli Firebase ma być powiązany projekt, który inicjalizujemy na komputerze. My wybieramy istniejący projekt, który wcześniej utworzyliśmy. Wybór istniejącego projektu. Następne dwa pytania dotyczą zasad bezpieczeństwa oraz nazwy publicznego katalogu projektu. Zostawiamy wartości domyślne klikając enter. Kolejne pytanie (już 6?) dotyczy formy naszej aplikacji webowej. Musimy zdecydować, czy ma to być aplikacja z tylko jedną podstroną (/index.html), czy z większą ilością podstron. W naszym wypadku mamy tylko jeden plik html, więc możemy wybrać Y (choć nie ma to większego znaczenia). Wpisujemy Y. Ostatnie (nareszcie) pytanie dotyczy integracji z GitHubem. My na razie tego nie chcemy, więc wybieramy N. Udało się! Właśnie zainicjalizowaliśmy katalog z projektem Firebase na naszym komputerze. Po wejściu w folder, który wybraliśmy do przechowywania naszego projektu, możemy zobaczyć kilka plików oraz folder public. Właśnie ten folder interesuje nas najbardziej, bo wewnątrz public będą przechowywane wszystkie pliki naszej strony. Automatycznie utworzone pliki. Wewnątrz public usuwamy automatycznie utworzony plik index.html i przenosimy w to miejsce pliki tworzące naszą stronę: index.html styles.css temperatura.js wilgotnosc.js login.js led.js Zawartość katalogu public. Zanim wyślemy naszą stronę w świat, możemy podejrzeć jak będzie wyglądała. W tym celu w wierszu poleceń, z ustawioną ścieżką naszego projektu, wpisujemy: firebase serve Możemy podejrzeć naszą stronę wpisując w przeglądarce: http://localhost:5000 Podgląd strony przed wrzuceniem do sieci. Jeżeli wszystko się zgadza, możemy wrzucić naszą stronę na serwer poleceniem: firebase deploy Jeżeli wszystko się uda, w konsoli powinniśmy zobaczyć adres url, pod którym jest dostępna nasza strona. Domyślnie jest to: https://id-projektu.web.app Udało się! Nasza strona jest już dostępna publicznie 🙂 Możemy zarządzać kolejnymi wersjami naszej strony z poziomu konsoli Firebase. Konsola Firebase. Gotowe! Nasza strona jest już dostępna publicznie. Na razie jest ona bardzo prosta, ale istnieje wiele możliwości jej rozwoju np.: personalizacja wyników dla różnych użytkowników logowanie za pomocą mediów społecznościowych poprawienie designu kolory kafelków zależne od wartości temperatury i wilgotności Teraz zostało nam tylko umieścić czujnik wilgotności w doniczce i nasz pierwszy projekt Firebase jest gotowy 🙂 PODSUMOWANIE Tym optymistycznym akcentem kończymy naszą wycieczkę do krainy Firebase. To była ostatnia część naszego kursu, w której podsumowaliśmy zdobytą wiedzę i nauczyliśmy się korzystać z Hostingu Firebase. Mam nadzieję, że chociaż trochę przybliżyłem Ci temat Firebase. Jest to wspaniałe narzędzie, które daje mnóstwo możliwości. Zachęcam do samodzielnego zgłębienia tematu, bo kwestie, które poruszyłem są tylko wierzchołkiem góry lodowej. PS. Jeśli masz pomysł do czego można zastosować Firebase, koniecznie podziel się nim w komentarzu 🙂 Ten artykuł jest częścią serii "Firebase w zastosowaniach IoT" #1 - Czym jest Firebase? Jak zacząć? #2 - Firebase z ESP32 i ESP8266 #3 - Wyświetlanie danych użytkownikowi poprzez stronę internetową #4 - Projekt praktyczny, Hosting (właśnie to czytasz) 5 Cytuj Link do komentarza Share on other sites More sharing options...
Treker (Damian Szymański) Marzec 22, 2021 Udostępnij Marzec 22, 2021 @Karrol wpis został właśnie zaakceptowany, więc już widoczny publicznie 🙂 Cytuj Link do komentarza Share on other sites More sharing options...
Wiktor2019 Kwiecień 27, 2021 Udostępnij Kwiecień 27, 2021 @Karrol Super artykuł ale czy wiesz może jak zrobić na Firebase aktywny wykres danych np. z ostatnich 7 dni? (chodzi o to by dane były wyświetlane na hostowanej stronie na wykresie) Cytuj Link do komentarza Share on other sites More sharing options...
Karrol Kwiecień 27, 2021 Autor tematu Udostępnij Kwiecień 27, 2021 Cześć @Wiktor2019 , wydaje mi się, że sam Firebase nie oferuje takiej funkcji, ale można użyć jakiejś biblioteki JavaScript do obsługi wykresów. Wtedy nasłuchujemy zmian w bazie i aktualizujemy wykres. Mało piszę w JS, więc nie umiem polecić jakiejś konkretnej, ale myślę, że bez problemu w internecie jakąś znajdziesz. Pisanie strony od zera w JS to zaleta i jednocześnie wada FB. Zaleta, bo nie masz narzuconych ograniczeń np. co do wyglądu wykresu (możesz użyć dowolnej biblioteki jaka istniej lub napisać własną). Wada, bo nie masz gotowego "przeciągnij i upuść", więc musisz sam znaleźć i skonfigurować odpowiednie rozwiązanie (chociaż możemy to traktować jako zaletę, bo dzięki temu więcej się nauczysz 😉 ). 1 1 Cytuj Link do komentarza Share on other sites More sharing options...
Polecacz 101 Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Produkcja i montaż PCB - wybierz sprawdzone PCBWay! • Darmowe płytki dla studentów i projektów non-profit • Tylko 5$ za 10 prototypów PCB w 24 godziny • Usługa projektowania PCB na zlecenie • Montaż PCB od 30$ + bezpłatna dostawa i szablony • Darmowe narzędzie do podglądu plików Gerber Zobacz również » Film z fabryki PCBWay
Pomocna odpowiedź
Dołącz do dyskusji, napisz odpowiedź!
Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!