Skocz do zawartości

Leoneq

Użytkownicy
  • Zawartość

    111
  • Rejestracja

  • Ostatnio

  • Wygrane dni

    4

Leoneq wygrał w ostatnim dniu 14 marca

Leoneq ma najbardziej lubianą zawartość!

Reputacja

101 Mistrz

2 obserwujących

O Leoneq

  • Ranga
    5/10
  • Urodziny 23.02.2004

Informacje

Ostatnio na profilu byli

601 wyświetleń profilu
  1. Dodaje się te argumenty dla bezpieczeństwa, podczas ładowania skryptów np. z CDN. Integrity kontroluje czy plik nie został zmieniony (np. podczas ataku), a crossorigin informuje przeglądarkę o tym, kto chce zasoby (no, a przynajmniej ja tak to sobie tłumaczę). Anonymous nie potrzebuje potwierdzenia tożsamości, a use-credentials wymaga dodatkowych informacji do ustalenia tożsamości. Jeżeli chcesz dołączyć pliki lokalne, np. z dysku czy FS - nie przejmuj się tymi argumentami. Myślę że @ethanak ci to lepiej wytłumaczy.
  2. Ekspertem programowania nie jestem. (String)x napisałem, ponieważ podobnie konwertowałem zmienne do inta: (int)x - kwestia przyzwyczajenia. Sprawdziłem obie wersje kodu, twoją i moją, i obie poprawnie się kompilują, i działają.
  3. Podobnie jak Soyer, mam wrażenie że samoświadomej AI nie stworzymy nigdy. Co prawda, możemy tworzyć systemy udające AI do tego stopnia, że pomyślimy że maszyna już ma samoświadomość - lecz to tylko efekt. Maszyna tak naprawdę jest automatem - nie jest autonomiczna. Nawet najlepsze roboty próbują być autonomiczne - czyli przede wszystkim zapobiegają utracie tej autonomiczności. Bardzo dobrze pokazują to roboty Boston Dynamics - zawsze będą dążyły do wykonania celu, ale nigdy nie będą świadome jaki cel mają - nie wiedzą, że właśnie popycha je człowiek dla zabawy, tylko że jest kolejna przeszkoda do pokonania. Nie wiedzą że przenoszą bombę - możemy jedynie zrobić algorytmy, które to będą udawać. Takie trochę masło maślane, ale ja też uważam że maszyna nie wiadomo jak mądra zawsze będzie automatem. Prędzej nam do połączenia autonomicznego mózgu z automatem, niż tworzyć mózg od nowa.
  4. statnio zrobiliśmy sobie smartdom z kilkoma czujnikami i obsługą przez przeglądarkę. Dzisiaj zajmiemy się bardziej frontendem, aby strona była bardziej przyjazna użytkownikom, oraz dodamy małego szpiega, który będzie nam przekazywał temperaturę do dużego ESP. Spis treści serii artykułów: 1. Omówienie, i przygotowanie środowiska 2. Zapoznanie z nowym środowiskiem, praca jako Arduino, prosty serwer WWW 3. Przyspieszony kurs na webmastera 4. Wykresy, zapis do SPIFFS, mini smart-dom 5. Odbiór danych z przeglądarki, stałe IP, łączenie modułów ESP Przekazywanie danych do ESP Teraz zadanie mamy takie, żeby stworzyć pole tekstowe i koło do wyboru kolorów. Tekst z textboxa wyświetlimy na LCD, a na wybrany kolor zaświecimy WS2812. Zacznijmy od tego prostszego, czyli tzw. textboxa (text field, pole tekstowe). W HTMLu mamy na to gotowy znacznik: <input type="textbox" value="koronaferie 2020" id="text"></input> Znacznik <input> to wejście ze strony użytkownika. Możemy także mieć wejście typu checkbox, radius, itd. Textbox to nasze pole tekstowe, a "value" to domyślny tekst naszego textboxa. No dobra, to było dość proste. Dane będziemy wysyłać do ESP dzięki metodzie get. Polega ona na wysyłaniu argumentów przy wywołaniu podstrony - u nas /set. Najlepiej będzie zrobić zmienną z całą stroną, i dopiero potem ją wysłać: var set = '/set?' + 'txt=' + $('#text').val(); $.post(set); Po wpisaniu "ip/set" dajemy znak zapytania - czyli wysyłamy już argumenty. Argumenty wysyłamy w formie "nazwa=dane". Tutaj będzie to "txt=", a dane to odczytanie wartości z textboxa o id #text. Kolejne argumenty zaraz dodamy - będziemy je oddzielać znakiem "&". Po przygotowaniu zmiennej wysyłamy żądanie. Najlepiej to umieścić w setInterval. Zostało nam zrobić kolejną podstronę. server.on("/set", setParams); void setParams() { String text = server.arg("txt"); lcd.print(text); server.send(200, "text/html", ""); //Send web page } Tworzymy zmienną "text" w której przechowujemy argument dla "txt". Wysyłamy jak zwykle kod 200, strony nie trzeba wysyłać - więc pozostawiamy pustą stronę. Spróbuj teraz załadować kod, i zmienić tekst w textboxie. Powinien się zmieniać także na wyświetlaczu, z niewielkim opóźnieniem. Czas na odrobinę magii Dodanie kolejnych argumentów nie będzie problemem. Dlatego to sobie zostawimy na później, a teraz zajmiemy się problemem "skąd wziąć argumenty". Dodamy sobie plugin "Spectrum" - dzięki niemu, będziemy mogli w łatwy i przyjemny sposób wybrać kolor naszych ledów. Szczegóły znajdziecie tutaj. Dodajemy zatem plik css na górze i js na dole strony: <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/spectrum-colorpicker2@2.0.0/dist/spectrum.min.css"> <script src="https://cdn.jsdelivr.net/npm/spectrum-colorpicker2@2.0.0/dist/spectrum.min.js"></script> W lewej kolumnie dodajemy tekst "LED:" oraz znacznik input - tam będziemy wprowadzać kolory. Argument "value" to początkowy kolor - podany heksadecymalnie. <p>LED: <input id="kolorki" value='#276cb8'/></p> W kodowaniu na razie tyle co nic. Dodajemy jedynie informację (w document.ready, nie w setInterval), jaki ma być typ naszego wybieracza kolorów. $('#kolorki').spectrum({ type: "color" }); Po otworzeniu i wgraniu strony, ujrzymy takie cudo: Ale to dopiero połowa magii! Druga połowa będzie fizyczna. Wyjście naszego pola z kolorkami to tekst, dosłownie: rgba(r,g,b,a) Dlatego musimy sobie zrobić funkcję, która nam to rozbije na poszczególne komponenty. Kanał alpha będzie sterował jasnością WS. function spliceRGB(input) { var color = input.slice(input.indexOf('(') + 1, input.indexOf(')')); // "r, g, b, a" var colors = color.split(','); return { r: colors[0], g: colors[1], b: colors[2], a: colors[3] } } Jest to dość prosta funkcja. Na początku usuwamy "rgba(" oraz ")", potem rozbijamy tekst na kawałki oddzielone ",". Zwracamy 4 wartości: r, g, b i a. var r = spliceRGB($("#kolorki").val()).r; var g = spliceRGB($("#kolorki").val()).g; var b = spliceRGB($("#kolorki").val()).b; var i = (spliceRGB($("#kolorki").val()).a * 255); Tak będą wyglądały zmienne, które przekażemy ESP. Do funkcji podajemy wartość którą wybraliśmy, i dajemy zmiennej tylko dany kolor. Z racji tego, że kanał alpha ma wartości od 0 do 1, mnożymy go x255 aby uzyskać liczbę przyjazną ESP. var set = '/set?txt=' + txt + '&r=' + r + '&g=' + g + '&b=' + b + '&i=' + i; $.post(set); Tworzymy dość długą zmienną, która po kolei zawiera wszystkie te wartości. Na końcu najzwyczajniej je wysyłamy. Jako zadanie dodatkowe spróbuj zrobić przycisk do włączania i wyłączania wbudowanego LEDa, ale już nie przez podstrony a get. void setParams() { int r = server.arg("r").toInt(); int g = server.arg("g").toInt(); int b = server.arg("b").toInt(); int i = server.arg("i").toInt(); String text = server.arg("txt"); cls(); lcd.print(text); for(int index=0; index<3; index++) { leds.setPixelColor(index, r, g, b); leds.setBrightness(i); leds.show(); } server.send(200, "text/html", ""); } Tak wygląda cała funkcja odczytująca dane. Odzyskujemy wartości kolorów, oraz tekst. Piszemy tekst na wyświetlaczu, po czym dla każdej diody ustawiamy wybrane kolory i jasność. Na końcu informujemy, że wszystko poszło sprawnie. Spróbuj teraz to wszystko odpalić na telefonie, i wygodnie z łóżka pobawić się LEDami - bardzo fajna zabawa, polecam :3 Suwak alpha nie dajemy na max. wartość - inaczej wyjście dostaniemy w postaci heksadecymalnej, z której nie wyciągniemy kolorów tak łatwo. Ustaw alpha na mniejszą od 1! ESP01 Na początku napisałem, że układy ESP01 są fajne dla dedykowanych rozwiązań. Zrobimy sobie w takim razie małego ESP odczytującego temperaturę z drugiego pokoju, przekazujące dane do "dużego" ESP. W związku z tym, "usuwamy" wykres z DS: void handleData() { //ds.requestTemperatures(); //String strona = (String)ds.getTempCByIndex(0); String strona = "0"; strona += "\n"; strona += analogRead(PIN_FRES); Temperatura 1 z "wyłączonym" DS18B20. Żeby zaprogramować teraz małe ESP, proponuję zrobić sobie do tego programator ze schematu. Tak wygląda mój: Dzięki niemu nie trzeba kombinować z kablami - wystarczy wsadzić ESP i wrzucić program. Jeżeli mowa o programie, warto sobie powiedzieć znowu, co chcemy napisać. Najlepiej będzie, jeżeli małe ESP w ten sam sposób co przeglądarka, wyśle nam temperaturę. Robimy nowy projekcik: Otwieramy plik main.cpp. Zacznijmy od dołączenia podstawowych bibliotek: #include <Arduino.h> #include <OneWire.h> #include <DallasTemperature.h> #include <ESP8266WiFi.h> #include <ESP8266WebServer.h> Z racji tego, że poprzedni projekt był konwertowany z projektu Arduino, biblioteki tym razem zainstalowałem przez platformio: Następnie definiujemy piny. Przyjmijmy, że zrobimy kilka małych ESP i wsadzimy je do gniazdek - każde będzie miało czujnik i przekaźnik (tutaj LED). #define PIN_LED 2 #define PIN_DS 0 Następnie tworzymy obiekty: klienta, DS, serwera. Stworzyliśmy także 4 adresy: IP, bramy, maski, i DNS. Dzięki nich będziemy mogli ustawić stałe IP naszemu ESP - w przeciwnym razie, jeżeli się zmieni, będziemy musieli zmieniać program i przepisywać wszystko. Pierwszy adres ustalamy sami - najlepiej zmienić po prostu ostatnią liczbę. DNSy zostawiamy, ponieważ z nich nie korzystamy - ale bramę i maskę możemy uzyskać z konsoli windowsowej CMD: Default gateway to nasza brama, którą przepisujemy. Podobnie z maską - prawdopodobnie będziesz miał taką samą. WiFiClient client; OneWire ow(PIN_DS); DallasTemperature ds(&ow); ESP8266WebServer server(80); IPAddress staleIP(192, 168, 43, 90); IPAddress gateway(192, 168, 43, 1); IPAddress subnet(255, 255, 255, 0); IPAddress dns(8, 8, 8, 8); Pamiętajmy o SSID i haśle naszego wifi - podaj tam wartości Twojego Wifi. const char* ssid = ""; //nazwa const char* pass = ""; //hasło Teraz zrobimy funkcję setup. Nic tajemniczego chyba tu nie ma - IP będzie takie, jakie ustawiliśmy. Obsługujemy potem dwie strony: główną i /set. void setup() { pinMode(PIN_LED, OUTPUT); Serial.begin(115200); Serial.print("Lacze z wifi"); ds.begin(); WiFi.begin(ssid, pass); WiFi.config(staticIP, gateway, subnet, dns); while(WiFi.status() != WL_CONNECTED) { Serial.print('.'); delay(400); } delay(500); Serial.println("Polaczono! IP:"); Serial.println(client.localIP()); server.on("/set", setParam); server.on("/", mainPage); server.begin(); } W loopie nic nie musimy robić poza obsługą serwera. void loop() { server.handleClient(); } Na stronie głównej będziemy wysyłać dane w tej samej formie co "duże" ESP. Dzięki temu bez problemu będziemy mogli pobrać temperaturę. Dodaliśmy także nagłówek "Access control allow origin", który pozwoli nam na łączenie się dużemu ESP. void mainPage() { ds.requestTemperatures(); server.sendHeader("Access-Control-Allow-Origin", "*"); server.send(200, "text/html", (String)ds.getTempCByIndex(0)); } Z kolei funkcja obsługująca LEDa zawiera prostego ifa - jeżeli wysłaliśmy "led=on" to włączy, "led=off" to wyłączy, a cokolwiek innego ominie. void setParam() { String led = server.arg("led"); if(led == "on") digitalWrite(PIN_LED, LOW); if(led == "off") digitalWrite(PIN_LED, HIGH); server.sendHeader("Access-Control-Allow-Origin", "*"); server.send(200, "text/html", ""); } Cały kod zatem będzie wyglądał tak: #include <Arduino.h> #include <OneWire.h> #include <DallasTemperature.h> #include <ESP8266WiFi.h> #include <ESP8266WebServer.h> #define PIN_LED 2 #define PIN_DS 0 WiFiClient client; OneWire ow(PIN_DS); DallasTemperature ds(&ow); ESP8266WebServer server(80); IPAddress staleIP(192, 168, 43, 90); IPAddress gateway(192, 168, 43, 1); IPAddress subnet(255, 255, 255, 0); IPAddress dns(8, 8, 8, 8); const char* ssid = "dobre pomaranczowe"; //nazwa const char* pass = "smerfysmerfy"; //hasło void setParam() { String led = server.arg("led"); if(led == "on") digitalWrite(PIN_LED, LOW); if(led == "off") digitalWrite(PIN_LED, HIGH); server.sendHeader("Access-Control-Allow-Origin", "*"); server.send(200, "text/html", ""); } void mainPage() { ds.requestTemperatures(); server.sendHeader("Access-Control-Allow-Origin", "*"); server.send(200, "text/html", (String)ds.getTempCByIndex(0)); } void setup() { pinMode(PIN_LED, OUTPUT); Serial.begin(115200); Serial.print("Lacze z wifi"); ds.begin(); WiFi.begin(ssid, pass); while(WiFi.status() != WL_CONNECTED) { Serial.print('.'); delay(400); } delay(500); Serial.println("Polaczono! IP:"); Serial.println(client.localIP()); server.on("/set", setParam); server.on("/", mainPage); server.begin(); } void loop() { server.handleClient(); } Wgrywamy kod do "małego" esp, podłączamy wszystko, i uruchamiamy Serial Monitor. Niestety, moje ESP się zbuntowało: Na to wystarczy pobrać "Advanced IP Scanner", który był wspomniany w malinkowym kursie. Uzyskany adres IP wpisujemy w przeglądarkę: No, to teraz małe ESP ukrywamy w innym pokoju. Pamiętajmy jedynie, żeby dalej miało zasięg naszego routera. Przechodzimy do kodu dużego ESP: var temp = new XMLHttpRequest(); temp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var temperatura = this.responseText; document.getElementById('temp1').innerHTML = temperatura; addData(wykres, temperatura, 0); } }; temp.open("GET", "http://192.168.43.75", true); temp.send(); Obok całego kodu dla "strony" dodajemy praktycznie taki sam kod dla "temp". Pamiętaj, żeby w "temp.open()" wpisać IP małego ESP. Zostało nam z "dużej" funkcji usunąć stary kod (tutaj macie całą funkcję): if (this.readyState == 4 && this.status == 200) { var zawartosc = this.responseText; var dane = zawartosc.split("\n"); var temp1 = dane[0]; var fres = dane[1] / 4; var temp2 = dane[2]; var cis = dane[3] / 100; var odl = dane[4]; var btn = dane[5]; var color = 'rgba('+fres +','+(fres - 20)+','+(fres - 20)+','+'1)'; var r = spliceRGB($("#kolorki").val()).r; var g = spliceRGB($("#kolorki").val()).g; var b = spliceRGB($("#kolorki").val()).b; var i = (spliceRGB($("#kolorki").val()).a * 255); var txt = $('#text').val(); var set = '/set?txt=' + txt + '&r=' + r + '&g=' + g + '&b=' + b + '&i=' + i; document.getElementById('temp1').innerHTML = temp1; document.getElementById('temp2').innerHTML = temp2; document.getElementById('light').style.color = color; document.getElementById('cis').innerHTML = cis; document.getElementById('odl').innerHTML = odl; $.post(set); if(!btn) document.getElementById('drzwi').innerHTML = 'otwarte'; else document.getElementById('drzwi').innerHTML = 'zamknięte'; addLabel(wykres, etykieta); addData(wykres, temp2, 1); popData(wykres, 0); } }; Wgrywamy kod. Uruchamiamy oba ESP, patrzymy czy IP się nie zmieniło (jeżeli jednak nie dałeś stałego IP), i wszystko powinno działać. Jeżeli chodzi o kurs ESP - to chyba tyle. Myślę, że dzięki temu kursowi teraz będziesz mógł postawić swój własny smartdom (lub jakiekolwiek inne urządzenie IoT). Nauczyliśmy się stawiać dynamiczne i responsywne strony WWW, odczytywać i zapisywać dane zarówno przez przeglądarkę, i FS, i ogólnie udało nam się z ESP zrobić "arduino". Możemy nawet zrobić całą sieć ESP01, gdzie każde ESP może sterować chociażby przekaźnikiem - i komunikować się do dużego ESP, lub malinki. Tak wygląda mój setup: W razie jakichkolwiek pytań - śmiało pisz na dole. Zostawiam Ci jeszcze zipa z projektami do platformio, i bibliotekami - w razie gdybyś miał problemy z posklejaniem kodów w całość. Pamiętajcie - bądźcie kreatywni, i pochwalcie się poniżej swoimi projektami! kurs esp.zip Spis treści serii artykułów: 1. Omówienie, i przygotowanie środowiska 2. Zapoznanie z nowym środowiskiem, praca jako Arduino, prosty serwer WWW 3. Przyspieszony kurs na webmastera 4. Wykresy, zapis do SPIFFS, mini smart-dom 5. Odbiór danych z przeglądarki, stałe IP, łączenie modułów ESP
  5. Zamierzam taki zip dodać pod koniec kursu. Więc maks. do 15 marca powinieneś mieć całego zipa z projektem i bibliotekami.
  6. Jak ukażą się wszystkie odcinki, oczywiście tak zrobię. Kończę już kolejną część, ostatnia wyjdzie do 15 marca - i wtedy dodam spis treści, oraz ponumeruję artykuły.
  7. Hmm, o CSS Grid nie słyszałem. Zdecydowana większość stron z którymi miałem styczność miała Bootstrapa. Ale i tak nie ładujemy całego Bootstrapa do SPIFFS (chociaż możemy, żeby uniezależnić się od Internetu), tylko w kodzie mówimy skąd mamy Bootstrapa załadować do przeglądarki. Warto jednak wiedzieć o alternatywie.
  8. W teorii wszystko da się obsłużyć klawiaturą Myślę, że należałoby zaimplementować obsługę tego w jQuery, co może rzeczą trudną nie jest, ale ja Ci w tym już nie pomogę.
  9. No, dlatego mówię najczęściej. U mnie akurat z tym i bez tego działa, ale to opera która jest dość zaawansowaną przeglądarką. Dopiszę zatem żeby tego nie usuwać bo sie posypie i tyle.
  10. Przeglądarka najczęściej i tak poprawnie się odpali. Napisałem że doctype informuje o jakim rodzaju dokumentu mamy do czynienia, co chyba w kursie wystarczy.
  11. @ethanak, no chciałem XD Ten kawałek kodu mówi przeglądarce, jaki dokument ma "przetrawić". Można tam wpisać coś innego, że np. przeglądarka ma trawić HTML 4, ale my się zajmujemy tutaj HTML 5 który wymaga takiego a nie innego zapisu.
  12. W poprzedniej części, nauczyliśmy się kodować rozbudowane i responsywne strony. Mam nadzieję, że przeczytałeś co nieco z linków na dole, ponieważ teraz będziemy mocniej kodować, aby stworzyć jeszcze ładniejszą i jeszcze bardziej funkcjonalną stronę. Zrobimy nawet mały smartdom! Do dzieła! Spis treści serii artykułów: 1. Omówienie, i przygotowanie środowiska 2. Zapoznanie z nowym środowiskiem, praca jako Arduino, prosty serwer WWW 3. Przyspieszony kurs na webmastera 4. Wykresy, zapis do SPIFFS, mini smart-dom 5. Odbiór danych z przeglądarki, stałe IP, łączenie modułów ESP Wykresy Wcześniej mieliśmy specjalną podstronę, gdzie zapisywaliśmy temperaturę co jedną sekundę. Jeżeli chcielibyśmy dodać drugi termometr musielibyśmy dodać drugą podstronę, itd... Ale dzięki jQuery, możemy odczytać z jednej podstrony więcej informacji, niż tylko jedna! Zacznijmy od zmodyfikowania poprzedniego kodu. Na rozgrzewkę, zmieńmy nazwę /temp na /data. server.on("/data", handleData); //setup() void handleData() { ds.requestTemperatures(); cls(); lcd.print("Temperatura: "); lcd.println(ds.getTempCByIndex(0)); server.send (200, "text/html", (String)ds.getTempCByIndex(0)); } Następnie przejdziemy od razu do wyświetlenia wykresu. Do tego będziemy potrzebować biblioteki ChartJS. Dopisujemy zatem kilka linijek kodu: <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js" integrity="sha256-R4pqcOYV8lt7snxMQO/HSbVCFRPMdrhAFMH+vr9giYI=" crossorigin="anonymous"></script> <div class="col-sm"> <canvas id="wykres"></canvas> </div> Pierwsza linijka odpowiada za dołączenie biblioteki. W drugiej zaś, tworzymy nową małą kolumnę, w środku niej canvas. Canvas to znacznik wykorzystywany jako swego rodzaju płótno - będziemy tam rysować nasz wykres. Jak na razie w środku nie ma nic - ale zaraz coś namalujemy, dzięki nowej bibliotece. Pamiętajmy, że nową kolumnę umieszczamy obok pierwszej, w tym samym rzędzie! Jak się domyślasz, trzeba będzie dodać trochę kodu. Proponuję już nie używać znacznika <script>, tylko wszystkie nasze skrypty przenieść do nowego pliku. Dlatego, w folderze testowym, robimy nowy plik "skrypty.js". W środku przenosimy stary kawałek kodu: Cały kod JS w dokumencie HTML usuwamy. Znaczniki możemy zostawić, dopiszemy jedynie atrybut, gdzie jest nasz nowy plik. Przechodzimy do naszego nowo utworzonego pliku - zrobimy na razie wykres z stałych danych. Stwórzmy zatem tablice tych danych: var temperatura = [25, 20, 21, 22, 27]; var lata = ["1 rok", "2 rok", "3 rok", "4 rok", "5 rok"]; Przykładem tutaj będzie średnia temperatura pokoju na przestrzeni lat. Lata będą osią x (poziomą), temperatura y (pionową). Tablica labels przechowuje etykiety poszczególnych danych, var ctx = document.getElementById('wykres'); var wykres = new Chart(ctx, { type: 'line', data: { labels: lata, datasets: [ { label: 'Temperatura', data: temperatura, borderColor: 'rgba(255, 130, 150, 1', backgroundColor: 'rgba(255, 130, 150, 0.2)' } ] } }); Następnie przypisujemy nasze płótno do zmiennej. Kolejna zmienna to już stricte nasz wykres. Pierwszy atrybut mówi, że będzie to wykres liniowy (możemy zrobić kołowy, czy słupkowy). Potem zapisujemy dane wykresu: na początku definiujemy "labels" (etykiety) - to one będą naszą osią czasu. Datasets to paczki danych umieszczanych na tamtej osi - my mamy tylko jedną paczkę, temperatura. Przed każdą temperaturą wyświetlamy zatem tekst "Temperatura: ", informujemy jakie dane chcemy wyświetlić (po kolei!), następnie definiujemy jaki chcemy kolor kreski i kolor tła, jakie kreska wykreśli. Wrzucamy to wszystko do naszego dokumentu: $( document ).ready(function() { var temperatura = [25, 20, 21, 22, 27]; var lata = ["1 rok", "2 rok", "3 rok", "4 rok", "5 rok"]; var ctx = document.getElementById('wykres'); var wykres = new Chart(ctx, { type: 'line', data: { labels: lata, datasets: [ { label: 'Temperatura', data: temperatura, borderColor: 'rgba(255, 130, 150, 1', backgroundColor: 'rgba(255, 130, 150, 0.2)' } ] } }); setInterval(function() { var strona = new XMLHttpRequest(); strona.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { document.getElementById("data").innerHTML = this.responseText; } }; strona.open("GET", "/data", true); strona.send(); }, 1000); }); Dlaczego wszystko jest w "ready"? Dzięki temu, nasz kod wykona się tylko wtedy, kiedy wszystko się załaduje. Bo jak kod w jQuery miałby się wykonać bez jQuery? Jeżeli dokument już zapisałeś, spróbuj uruchomić stronę: A teraz spróbuj zmienić rozmiar okna przeglądarki, czy uruchomić wszystko na telefonie. Jak to wygląda? Właśnie dzięki responsywności, dostajemy możliwość "łatwego" zmieniania rozmiarów strony. Dlatego będzie ona dobrze czytelna i na komputerze, i na telefonie. Wrzućmy zatem wszystko co mamy w folderze testowym, do folderu /data, i wgrajmy na ESP. Dokonujemy szybkich zmian w kodzie: String getScript() { String strona; File file = SPIFFS.open("/skrypty.js", "r"); while(file.available()) strona+=(char)file.read(); file.close(); return strona; } //skrypty server.on("skrypty.js", handleSkrypty); void handleSkrypty() { server.send(200, "text/javascript", getScript()); } I wgrywamy całość na ESP. Jak widzimy, wszystko pięknie działa: Wykresy w czasie rzeczywistym Spróbujmy teraz wyświetlić "rzeczywiste" dane. Niestety, to już jest trochę trudne - lecz na pewno sobie poradzimy. Na początku, dopóki nie dostaniemy odczytów z termometru, będziemy mieli 0 stopni: var temperatura = [0, 0, 0, 0, 0]; To, ile damy tutaj zmiennych, będzie decydować o "szerokości" naszego wykresu. var ctx = document.getElementById('wykres'); var wykres = new Chart(ctx, { type: 'line', data: { labels: ['', '', '', '', ''], datasets: [ { data: temperatura, label: 'Temperatura', borderColor: 'rgba(255, 130, 150, 1', backgroundColor: 'rgba(255, 130, 150, 0.2)' } ] } }); Pierwszych 5 temperatur tak naprawdę nie ma, więc damy im puste etykiety. Reszta zostaje tak samo. Teraz dodamy dwie funkcje: dodającą wartość, i usuwającą ostatnią wartość. function addData(chart, label, data) { chart.data.labels.push(label); chart.data.datasets[0].data.push(data); chart.update(); } Pierwszy argument, to wykres - bo w końcu możemy ich mieć kilka na stronie. Drugi argument to etykieta, trzeci to wartość. Na początku "popychamy" stos etykiet, dodając kolejną. Podobnie robimy z danymi, a na końcu aktualizujemy wykres. function popData(chart, index) { chart.data.labels.splice(index, 1); chart.data.datasets[0].data.splice(index, 1); chart.update(); } Kolejna funkcja usuwa nam etykietę na podanym indeksie, a potem dane. var sekundy = 0; setInterval(function() { var strona = new XMLHttpRequest(); sekundy++; var etykieta = "" + sekundy + " sekunda"; strona.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var zawartosc = this.responseText; var dane = zawartosc.split("\n"); document.getElementById('data').innerHTML = dane[0]; addData(wykres, etykieta, dane[0]); popData(wykres, 0); } }; strona.open("GET", "/data", true); strona.send(); }, 1000); }); Tak wygląda cała nowa funkcja, która nam aktualizowała temperaturę. Z racji tego, że wykonuje się co sekundę, zrobimy od razu licznik tych sekund - stąd zmienna na początku. W funkcji zwiększamy zmienną o 1, a potem tworzymy etykietę. W środku funkcji pobierającej temperaturę dokonało się jednak najwięcej zmian. Zawartość całej strony /dane przechowujemy w zmiennej, którą potem rozdzielamy na linie co znak nowej linii. Dlaczego tak, a nie po prostu odczytać? Dzięki temu, na jednej podstronie możemy odczytywać bardzo wiele informacji, zamiast tworzyć nowe podstrony. Temperatura będzie zapisana na pierwszej linijce strony - wypisujemy ją na stronie głównej, potem dodajemy ją do wykresu (z etykietą), a starą temperaturę usuwamy. Jeżeli nie będziemy usuwać starych danych, po pewnym czasie całość zrobi się mocno nieczytelna. Przechodzimy do kodu; modyfikujemy funkcję handleData: void handleData() { ds.requestTemperatures(); cls(); lcd.print("Temperatura: "); lcd.println(ds.getTempCByIndex(0)); String strona = (String)ds.getTempCByIndex(0); strona += "\n"; //dodamy zaraz więcej danych! server.send (200, "text/html", strona); } Praktycznie nic się nie zmieniło, jedyne co to dodaliśmy zmienną strona przechowującą podstronę /data. Jest ona pisana w locie, co umożliwia wypisanie tam danych z czujników. Po każdym dodaniu linii, dodajemy znak nowej linii dla rozdzielenia danych. Wszystko wgrywamy do ESP, i sprawdzamy czy działa: Zapisywanie plików To teraz spróbujmy zapisać temperaturę do pliku, żeby potem "na żądanie" robić wykres. Np. co godzinę zapisywać temperaturę, robić średnią, i pokazywać na wykresie. Akurat to jest dość proste, wystarczy nowa funkcja: void writeToFile(String plik, String tekst) { File file = SPIFFS.open(plik, "w"); if (file.print(tekst)) { cls(); lcd.println("Zapisano pomyslnie!"); } else { cls(); lcd.println("Blad zapisu!"); } file.close(); } Pierwszym argumentem jest plik. Musimy sami utworzyć taki pusty plik, aby do niego zapisać różne dane. Drugi argument to oczywiście zawartość, którą chcemy zapisać. Jako zadanie domowe (no dobra, zadanie dodatkowe), spróbuj zrobić program który zapisze temperaturę, a następnie odczyta i zapisze na wykresie. Praca na wielu danych No, to jak mamy już wszystko do pracy na wielu danych, dodanie czujników powinno być czystą przyjemnością. Dlatego dodamy: HC-SR04 przycisk (kontakron, co chcesz) barometr BMP180 fotorezystor a nawet WS2812. A co. Scenariusz jest prosty: mierzę temperaturę, odległość, stan przycisku, ilość światła, wysyłam to do WWW, a z WWW dostaję informacje o kolorze WS2812. W tym momencie instalujemy wszystkie potrzebne biblioteki, dodajemy je na początku kodu, i dodajemy je do naszej funkcji obsługującej /data: void handleData() { ds.requestTemperatures(); String strona = (String)ds.getTempCByIndex(0); strona += "\n"; strona += analogRead(PIN_FRES); strona += "\n"; strona += bmp.readTemperature(); strona += "\n"; strona += bmp.readPressure(); strona += "\n"; strona += zmierzOdleglosc(); strona += "\n"; strona += digitalRead(PIN_BTN); //temperatura ds //fotorezystor //temperatura bmp //cisnienie //odleglosc //przycisk server.send (200, "text/html", strona); } Jedyne co się zmieniło, to dodanie kilku odczytów oddzielonymi znakami nowej linii. Po wgraniu tego do kodu, powinieneś zobaczyć takie coś na stronie /data: Pierwsza wartość to zatem temperatura, druga ilość światła, trzecia to też temperatura (uznajmy że z innego pokoju), czwarta to ciśnienie w paskalach, piąta to odległość zmierzona przez HC-SR04, ostatnia to odczyt z kontaktronu. Wygląda na to, że muszę popracować z HC... Ale dlaczego wszystko jest w jednej linii, tylko porozdzielane spacjami? Otóż to są nowe linie, co możemy zobaczyć w narzędziach deweloperskch: Aby przejść do nowej linii, musielibyśmy dać znacznik <br>. Jak widać, przeglądarka także "sama" dodała <html> i <body>. No, jeżeli już masz wszystkie czujniki gotowe, przejdziemy do strony. Odpalamy plik skrypty.js. Ale chwila! Zanim zaczniemy kodować, musimy sobie odpowiedzieć na pytanie "co kodować". Zawsze przed kodowaniem zadaj sobie to pytanie! Mamy 6 odczytów. Dwa z nich wyświetlimy na wspólnym wykresie, światło możemy wyrazić kolorem barwy, ciśnienie pokażemy w hPa obok "surowych" danych. Będziemy wyświetlali napis, czy drzwi zostały otwarte (kontaktron). No, to teraz możemy kodować. Zacznijmy od zapisu tych wszystkich danych: if (this.readyState == 4 && this.status == 200) { var zawartosc = this.responseText; var dane = zawartosc.split("\n"); var temp1 = dane[0]; var fres = dane[1]; var temp2 = dane[2]; var cis = dane[3] / 100; var odl = dane[4]; var btn = dane[5]; document.getElementById('data').innerHTML = dane[0]; addData(wykres, etykieta, dane[0]); popData(wykres, 0); } To jest if z funkcji "setInterval". Tak jak wcześniej tylko dodaliśmy zapis danych, tak i teraz tylko dodaliśmy odczyty. Aby wyświetlić temperatury na jednym wykresie, musimy najpierw zmodyfikować funkcje pop i push: function addData(chart, data, dset) { chart.data.datasets[dset].data.push(data); chart.update(); } function addLabel(chart, label) { chart.data.labels.push(label); chart.update(); } function popData(chart, index) { chart.data.labels.splice(index, 1); chart.data.datasets.forEach((dataset) => { dataset.data.splice(index, 1); }); chart.update(); } Komenda "znikająca" została teraz obramowana pętlą forEach. Wykonuje się ona tyle razy, ile razy występuje coś (dosłownie - dla każdego). Dla każdego zatem zestawu danych, usuwamy zmienną. Aby dodać daną, od teraz musimy powiedzieć do którego zestawu danych ją dodać. Dodawanie etykiety dałem do osobnej funkcji, aby nie robić duplikatów etykiet. A tak wygląda nasz nowy wykres: var temperatura = [0, 0, 0, 0, 0]; var temperatura2 = [0, 0, 0, 0, 0]; var ctx = document.getElementById('wykres'); var wykres = new Chart(ctx, { type: 'line', data: { labels: ['', '', '', '', ''], datasets: [ { data: temperatura, label: 'Temperatura w pokoju', borderColor: 'rgba(255, 130, 150, 1)', backgroundColor: 'rgba(255, 130, 150, 0.2)' },{ data: temperatura2, label:'Temperatura w drugim pokoju', borderColor: 'rgba(23, 126, 214, 1)', backgroundColor: 'rgba(23, 126, 214, 0.2)', } ] } }); Dodaliśmy drugi zestaw danych. Ma on niebieską kreskę, i pusty zestaw początkowych danych. Etykiety teraz będą różne - dla dwóch różnych pokoi. Zostało nam dodać dodawanie i odejmowanie danych z wykresu: document.getElementById('temp1').innerHTML = temp1; document.getElementById('temp2').innerHTML = temp2; addLabel(wykres, etykieta); addData(wykres, temp1, 0); addData(wykres, temp2, 1); popData(wykres, 0); I szybkie zmiany w HTMLu: <p>Temperatura w pokoju: <span id="temp1">N/A</span></p> <p>Temperatura w drugim pokoju: <span id="temp2">N/A</span></p> Spróbujmy teraz to wgrać i zobaczyć efekt. Piękny efekt. No, to teraz może pobawimy się FontAwesome? Wchodzimy tutaj, i szukamy ikonki termometru: Niestety, dla nas dostępne są tylko te widoczne ikony - te szare są dla wersji płatnych. Wybierzmy ikonkę która nam najbardziej przypadła do gustu: Klikamy "Start using this icon", i kod kopiujemy tam, gdzie ma być ikonka. Ja sobie dam ją przed temperatury: <p>Temperatura w pokoju: <i class="fas fa-thermometer-three-quarters"></i> <span id="temp1">N/A</span></p> <p>Temperatura w drugim pokoju: <i class="fas fa-thermometer-three-quarters"></i> <span id="temp2">N/A</span></p> I po wgraniu dostajemy takie ładne ikonki: To teraz możemy dodać pozostałe dane. Spróbujemy zmienić kolor za pomocą jQuery - lecz musimy jakoś przekonwertować wartość z fotorezystora do koloru. Kolor możemy zapisać heksadecymalnie (#ffffff to biały), w formacie rgba (255, 255, 255, 255 to biały nieprzezroczysty), czy podać stałą (red - czerwony). Tutaj proponuję wyświetlić kolor w skali szarości. Dlatego odczyt od razu podzielimy przez 4, by mieć wartości 0-255: var fres = dane[1] / 4; 'Następnie stworzymy zmienną, która będzie przechowywać tekst atrybutu color. Dlaczego odejmowanie jest w nawiasach? Otóż bez nich, odjęlibyśmy tekst "20". Dlatego zapis 2+2 da nam 22, a zapis (2+2) da nam 4. Ale... dlaczego w ogóle odejmujemy? Jeżeli światło będzie białe, nie będziemy go widzieli na stronie z białym tłem. var color = 'rgb('+fres +','+(fres - 20)+','+(fres - 20)+')'; Oraz zmieniamy co sekundę kolor: document.getElementById('light').style.color = color; No dobra, ale czego zmieniamy? Ja sobie dodałem tekst "Światło" oraz ikonkę kwadratu z przypisanym id "light". To on będzie zmieniał kolor. <p>Światło: <i class="fas fa-square" id="light"></i> Teraz dodamy ciśnienie, oraz jednostki dla temperatur. W googlu szukamy "degree symbol" lub kopiujemy stąd: ° Cała kolumna będzie wyglądać tak: <div class="col-sm"> <h1>Centralna Baza Dowodzenia</h1> <p>Dumnie wspierana przez ESP8266</p> <a type="button" class="btn btn-success" href="/on">ON</a> <a type="button" class="btn btn-danger" href="/off">OFF</a> <p>Temperatura w pokoju: <i class="fas fa-thermometer-three-quarters"></i> <span id="temp1">N/A</span>°C</p> <p>Temperatura w drugim pokoju: <i class="fas fa-thermometer-three-quarters"></i> <span id="temp2">N/A</span>°C</p> <p>Światło: <i class="fas fa-square" id="light"></i></p> <p>Ciśnienie: <span id="cis"></span>hPa</p> <p>Odległość: <span id="odl"></span>cm</p> <p>Drzwi są <span id="drzwi"></span></p> </div> Pomiędzy znacznik </span> a </p> dodaliśmy tekst, z jednostką. Podobnie z ciśnieniem. Przechodzimy zatem do kodowania: document.getElementById('cis').innerHTML = cis; document.getElementById('odl').innerHTML = odl; if(!btn) document.getElementById('drzwi').innerHTML = 'otwarte!'; else document.getElementById('drzwi').innerHTML = 'zamknięte.'; Robimy prostego ifa, który nam wyświetli tekst "zamknięte", jeżeli odczyta HIGH. W przeciwnym razie wyświetli tekst "otwarte". Sprawdźmy, czy kod działa: Można powiedzieć, że zrobiliśmy już mini smartdom. Mamy sterowanie z poziomu aplikacji oraz wiele odczytów. W następnym odcinku spróbujemy dodać małego ESP, aby zbierał odczyty z innego pokoju, użyjemy WS2812, oraz zrobimy małe podsumowanie. Bądźcie kreatywni! Spróbujcie urozmaicić swój smartdom, stronę, i pokażcie w komentarzach wasze konstrukcje! Spis treści serii artykułów: 1. Omówienie, i przygotowanie środowiska 2. Zapoznanie z nowym środowiskiem, praca jako Arduino, prosty serwer WWW 3. Przyspieszony kurs na webmastera 4. Wykresy, zapis do SPIFFS, mini smart-dom 5. Odbiór danych z przeglądarki, stałe IP, łączenie modułów ESP
  13. W poprzednim odcinku nauczyliśmy się włączać i wyłączać diodę LED przez Internet. Jedyne co wtedy zawierała strona, to krótki wyraz. Jeżeli chcemy, aby nasza strona była ładna, przejrzysta, i przede wszystkim - w 100% funkcjonalna, musimy się nauczyć podstaw HTMLa. Ale czym ten HTML jest? W skrócie, jest to język w którym piszemy strony WWW. To, jak strona ma wyglądać - piszemy w CSS, a co strona ma robić (animacje, skrypty) - piszemy w JavaScript (to pod żadnym pozorem nie jest zwykła Java). Zatem, zaczynamy! Spis treści serii artykułów: 1. Omówienie, i przygotowanie środowiska 2. Zapoznanie z nowym środowiskiem, praca jako Arduino, prosty serwer WWW 3. Przyspieszony kurs na webmastera 4. Wykresy, zapis do SPIFFS, mini smart-dom 5. Odbiór danych z przeglądarki, stałe IP, łączenie modułów ESP Plik index.html Proponuję zacząć od stworzenia strony na komputerze. Nie jest to trudne, a dopiero kiedy uznamy że strona jest gotowa - wgramy ja do mikrokontrolera. Stwórzmy zatem nowy folder: W środku tworzymy nowy plik: index.html. To będzie nasza strona główna. Otwieramy, i na razie wklejamy to: <!DOCTYPE html> <html> <body> <h1>Hello</h1> <p>there</p> </body> </html> Zapisujemy, i otwieramy w przeglądarce: Spróbujmy zrobić to samo na ESP. Możemy to zrobić na kilka sposobów: zrobić Stringa i co linijkę dodawać kod w htmlu (oof) wgrać na zewnętrzną pamięć i odczytać z np. karty sd (przekombinowane) na stałe zakodować stronę (to nie jest nasz cel) użyć SPIFFS (very nice) SPIFFS Już wyjaśniam co to SPIFFS. W pierwszym artykule wspomniałem, że na płytkach ESP są dwa układy: mikrokontroler i pamięć flash. Bardzo dobrze to widać na ESP01. Do tej drugiej kostki wgrywamy nasz program - ale co, gdyby na to wgrać coś innego? Espressif przewidział tą możliwość, i zrobił takie coś jak SPI Flash File System (SPIFFS). Oznacza to tyle, że dostajemy w pełni funkcjonalny (i wbudowany!) system plików. Jaka jest jego pojemność? Definiujemy to w konfiguracji płytki - zwykle dzielimy pamięć na kod i SPIFFS (dla NodeMCU może to być 2 i 2MB). Otwieramy folder z naszym projektem Platformio. Na tym samym poziomie co plik platformio.ini, tworzymy folder /data. Pliki w środku będą skopiowane do ESP. Przenieśmy zatem naszego indexa do /data, i odpalmy VS Code: VS Code jest na tyle mądry, że obsługuje składnię wielu języków - w tym i HTMLa. Po otworzeniu powinniśmy zobaczyć coś takiego: Aby wgrać naszą stronę, przechodzimy do zakładki Terminal → Run Task. Szukamy tam "Upload File System Image" - i czekamy aż się wgra nasz plik. Musimy teraz wprowadzić lekkie modyfikacje do kodu. Zaczynamy od biblioteki obsługującej nasz system plików. #include <FS.h> Zróbmy sobie teraz funkcję (tudzież zmienną) która zwróci nam całą stronę. W środku "getPage" robimy kolejnego stringa, do którego dodajemy po kolei znak po znaku. Plik musi być najpierw otwarty, potem zamknięty - możemy obsłużyć 1 plik naraz. Na końcu zwracamy stringa ze stroną. Co robi "r"? Spróbuj sam zobaczyć, naciskając Spację+CTRL. String getPage() { String strona; File file = SPIFFS.open("/index.html", "r"); while(file.available()) strona+=(char)file.read(); file.close(); return strona; } Na samym początku funkcji setup odpalamy bibliotekę. Ja to dodatkowo obudowałem w wyświetlanie wiadomości na LCD: lcd.setCursor(0, 0); lcd.print("SPIFFS "); if(!SPIFFS.begin()) lcd.println("x"); else lcd.println("√"); Na koniec zostało tylko dopisać do funkcji obsługujących klienta, że strona ma być brana z getPage. void handleRoot() { server.send (200, "text/html", getPage()); } void handleOn() { digitalWrite(PIN_LED, LOW); server.send(200, "text/html", getPage()); } void handleOff() { digitalWrite(PIN_LED, HIGH); server.send(200, "text/html", getPage()); } Wgrywamy program, i wpisujemy adres IP do przeglądarki: Można powiedzieć, że pierwsza część backendu (piękny slang informatyczny) za nami. Teraz zajmijmy się frontendem. Specjalnie do tego przygotujemy sobie DS18B20, do znalezienia w zestawach kursu na malinke lub Arduino II. Zrobimy sobie mały inteligenty domek. Nie bój się dodać kilka innych czujników, o ile wiesz co będziesz robił. Pierwsze kroki z HTML W językach z rodziny C każdą funkcję zapisujemy przez jej nazwę, i otwierając klamerki. W HTMLu piszemy <x> aby zacząć pewien element, </x> aby go zakończyć. Przeanalizujmy zatem nasz kod "hello there": <!DOCTYPE html> <html> <body> <h1>Hello</h1> <p>there</p> </body> </html> Na początku mamy doctype. Informuje on przeglądarkę o tym, z jakim rodzajem dokumentu ma ona do czynienia. Piszemy wszystko w HTML5, w starszym HTMLu deklaracja ta by wyglądała inaczej. Gdybyśmy tego nie dali, przeglądarka mogłaby pomyśleć że ma do czynienia z HTML 3 lub starszym. Po definicji, otwieramy cały dokument - <html>. Otwieramy także <body> - tam zapisujemy wszystko to, co widzimy. Mamy zatem już szkielet strony - możemy dodać nagłówek <h1> oraz tekst <p>. Ale nikt chyba nie lubi Times New Romana (chyba, że robimy gazetę) - spróbujmy zmienić czcionkę. Kiedyś, dawno temu wszystko się kodowało w HTMLu (strony z 2004/5 mocno to pokazują). Dzisiaj używamy CSS do określania stylów - Obok index.html tworzymy zatem style.css. W środku możemy zapisywać, co elementy o danym ID lub danej klasie będą miały za właściwości. h1 { font-family: Arial, Helvetica, sans-serif; } #idd { color: #ccc; } .klasa { font-size: 10px; } Plik style.css to zbiór wszystkich naszych właściwości danych elementów. W powyższym kodzie, na początku mówimy że każdy h1 będzie pisany Arialem. Elementy z id #idd będą miały kolor szarawy, a z klasą .klasa będą wysokie na 10px. Od razu powiem, czym klasa różni się od id: jeżeli mamy przyciski, które się mają różnić tylko kolorem, musielibyśmy robić prawie dwa identyczne id, a na klasach - tylko jedna pod przycisk, i dwie małe pod kolor. Ba, możemy potem klasy 'kolor' wykorzystać gdzie indziej (np. do tekstu). Zobaczmy teraz, czy style działają. Najpierw musimy jednak zmodyfikować indexa: <head> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> <h1>Hello</h1> <p class="klasa">there</p> <p id="idd">general kenobi</p> </body> Pojawił się znacznik <head>. Tam często linkujemy dokumenty niezbędne do funkcjonowania strony, oraz definiujemy takie rzeczy jak nazwa, ikona, charset, itd - jak mówiłem, body widzimy, head nie. Zauważyłeś także, że przy znacznikach <p> są przypisane klasy i id. Możemy w ten sam sposób (przy znaczniku) także podać kolory i inne właściwości, ale od tego się odchodzi (dlatego wam pokazuję css). Na końcu, musimy jeszcze podać obsługę żądania ip/style.css do kodu. Dodajemy drugiego stringa czytającego style.css, oraz obsługę dla klienta - analogicznie jak w indexie. String getStyle() { String strona; File file = SPIFFS.open("/style.css", "r"); while(file.available()) strona+=(char)file.read(); file.close(); return strona; } void handleStyle() { server.send(200, "text/css", getStyle()) } //setup() server.on("/style.css", handleStyle); Wgrywamy kod, pliki i sprawdzamy, czy działa: Spróbujmy teraz zaimplementować JavaScript. Daje nam to możliwość "podstawiania" zmiennych do tekstu i dynamiczną zmianę strony, bez jej ponownego wczytywania. Podłącz swój czujnik i napisz kod (poważnie, jeżeli termometr zrobiłeś na Arduino, to na ESP nie będziesz mieć problemu). Ja wykorzystam Dallas DS18B20, dostępnego w forbotowym zestawie z malinką i Arduino - jest to cyfrowy termometr. Link do artykułu jest tutaj, a jak już mamy do niego kod, czas modyfikować stronę. Pouczymy się teraz czystego HTMLa (no, a bardziej frameworków). Podstawowe znaczniki HTML Znaczników mamy kilka(naście). Najbardziej będą potrzebne: <p> - tekst <hX> - nagłówek, za X dajemy numer (wielkość) <div> - pusty blok <button> - przycisk <a> - link <img /> - zdjęcie (uwaga: zdjęcia nie otwieramy, tylko od razu zamykamy) <script>, <style> - tam umieszczamy fragmenty kodu JS lub CSS <html>, <body>, <head> - tworzą szkielet strony Aby te znaczniki ostylować, możemy "surowo" to zrobić w HTMLu, dodać własne CSS, lub skorzystać z gotowych frameworków. Ale jak z gotowych? Otóż Bootstrap to framework zawierający zestawy klas, umożliwiające stworzyć ładną, i responsywną stronę. Oznacza to tyle, że kodowania będzie mało, a dostaniemy własną, elegancką stronę do wyświetlenia na wszystkim co ma dostęp do internetu. Będziemy chcieli stworzyć ładną stronę z dwoma przyciskami, i etykietą pokazującą temperaturę. Bootstrap Otwieramy plik indexa. Wszystko w <head> usuwamy i wklejamy to: <meta charset="utf-8"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"> <title>domek</title> Pierwszy znacznik informuje stronę o zestawie znaków - tutaj utf-8 (umożliwi to nam wyświetlanie polskich znaków). Następnie linkujemy arkusz bootstrapa, po czym nadajemy naszej stronie nazwę "domek". Przydałoby się jeszcze dodać FontAwesome, z tego linku (trzeba się zarejestrować). Pozwoli nam to na wyświetlanie różnych ikonek. Przechodzimy do samego dołu dokumentu. Tam dopisujemy: <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script> </body> </html> Pierwszy skrypt ładuje jQuery (lekki javascript), następny Poppera (pomaga bootstrapowi w responsywności), ostatni skrypt uzupełnia Bootstrapa. Dlaczego je ładujemy na końcu? To są największe pliki do załadowania, i często da się skorzystać ze strony bez nich. Jest to optymalizacja ładowania strony przy wolnym łączu. Szkielet strony W boostrapie tak wygląda struktura strony: Mistrz painta w akcji. Strona zawiera kontenery (.container), rzędy (.row), i kolumny (.col-*-*). Umożliwia to stworzenie eleganckiej i responsywnej strony w kilku łatwych krokach. Chcemy mieć kontener, rząd i kolumnę: <div class="container"> <div class="row"> <div class="col-sm"> </div> </div> </div> "container" to klasa kontenera, "row" rzędu, a "col-sm" małej kolumny. Teraz dajmy nagłówek, opis, etykietę i dwa przyciski: <div class="container"> <div class="row"> <div class="col-sm"> <h1>Centralna Baza Dowodzenia</h1> <p>Dumnie wspierane przez ESP8266</p> <a type="button" class="btn btn-success" href="/on">ON</a> <a type="button" class="btn btn-danger" href="/off">OFF</a> <p>Temperatura: <span id="temp"> N/A </span></p> </div> </div> </div> Elementy <h1> oraz <p> są domyślnie w Bootstrapie ostylowane. Tworzymy dwa linki o typie przycisków (odsyłam do kursu HTML na dole strony), oraz przypisuję im klasy przycisku, potem konkretnie o kolorach zielony i czerwony. Odsyłają one do podstron /on i /off, tak jak było wcześniej. Na koniec dajemy tekst, oraz etykietę z id "temp" która się przyda do skryptowania. Spróbujmy zatem to wgrać do SPIFFS i uruchomić: Jeżeli chcemy podejrzeć strukturę strony, wystarczy kliknąć prawy przycisk myszy i wybrać Zbadaj Element (chrome/opera). Pierwsze przygody z jQuery Uznajmy, że skończyliśmy stronę. Teraz spróbujmy wyświetlić temperaturę. na samym dole, przed gotowymi skryptami, otwieramy <script>. <script> </script> Dodajemy funkcję setInterval, wykonującą się co sekundę. <script> setInterval(function() { }, 1000); </script> W tej funkcji dodajemy ten kod: var strona = new XMLHttpRequest(); strona.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { document.getElementById("temp").innerHTML = this.responseText; } }; Tworzymy zmienną strona, która wywołuje żądanie do strony. Kiedy mamy naszą stronę, sprawdzamy, czy wszystko się poprawnie załadowało (status 200). Jeżeli tak, zmieniamy tekst. strona.open("GET", "/temp", true); strona.send(); Na koniec tworzymy żądanie, a potem je wysyłamy. Całość zatem powinna wyglądać tak: <script> setInterval(function() { var strona = new XMLHttpRequest(); strona.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { document.getElementById("temp").innerHTML = this.responseText; } }; strona.open("GET", "/temp", true); strona.send(); }, 10000); </script> Zostało nam wgrać nową stronę do SPIFFS, i modyfikujemy kod: void handleTemp() { ds.requestTemperatures(); cls(); lcd.print("Temperatura: "); lcd.println(ds.getTempCByIndex(0)); server.send (200, "text/html", (String)ds.getTempCByIndex(0)); } server.on("/temp", handleTemp); Standardowo już, w setupie dodajemy obsługę /temp, a w funkcji ją zwracamy i wypisujemy przy okazji temperaturę na wyświetlaczu. Efekt: Przyciski działają, mamy temperaturę - zadanie wykonane. Spróbuj teraz dodać sterowanie np. serwem, i odczyt z kontaktronu. W następnym odcinku zgłębimy Bootstrapa, FontAwesome i podepniemy DNSy. Powodzenia! Jeżeli chcesz się bardziej pouczyć HTMLa, CSS i frameworków, masz tutaj garść przydatnych linków (po polsku!): Bootstrap jQuery HTML CSS Spis treści serii artykułów: 1. Omówienie, i przygotowanie środowiska 2. Zapoznanie z nowym środowiskiem, praca jako Arduino, prosty serwer WWW 3. Przyspieszony kurs na webmastera 4. Wykresy, zapis do SPIFFS, mini smart-dom 5. Odbiór danych z przeglądarki, stałe IP, łączenie modułów ESP
  14. Eclipse to chyba pod wszystko jest. Tam także wszystko chyba jest Swoją drogą, ostatnio zauważyłem że nawet w codeblocksach jest specjalny szablon pod projekt Arduino. Tak więc wybór środowiska jest i to duży.
  15. Właśnie, chcielibyście żebym także opisał programowanie ESP w lua? Uznałem że jak jest kurs Arduino nie ma sensu męczyć innego języka, ale jeżeli chcecie - nie ma problemu (pytanie do społeczności)
×
×
  • Utwórz nowe...