Skocz do zawartości

Przeszukaj forum

Pokazywanie wyników dla tagów 'IoT'.

  • Szukaj wg tagów

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

Typ zawartości


Kategorie forum

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

Szukaj wyników w...

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


Data utworzenia

  • Rozpocznij

    Koniec


Ostatnia aktualizacja

  • Rozpocznij

    Koniec


Filtruj po ilości...

Data dołączenia

  • Rozpocznij

    Koniec


Grupa


Znaleziono 11 wyników

  1. Cześć, przymierzam się do projektu inteligentnego domu, mam w głowie zarys planu tego co chcę osiągnąć, ale ze względu na niewielkie doświadczenie z elektroniką będę bardzo wdzięczny za wszelkie rady i sugestie. Ogólna koncepcja jest taka: Serwer na Raspberry Pi zbierający dane z czujników i wysyłający komendy do elementów wykonawczych. Czujniki i elementy wykonawcze rozproszone po całym mieszkaniu (i poza nim ) W pierwszej kolejności chciałbym zacząć od kilku prostych czujników i łączenia się do nich z mojego komputera (na razie bez serwera na malince). Myślałem o tym żeby czujniki wyposażać w esp8266 i łączyć się do nich po HTTP. W przyszłości, jak powstałby serwer na Raspberry Pi, to mógłby odpytywać czujniki i zbierać dane. Jeśli chodzi o zasilanie czujników to myślałem o zasilaniu bateryjnym/akumulatorowym. Tu pojawia się pierwszy problem, bo słyszałem, że esp potrzebuje dość sporo energii do zasilania. Myślicie, że taki układ ma prawo działać przez dłuższy czas? A może zamiast esp powinienem spróbować czegoś innego? Z góry dzięki za wszystkie rady
  2. Stacja pogodowa z wysyłką danych na stronę WWW Od paru tygodni testuję stację pogodową którą udało mi się zmontować w ostatnim czasie. Stacja oparta o sterownik Lan Kontroler V2.5 firmy Tiny Control. Jest to kompaktowe rozwiązanie zawierające: 5 wejść analogowych: pomiar temperatury, napięcia i prądu, oraz innych wielkości fizycznych, wejście cyfrowe w standardzie 1wire, wejście cyfrowe do obsługi czujnika temperatury i wilgotności DHT22, 4 wejścia logiczne: jako czujnik stanu do monitoringu, jako licznik impulsów z licznika energii, 1 przekaźnik (NZ, NO, C), 1 wyjście tranzystorowe, 4 wyjścia do załączania przekaźników oraz pomiar temperatury i napięcia zasilania na płytce. Do sterownika podłączyłem następujące czujniki: temperatury/wilgotności/ciśnienia - wszystko w jednym: CZUJNIK BME280/SPLITER 1WIRE/ RJ12 Grove - czujnik opadów / wody DFRobot Gravity - analogowy czujnik wilgotności gleby - odporny na korozję DFRobot Gravity - czujnik światła ultrafioletowego UV analogowy Miernik prędkości wiatru (chiński, zamówiony na aliexpress) Podłączenie Urządzenie Lan Controler wymaga podłączenia czujników do odpowiednich wejść analogowych/cyfrowych. Czujnik BME280 podłączany jest do złącza wire1. Po zmontowaniu całość prezentuje się następująco: Oprogramowanie Sterownik Lan Controler posiada wbudowany serwer www i panel zarządzania parametrami sterownika oraz wejściami: dodatkowo posiada możliwość wysyłki danych do serwerów protokołem HTTP, taką opcję zbierania danych umożliwia np. serwer Thing Speak (https://thingspeak.com/) ThingSpeak to aplikacja i interfejs API dla urządzeń IoT (opensourcowe) do przechowywania i pobierania danych z urządzeń elektronicznych za pomocą protokołu HTTP i MQTT. tak wyglądają dane z mojej stacji pogodowej na platformie Thinspeak: dzięki opcji zagnieżdżania poszczególnych widgetów z ThingSpeak na innych stronach, zrobiłem własną zawierającą kluczowe parametry pogodowe: Rozwój: w planie dodatkowy czujnik - przepływu wody, mierzący ilość opadów, planuję oprzeć to o czujnik przepływu YF-S402, natomiast przed zimą czujnik zanieczyszczenia powietrza. Oczywiście wszystkie dane będą publikowane na stronie WWW automatycznie.
  3. Po przeczytaniu dwóch poprzednich części znamy już pobieżnie zasady działania HTTP, potrafimy już stworzyć prosty (choć prawdopodobnie użyteczny) serwer. Ale przecież serwer to dopiero połowa - drugą, równie ważną jest klient. I znów będziemy próbować przesyłać między serwerem a klientem dane dotyczące stanu wejść naszego Arduino (czyli najprawdopodobniej jakichś czujników). Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Spis treści serii artykułów: Protokół HTTP w zastosowaniach IoT - część 1: trochę teorii Protokół HTTP w zastosowaniach IoT - część 2: budujemy serwer Protokół HTTP w zastosowaniach IoT - część 3: tworzymy klienta I tu uwaga: ponieważ musimy użyć dwóch urządzeń, potrzebne byłyby dwie identyczne płytki. Ponieważ nie każdy ma akurat w szufladzie dwa takie same układy - możemy zasymulować działanie takiego serwera używając naszego komputera. W tym celu w załączniku znajduje się krótki program napisany w Pythonie. Serwer działa na porcie 8001 zamast 80. Gdybyśmy jednak chcieli zmienić to zachowanie, musimy pamiętać, że: na naszym serwerze nie może działać żaden inny serwer na porcie 80; w przypadku Linuksa aby serwer mógł działać na porcie 80, musimy go uruchamiać z konta root (np. przez sudo) - normalny użytkownik nie może uruchamiać serwerów na portach niższych niż 1024. Jeśli chcemy, aby nasz serwer uruchamiał się na inym porcie niż 8001 - po prostu musimy znaleźć linijkę: port = 8001 i zamienić liczbę 8001 na numer portu. Serwer uruchamiamy z konsoli po wejściu do katalogu zawierającego program poleceniem: python3 miniserver.py lub odpowiednikiem dla naszego systemu operacyjnego. Jeśli nasz komputer ma zainstalowanego firewalla, należy zezwolić na dostęp z zewnątrz dla naszego serwera. Ponieważ różne firewalle mają różne metody służące do takowego zezwalania - odsyłam do dokumentacji naszego firewalla. Po uruchomieniu serwera możemy sprawdzić jego działanie wchodząc przeglądarką na adres http://ip_naszego_komputera:8001/ lub http://localhost:8001/ - powinny ukazać się trzy liczby. Jako że nasz komputer nie ma najprawdopodobniej podłączonych żadnych potencjometrów czy przycisków - liczba odpowiadająca potencjometrowi jest po prostu brana z generatora losowego, a klawiszowi zmienia się za każdym wywołaniem. Tyle tytułem przydługiego wstępu, możemy wreszcie zabrać się za tworzenie... klienta HTTP Ponieważ do klienta będą potrzebne takie same płytki jak do serwera, przypominam układy połączeń dla Arduino z shieldem Ethernet oraz płytek ESP3266 i ESP32: I znów jak poprzednio użyjemy wspólnego fragmentu kodu. Będzie on bardzo podobny do kodu używanego przy pisaniu serwera. Jedynymi różnicami są brak deklaracji i uruchomienia serwera oraz zdefiniowanie wielkości bufora (różne dla małego Arduino i większych płytek). Należy pamiętać, że w przypadku Ethernetu musimy zapewnić unikalność adresów MAC! #ifdef AVR // część dla Arduino i Ethernet Shield #include <SPI.h> #include <Ethernet.h> #define POT_PIN A1 #define KEY_PIN A0 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEE }; #define MyServer EthernetServer #define MyClient EthernetClient #define SERIAL_SPEED 9600 void init_network(void) { Ethernet.begin(mac); while (Ethernet.linkStatus() == LinkOFF) { Serial.println(F("Ethernet cable is not connected.")); delay(500); } // dajmy mu trochę czasu na połączenie delay(1000); Serial.print(F("Adres: ")); Serial.println(Ethernet.localIP()); } #else // część dla ESP #ifdef ESP32 #include <WiFi.h> #define POT_PIN 32 #define KEY_PIN 16 #else #include <ESP8266WiFi.h> #define POT_PIN A0 #define KEY_PIN 4 #endif const char* ssid = "My_SSID"; const char* password = "My_Password"; #define MyServer WiFiServer #define MyClient WiFiClient #define SERIAL_SPEED 115200 void init_network(void) { WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(); Serial.print("Połączono z WiFi, adres: "); Serial.println(WiFi.localIP()); } #endif void setup(void) { Serial.begin(SERIAL_SPEED); #ifdef __AVR_ATmega32U4__ while (!Serial); // potrzebne tylko dla Leonardo/Micro #endif init_network(); pinMode(KEY_PIN, INPUT_PULLUP); } #ifdef AVR #define BUFFER_LENGTH 128 #else #define BUFFER_LENGTH 1024 #endif Pierwszy klient będzie bardzo prosty. Nie musimy na razie uruchamiać naszego serwera, zamiast tego połączymy się z serwerem Google: const char Server[]="www.google.pl"; const int ServerPort = 80; void loop() { MyClient client; if (!client.connect(Server, ServerPort)) { Serial.println(F("Brak połączenia z serwerem")); delay(5000); return; } // pytamy googla o stronę główną client.print(F("GET / HTTP/1.1\r\nHost: www.google.pl\r\nCpnnection: Close\r\n\r\n")); // po prostu wypisujemy na monitorze serial // wszystko co dostaliśmy z serwera while (client.connected()) { if (client.available()) { char c = client.read(); Serial.print(c); } else { delay(5); // poczekamy chwilę aż serwer wyśle coś więcej } } client.stop(); while(1) delay(1); // koniec pracy! } Po uruchomieniu - o ile nasz klient ma dostęp do Internetu - powinien połączyć się z serwerem Google, pobrać zawartość strony głównej i wypisać ją na monitorze Serial: Jak widać, klient bardzo grzecznie pobrał wszystko co mu google wysłał i bez wnikania w szczegóły wyrzucił na wyjście. No nic - od klienta powinniśmy oczekiwać czegoś więcej. Przede wszystkim reakcji na błędy... a już na pewno stwierdzenia, czy nie wystąpił błąd. Spróbujmy więc stworzyć... sprytniejszego klienta HTTP Tym razem będziemy łączyć się z naszym serwerem, więc musimy pamiętać, aby go uruchomić! // Podaj właściwy adres i port serwera IPAddress Server(192,168,1,5); const int ServerPort = 8001; void loop() { MyClient client; char bufor[BUFFER_LENGTH]; if (!client.connect(Server, ServerPort)) { Serial.println(F("Brak połączenia z serwerem")); delay(5000); return; } client.setTimeout(5000); client.print(F("GET / HTTP/1.0\r\n\r\n")); // wczytujemy pierwszą linię odpowiedzi serwera int n = client.readBytesUntil('\n',bufor, BUFFER_LENGTH-1); bufor[n]=0; // uzupełniamy kończące '\0' // teraz po prostu sprawdzimy, czy w buforze znajduje się // string " 200 " - jeśli nie, jest to błąd! if (!strstr(bufor, " 200 ")) { client.stop(); // dalej nie gadamy Serial.print(F("Otrzymano odpowiedź: ")); Serial.println(bufor); delay(10000); // czekamy 10 sekund return; } // możemy pominąć pozostałe nagłówki jako mało interesujące: bool naglowki_wczytane = false; while(client.connected()) { n = client.readBytesUntil('\n',bufor,BUFFER_LENGTH-1); bufor[n]=0; Serial.print("Nagłówek: "); Serial.println(bufor); if (n == 1) { // w buforze jest jeden znak '\r' naglowki_wczytane = true; break; } } if (!naglowki_wczytane) { Serial.println(F("Błąd odczytu nagłówków")); client.stop(); delay(10000); return; } // teraz całą resztę wczytujemy do bufora n=client.readBytes(bufor, BUFFER_LENGTH-1); bufor[n]=0; client.stop(); Serial.print(F("Odpowiedź serwera: ")); Serial.println(bufor); delay(5000); } Trochę tu pooszukiwaliśmy - nie sprawdzamy całej pierwszej linii, ale wystarczy aby linia zawierała napis "<spacja>200<spacja>" - możemy to uznać za potwierdzenie. Tym razem zobaczymy, jak działa serwer w połączenia z dwoma klientami. Po ukazaniu się pierwszej informacji wchodzimy przeglądarką na adres: http://adres_ip_serwera/set/123 Po powrocie do monitora widzimy, że serwer zapamiętał tę liczbę i bardzo grzecznie nam ją przekazuje. A więc już teraz możemy zobaczyć, że serwer może służyć jako pośrednik wymiany danych między dwoma klientami! Jeśli jednak przyjrzymy się uważniej temu, co wypisuje monitor serial, zobaczymy że coś jest nie w porządku. Na wszelki wypadek możemy włączyć opcję "pokaż znacznik czasu" w monitorze i... O właśnie. Między odebraniem ostatniej linii nagłówków a odebraniem właściwych danych mija dokładnie 5 sekund. Czyżby serwer opóźniał w jakiś sposób wysyłanie danych? Nie - serwer wysyła tak jak trzeba. Po prostu dla bezpieczeństwa ustawiliśmy w kodzie: client.timeout(5000); i w związku z tym klient nie jest w stanie stwierdzić, czy serwer rzeczywiście się rozłączył - na wszelki wypadek czekając 5 sekund. Jak temu zaradzić? Otóż na razie korzystaliśmy z najprostszej metody: czytamy z klienta aż do końca. Problematyczna jest tu po prostu klasa Stream i jej metoda read(), która nie potrafi jednoznacznie zasygnalizować czy klient już zakończył połączenie, czy namyśla się nad wysłaniem dalszych danych. A readBytes na wszelki wypadek czeka te 5 sekund zanim zwróci wynik... Co w takiej sytuacji? Teoretycznie można by czytać sobie po znaku i w przypadku braku owego sprawdzać, czy klient się przypadkiem nie rozłączył. Nie byłoby to specjalnie efektywne - poza tym metoda "czytaj do końca" ma jedną zasadniczą wadę: tak naprawdę nie wiemy, z jakich powodów ów koniec nastąpił; być może swerwer wysłał już wszystko co ma do wysłania - a być może jakaś awaria (serwera czy któregoś z pośredniczących routerów) uniemożliwiła mu wysłanie wszystkiego do końca. Na szczęście istnieje na to bardzo prosty sposób. Serwer wysyła nagłówek ContentLength informujący, ile bajtów będzie liczyła właściwa odpowiedź. Klient powinien zanalizować ten nagłówek i po odebraniu tylu bajtów nie czekać więcej, tylko zamykać połączenie, a w przypadku przedwczesnego zakończenia transmicji (czyli odebrania mniejszej ilości bajtów od zadeklarowanej i wykrycia końca transmisji) zasygnalizować błąd. Jeśli korzystamy z serwera w pythonie ma on już wbudowaną taką funkcjonalność. Jeśli natomiast jako serwera używamy drugiego egzemplarza płytki - należy nieco zmodyfikować kod serwera. Nowy fragment kodu będzie wyglądać tak: int pot = analogRead(POT_PIN); int key = digitalRead(KEY_PIN); int length = sprintf(bufor, "%d %d %d\n", pot, key, nasza_zmienna); // wypisujemy nagłówki client.print(F("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length=")); client.print(length); client.print(F("\r\n\r\n")); // teraz jeśli zapytanie było GET wysyłamy przygotowane dane if (!isHead) { client.print(bufor); } a kompletny kod dostępny jest w załączniku. Teraz zajmiemy się klientem. Nie możemu już sobie pozwolić na pominięcie wszystkiego aż do pustej linii - musimy analizować wszystkie nagłówki, bo nie wiadomo w którym (i czy w ogóle) będzie interesująca nas wartość. Przyjmijmy, że jeśli nagłówek ContentLength nie wystąpił - wracamy do poprzedniej metody czytania aż do końca połączenia. A więc stwórzmy teraz... szybszego klienta HTTP Nowy kod wygląda w ten sposób: // Podaj właściwy adres i port serwera IPAddress Server(192,168,1,5); const int ServerPort = 8001; #define LENGTH_UNKNOWN -1 void loop() { MyClient client; char bufor[BUFFER_LENGTH]; if (!client.connect(Server, ServerPort)) { Serial.println(F("Brak połączenia z serwerem")); delay(5000); return; } client.setTimeout(5000); client.print(F("GET / HTTP/1.0\r\n\r\n")); // wczytujemy pierwszą linię odpowiedzi serwera int n = client.readBytesUntil('\n',bufor, BUFFER_LENGTH-1); bufor[n]=0; // uzupełniamy kończące '\0' // teraz po prostu sprawdzimy, czy w buforze znajduje się // string " 200 " - jeśli nie, jest to błąd! if (!strstr(bufor, " 200 ")) { client.stop(); // dalej nie gadamy Serial.print(F("Otrzymano odpowiedź: ")); Serial.println(bufor); delay(10000); // czekamy 10 sekund return; } // wczytujemy po kolei nagłówki, szukając Content-Length int ContentLength = LENGTH_UNKNOWN; bool naglowki_wczytane = false; while(client.connected()) { n = client.readBytesUntil('\n',bufor,BUFFER_LENGTH-1); bufor[n]=0; Serial.print("Nagłówek: "); Serial.println(bufor); if (n == 1) { // w buforze jest jeden znak '\r' naglowki_wczytane = true; break; } if (!strncasecmp(bufor,"content-length:", 15)) { ContentLength = atoi(bufor+15); } } if (!naglowki_wczytane) { Serial.println(F("Błąd odczytu nagłówków")); client.stop(); delay(10000); return; } Serial.print(F("Rozmiar danych: ")); Serial.println(ContentLength); // teraz całą resztę wczytujemy do bufora if (ContentLength > BUFFER_LENGTH -1 || ContentLength == LENGTH_UNKNOWN) { ContentLength = BUFFER_LENGTH - 1; } n=client.readBytes(bufor, ContentLength); bufor[n]=0; client.stop(); if (n < ContentLength) { Serial.println(F("Odpowiedź niekompletna")); } else { Serial.print(F("Odpowiedź serwera: ")); Serial.println(bufor); } delay(5000); } Jak widać nieco się skomplikował. Pewnie nie wymaga objaśnień poza jednym drobiazgiem: Zmienna ContentLength jest zdeklarowana jako int. Nasuwałoby się od razu - dlaczego nie unsigned int? Przecież długość nie może być ujemna... Owszem, moglibyśmy zdeklarować ją jako unsigned. Tyle że wtedy musielibyśmy wprowadzić dodatkową zmienną tylko do tego, aby zasygnalizować wystąpienie tego nagłówka (bo wartość zero jest jak najbardziej prawidłowa). W tym przypadku podstawiamy liczbę ujemną przed czytaniem nagłówków i po ich odczytaniu od razu wiemy: jeśli liczba jest nieujemna mamy tam wielkość przesyłanych danych, w przeciwnym przypadku wielkość jest nieznana. Po uruchomieniu wyniki wyglądają następująco: Jak widać nie ma tu żadnego oczekiwania, wyniki pokazują się natychmiast. No, to już wiemy po co są nagłówki (przynajmniej niektóre). Jak widać - niosą one ze sobą różne informacje: w przypadku klienta - np. o tym, w jakiej postaci chaciałby najchętniej mieć podane dane; w przpadku serwera - w jakiej postaci te dane podano. Ale może czas już na coś konkretnego... w końcu do płytki mamy podłączony jakiś potencjometr i przycisk a dotychczas go nie używaliśmy... A więc naprawmy tę sytuację tworząc... prawdziwego klienta HTTP dla IoT Tym razem nie będziemy jednak wrzucać całego kodu do loop(), stworzymy funkcję, której zadaniem będzie: wysłanie na serwer wartości parametru; odebranie danych z serwera i wypisanie ich na monitorze serial; zwrócenie informacji czy operacja się udała. Ta funkcja powinna być wywołana po wciśnięciu przycisku, a argumentem funkcji niech będzie wartość odczytana z wejścia analogowego. Tym razem musimy skonstruować zapytanie. Wbrew pozorom jest to bardzo proste - za pomocą funkcji sprintf umieszczamy w buforze odpowiedni napis i wysyłamy zawartość bufora na serwer. Nowy kod będzie wyglądać następująco: // Podaj właściwy adres i port serwera IPAddress Server(192,168,1,5); const int ServerPort = 8001; #define LENGTH_UNKNOWN -1 bool zapisz(int dane) { // funkcja zwraca true jeśli udało się zapisać dane // false jeśli wystąpił błąd MyClient client; char bufor[BUFFER_LENGTH]; Serial.print(F("Wartość parametru: ")); Serial.println(dane); if (!client.connect(Server, ServerPort)) { Serial.println(F("Brak połączenia z serwerem")); delay(50); return false; } client.setTimeout(5000); // tworzymy zapytanie do serwera sprintf(bufor,"GET /set/%d HTTP/1.0\r\n\r\n", dane); client.print(bufor); // nie stosujemy Serial.println() gdyż w buforze // są już znaki końca linii Serial.print(F("Wysyłam zapytanie: ")); Serial.print(bufor); // wczytujemy pierwszą linię odpowiedzi serwera int n = client.readBytesUntil('\n',bufor, BUFFER_LENGTH-1); bufor[n]=0; // uzupełniamy kończące '\0' // teraz po prostu sprawdzimy, czy w buforze znajduje się // string " 200 " - jeśli nie, jest to błąd! if (!strstr(bufor, " 200 ")) { client.stop(); // dalej nie gadamy Serial.print(F("Otrzymano odpowiedź: ")); Serial.println(bufor); delay(1000); // czekamy sekundę return false; } // wczytujemy po kolei nagłówki, szukając Content-Length int ContentLength = LENGTH_UNKNOWN; bool naglowki_wczytane = false; while(client.connected()) { n = client.readBytesUntil('\n',bufor,BUFFER_LENGTH-1); bufor[n]=0; Serial.print(F("Nagłówek: ")); Serial.println(bufor); if (n == 1) { // w buforze jest jeden znak '\r' naglowki_wczytane = true; break; } if (!strncasecmp(bufor,"content-length:", 15)) { ContentLength = atoi(bufor+15); } } if (!naglowki_wczytane) { Serial.println(F("Błąd odczytu nagłówków")); client.stop(); delay(1000); return false; } Serial.print(F("Rozmiar danych: ")); Serial.println(ContentLength); // teraz całą resztę wczytujemy do bufora if (ContentLength > BUFFER_LENGTH -1 || ContentLength == LENGTH_UNKNOWN) { ContentLength = BUFFER_LENGTH - 1; } n=client.readBytes(bufor, ContentLength); bufor[n]=0; client.stop(); if (n < ContentLength) { Serial.println(F("Odpowiedź niekompletna")); } else { Serial.print(F("Odpowiedź serwera: ")); Serial.println(bufor); } return true; } void loop() { static int lastKey = digitalRead(KEY_PIN); int key = digitalRead(KEY_PIN); // jeśli klawisz został wciśnięty, wysyłamy wartość // odczytaną z wejścia analogowego na serwer if (lastKey && !key) { int i,pot; pot = analogRead(POT_PIN); for (i=0; i<10; i++) { // więcej niż 10 prób wysłania nie ma sensu if (zapisz(pot)) break; } if (i==10) { Serial.println(F("Nie udało się wysłać danych")); } } lastKey=key; } Po uruchomieniu programu możemy zobaczyć, że każde naciśnięcie przycisku powoduje zmianę wartości podawanej przez serwer: Aby to lepiej zobrazować możemy spróbować podejrzeć co się dzieje na serwerze w czasie rzeczywistym. Jeśli mamy jakiegoś linuksa (prawdopodobnie na maku też to zadziała) możemy wpisać po prostu polecenie: watch -n 5 wget -q -O - http://127.0.0.1:8001 Oczywiście jeśli korzystamy z innego serwera niż nasz pythonowy musimy wpisać zamiast 127.0.0.1:8001 właściwy adres i port. Wykonanie tego polecenia spowoduje, że polecenie wget będzie wykonywane co 5 sekund, a wartość odczytana z serwera będzie wyświetlana na ekranie. W przypadku windowsa nie jest to już takie proste... ale od czego mamy nagłówki serwera? W pliku miniserver.py znajdujemy linię zawierającą: #self.send_header("Refresh", 5) i odkomentowujemy polecenie usuwając znak #: self.send_header("Refresh", 5) i oczywiście restartujemy serwer. Spowoduje to wysłanie dodatkowego nagłówka Refresh: 5 Jeśli teraz wejdziemy na ten adres zwykłą przeglądarką - będzie ona odświeżać wyświetlane wyniki co 5 sekund. Oczywiście zadziała to również na Linuksie i Maku! Niezależnie od metody - możemy teraz zobaczyć jak po wciśnięciu przycisku dane wędrują od naszego klienta poprzez serwer do przeglądarki. I to by było na tyle. Miała być co prawda jeszcze czwarta część poświęcona praktycznym rozwiązaniom - okazało się jednak, że ilość materiału który należałoby omówić wymaga osobnego, kilkuczęściowego artykułu. Toteż na tym na razie musimy poprzestać i pożegnać się do następnego spotkania. Kody wszystkich programów dostępne są w załączniku: klient.zip Spis treści serii artykułów: Protokół HTTP w zastosowaniach IoT - część 1: trochę teorii Protokół HTTP w zastosowaniach IoT - część 2: budujemy serwer Protokół HTTP w zastosowaniach IoT - część 3: tworzymy klienta
  4. Z poprzedniej części mogliśmy się dowiedzieć, co to takiego ten cały HTTP -jak się okazało, nic strasznie skomplikowanego. Dzisiaj zajmiemy się praktyczną stroną - czyli napisaniem najprostszego serwera. Oczywiście - jako że temat traktuje o IoT - serwer będzie udostępniał dane odczytane z jakichś czujników. Już słyszę: a dlaczego serwer a nie klient, przecież klient powinien być łatwiejszy? Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. A dlatego, że aby sprawdzić działanie klienta, należy mieć serwer a takiego na razie nie mamy. Natomiast klienta HTTP ma tu raczej każdy - począwszy od przeglądarek, poprzez przeróżne wgety i curle kończąc na PuTTY i netcacie. Przyjrzyjmy się więc dokładniej, jak działa. Spis treści serii artykułów: Protokół HTTP w zastosowaniach IoT - część 1: trochę teorii Protokół HTTP w zastosowaniach IoT - część 2: budujemy serwer Protokół HTTP w zastosowaniach IoT - część 3: tworzymy klienta Serwer HTTP W najprostszym przypadku serwer po podłączeniu do sieci po prostu czeka na zgłoszenie klienta, wykonuje jakąś tam funkcję obsługi, rozłącza się z klientem i powraca do czekania. Spróbujmy napisać taki program. Ponieważ dla różnych płytek i modułów łączenia z siecią program może być różny, spróbujmy stworzyć przynajmniej dwa (czyli Arduino z Ethernet Shield i dla ESP8266/ESP32). Zacznijmy od jakichś założeń. A więc: Do płytki podłączony jest potencjometr 10 kΩ i przycisk; Serwer w odpowiedzi na zapytanie podaje wartości odczytu potencjometru i przycisku; Serwer nie sprawdza poprawności zapytania, zakłada że jeśli ktoś coś od niego chce to tylko tego co wypisuje; Serwer wypisuje wartości czystym tekstem, bez zabawy w jakieś HTML-e i podobne wymysły. Najpierw więc przygotujmy wszystko, co nam będzie potrzebne. Przede wszystkim musimy sobie przygotować płytkę i podłączyć do niej potencjometr i przycisk. W przypadku Arduino będzie to wyglądać tak: Podobnie dla ESP8266 i ESP32: Zanim zabierzemy się do pisania programu - kilka słów wstępu. Otóż różne biblioteki (WiFi, Ethernet) operujące na połączeniach sieciowych TCP/IP zawsze mają swoje klasy serwera i klienta, będące pochodnymi ogólnych klas Server i Client. Funkcje obsługi połączeń (spoza tych bibliotek) zawsze wymagają właśnie obiektów tych klas, a nie konkretnych (np. EthernetServer). A co robią takie obiekty? To są po prostu elementy najprostszego połączenia TCP/IP. Obiekt klasy Server nasłuchuje na określonym porcie i w przypadku przychodzącego połączenia tworzy nowy obiekt typu Client, już połączony ze stroną nawiązującą połączenie: EthernetClient client=server.available(); if (client) { // obsługa połączenia } Obiekt klasy Client można również utworzyć ręcznie - i wtedy trzeba mu podać adres i port serwera, z którym ma się połączyć: if (client.connect(ADRES_IP_SERWERA, PORT_SERWERA)) { // rozmowa z serwerem } else { Serial.println("Nie mogę nawiązać połączenia"); } Ponieważ Client dziedziczy po klasie Stream (a Stream po Print) - możemy tu używać wszystkich metod pochodzących z tej klasy (podobnie jak w obiektach klasy Serial) Dobra, wystarczy tego wstępu. Zabierzmy się wreszcie za napisanie prostego serwera. Zacznijmy od tego, co będzie się powtarzać we wszystkich następnych programach, czyli od tych wszystkich beginów, initów i innych powtarzalnych rzeczy. Spróbujmy od razu zrobić to tak, aby jak najmniej ingerować w późniejsze programy czy to przy zmianie płytki (Arduino - ESP), czy przy dalszych eksperymentach z programami (serwer i klient). Taki typowy kod (dla Arduino) wygląda tak: #include <SPI.h> #include <Ethernet.h> #define POT_PIN A1 #define KEY_PIN A0 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; EthernetServer server(80); void setup() { Serial.begin(9600); while (!Serial); // dla Leonardo Ethernet.begin(mac); while (Ethernet.linkStatus() == LinkOFF) { Serial.println(F("Ethernet cable is not connected.")); delay(500); } // dajmy mu trochę czasu na połączenie delay(1000); Serial.print(F("Adres serwera: ")); Serial.println(Ethernet.localIP()); pinMode(KEY_PIN, INPUT_PULLUP); server.begin(); } Oczywiście jest to najprostszy z możliwych sposobów połączenia z siecią, więcej informacji można znaleźć w dokumentacji i przykładach biblioteki Ethernet. Porównajmy to z kodem dla ESP8266 i ESP32: #ifdef ESP32 #include <WiFi.h> #define POT_PIN 32 #define KEY_PIN 16 #else #include <ESP8266WiFi.h> #define POT_PIN A0 #define KEY_PIN 4 #endif const char* ssid = "My_SSID"; const char* password = "My_PASSWORD"; WiFiServer server(80); void setup(void) { Serial.begin(115200); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(); Serial.print("Połączono z WiFi, adres serwera: "); Serial.println(WiFi.localIP()); pinMode(KEY_PIN, INPUT_PULLUP); server.begin(); } I znów procedura łąćzenia jest maksymalnie uproszczona - ale nie czas to i nie miejsce na omawianie niuansów różnych rodzajów połączeń:) Jak widać kody dla ESP8266 i ESP32 są na tyle podobne, że mogliśmy użyć wspólnego kodu zmieniając tylko bibliotekę i definicje pinów. Spróbujmy jednak pójść dalej i stworzyć wspólny kod dla Arduino/Ethernet i ESP. #ifdef AVR // część dla Arduino i Ethernet Shield #include <SPI.h> #include <Ethernet.h> #define POT_PIN A1 #define KEY_PIN A0 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; #define MyServer EthernetServer #define MyClient EthernetClient #define SERIAL_SPEED 9600 void init_network(void) { Ethernet.begin(mac); while (Ethernet.linkStatus() == LinkOFF) { Serial.println(F("Ethernet cable is not connected.")); delay(500); } // dajmy mu trochę czasu na połączenie delay(1000); Serial.print(F("Adres serwera: ")); Serial.println(Ethernet.localIP()); } #else // część dla ESP #ifdef ESP32 #include <WiFi.h> #define POT_PIN 32 #define KEY_PIN 16 #else #include <ESP8266WiFi.h> #define POT_PIN A0 #define KEY_PIN 4 #endif const char* ssid = "My_SSID"; const char* password = "My_PASSWORD"; #define MyServer WiFiServer #define MyClient WiFiClient #define SERIAL_SPEED 115200 void init_network(void) { WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(); Serial.print("Połączono z WiFi, adres serwera: "); Serial.println(WiFi.localIP()); } #endif MyServer server(80); void setup(void) { Serial.begin(SERIAL_SPEED); #ifdef __AVR_ATmega32U4__ while (!Serial); // potrzebne tylko dla Leonardo/Micro #endif init_network(); pinMode(KEY_PIN, INPUT_PULLUP); server.begin(); } Możemy teraz zobaczyć, że: wszystkie zależne od płytki wywołania funkcji inicjalizujących połączenie zostały przeniesione do funkcji init_network(); nazwy klas zależnych od płytki i typu połączenia z siecią zostały "zamaskowane" definicjami MyClient i MyServer W ten sposób pisząc dalej program nie będziemy musieli zawracać sobie głowy różnicami między płytkami, bibliotekami i sposobami połączeń z siecią. I teraz mając wszystko przygotowane, możemy na spokojnie zająć się... pierwszą wersją serwera Kod jest tu banalnie prosty i chyba nie wymaga komentarza. Po połączeniu klienta pobieramy od niego wszystko jak leci nigdzie tego nie zapamiętując, aż do napotkania pustej linii. W tym momencie zakładamy, że klient nic więcej wysyłać nie będzie. W odpowiedzi wysyłamy dwa nagłówki: HTTP/1.1 200 OK - potwierdzenie, że wszystko się udało Content-Type: text/plain - czyli, że odpowiadamy czystym tekstem Po nagłówkach wysyłamy linię zawierającą obie odczytane z wejść wartości i zamykamy połączenie. void loop() { // Sprawdzamy, czy klient się połączył MyClient client = server.available(); if (!client) { // nie połączył się return; } Serial.println(F("Nowe połączenie")); // Czytamy znak po znaku aż do napotkania pustej linii: bool linia_pusta = true; bool naglowki_wczytane = false; while (client.connected()) { if (!client.available()) continue; char c = client.read(); Serial.print(c); if (c == '\n') { // koniec linii if (linia_pusta) { naglowki_wczytane = true; // wczytaliśmy wszystko break; } linia_pusta = true; } else if (c != '\r') { // znak '\r' po prostu pomijamy linia_pusta = false; } } // czy na pewno wszystko wczytaliśmy? if (!naglowki_wczytane) { // klient zniknał Serial.println(); Serial.println(F("Klient rozwiał się we mgle")); } else { // Wypisujemy ważne nagłówki i pustą linię client.print(F("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n")); int pot = analogRead(POT_PIN); int key = digitalRead(KEY_PIN); client.print(pot); client.print(' '); client.println(key); } client.stop(); } Możemy teraz w praktyce przetestować nasz serwer. Możemy do tego użyć przeglądarki, wpisując następujący URL: http://adres_ip_serwera/ My jednak chcielibyśmy obejrzeć całą transmisję, więc znów użyjemy PuTTY lub nc: Jak się przekonaliśmy, napisanie prostego serwera nie jest wcale takie trudne, a nawet taki banalny program może byc użyteczny (np. w czujniku temperatury, odpytywanym periodycznie przez główny komputer w domu). Tym niemniej program może się niespecjalnie podobać. Przede wszystkim - czego byśmy nie wpisali, dostaniemy w odpowiedzi wartości z czujników. Podglądając komunikację widzimy, że przeglądarka wysłała dodatkowy request prosząc o ikonkę serwisu - a takiej przecież nie mamy. Spróbujmy więc ulepszyć nieco nasz serwer, tworząc... ulepszoną wersję serwera Poczyńmy znów pewne założenia: Oprócz potencjometru i przycisku serwer podaje wartość jakiejś zmiennej (nazwijmy ją po prostu nasza_zmienna); Serwer oprócz metody GET obsługuje również HEAD; W przypadku otrzymania nieprawidłowej linii serwer odpowiada komunikatem błędu; Z poziomu przeglądarki możemy ustawić wartość "nasza_zmienna" wpisując URL typu http://adres_ip_serwera/set/wartość; Jeśli serwer otrzyma żądanie /favicon.ico, odpowiada błędem 404 (Not Found). #ifdef AVR #define BUFFER_LENGTH 128 #else #define BUFFER_LENGTH 1024 #endif int nasza_zmienna=0; void error400(MyClient& client) { client.println(F("HTTP/1.1 400 Bad Request\r\n\ Content-type: text/plain\r\n\r\n\ Bad request")); client.stop(); } void error404(MyClient& client) { client.println(F("HTTP/1.1 404 Not Found\r\n\ Content-type: text/plain\r\n\r\n\ Not found")); client.stop(); } void error405(MyClient& client) { client.println(F("HTTP/1.1 405 Method Not Allowed\r\n\ Allow: GET,HEAD\r\n\ Content-type: text/plain\r\n\r\n\ Method not allowed")); client.stop(); } void loop(void) { char bufor[BUFFER_LENGTH]; bool isHead; // Sprawdzamy, czy klient się połączył MyClient client = server.available(); if (!client) { // nie połączył się return; } Serial.println(F("Nowe połączenie")); // dajmy sobie czas na wpisanie czegoś do PuTTY client.setTimeout(20000); // Wczytujemy pierwszą linię int n = client.readBytesUntil('\n',bufor, BUFFER_LENGTH-1); if (!n) { // nic nie wczytano? Serial.println(F("Klient się rozmyślił")); client.stop(); return; } bufor[n] = 0; // dopisujemy '\0' na końcu stringu Serial.println(bufor); // teraz pomijamy resztę nagłówków bool linia_pusta = true; bool naglowki_wczytane = false; while (client.connected()) { if (!client.available()) continue; char c = client.read(); Serial.print(c); if (c == '\n') { // koniec linii if (linia_pusta) { naglowki_wczytane = true; // wczytaliśmy wszystko break; } linia_pusta = true; } else if (c != '\r') { // znak '\r' po prostu pomijamy linia_pusta = false; } } // czy na pewno wszystko wczytaliśmy? if (!naglowki_wczytane) { // klient zniknał Serial.println(); Serial.println(F("Klient rozwiał się we mgle")); client.stop(); return; } char *path=strchr(bufor,' '); if (!path) { error400(client); return; } *path++=0; // wstawiamy zero w miejsce spacji // oddzielającej metodę od ścieżki if (!strcmp(bufor, "GET")) { isHead = false; } else if (!strcmp(bufor, "HEAD")) { isHead = true; } else { error405(client); return; } char *proto = strchr(path, ' '); if (!proto) { error400(client); return; } *proto++=0; // nie przesadzajmy, uwierzmy na słowo że to HTTP :) // nie będziemy sprawdzać co siedzi w proto if (!strcmp(path,"/favicon.ico")) { error404(client); return; } if (!strncmp(path, "/set/", 5)) { nasza_zmienna = atoi(path+5); Serial.print(F("Ustawiamy nową wartość zmiennej: ")); Serial.println(nasza_zmienna); } // wypisujemy nagłówki client.print(F("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n")); // teraz możemy odpowiedzieć trzema wartościami: // potencjometr, przycisk i nasza zmienna if (!isHead) { int pot = analogRead(POT_PIN); int key = digitalRead(KEY_PIN); client.print(pot); client.print(' '); client.print(key); client.print(' '); client.println(nasza_zmienna); } client.stop(); } Kod powinien być zrozumiały bez szczegółowego omawiania każdej linii, być może z dwiema uwagami: dotychczas nie mówiliśmy o metodzie HEAD. Działa ona podobnie jak GET, ale serwer kończy transmisję po wysłaniu nagłówków. zamiast błędu 405 (Method Not Allowed) należałoby użyć 501 (Not Implemented). Sugerowałoby to jednak błąd oprogramowania serwera tymczasem to klient przeznaczony do współpracy z urządzeniami IoT powinien wiedzieć jakie pytanie można zadać serwerowi. Dodatkowo obsługa błędu 405 wymaga wysłania dodatkowego nagłówka informującego klienta, jakich metod może używać. A oto wyniki działania naszego serwera: Widzimy więc, że zgodnie z naszymi założeniami: Metoda HEAD powoduje wysłanie tylko nagłówków; Nieznana metoda powoduje wystąpienie błędu 405 Prośba o /favicon.ico powoduje wystąpienie błędu 404 URI o specialnej postaci: /set/<liczba> powoduje zapisanie liczby w pamięci serwera. Możemy również zauważyć, ze odpowiedź informująca o błędzie ma taką samą postać jak prawidłowa, czyli składa się z głównego nagłówka odpowiedzi (tzw. "status line"), nagłówka typu, ewentualnie dodatkowych nagłówków i odpowiedzi w postaci zrozumiałej dla człowieka. Dodatkowym efektem wysłania 404 w odpowiedzi na favicon.ico jest zapamiętanie tej informacji przez przeglądarkę i nie proszenie o nią za każdym razem. Taki serwer może już służyć bardziej praktycznym celom - np. sterowaniem różnych domowych urządzeń za pomocą przeglądarki. I tyle na dziś. Kody obu programów znajdziemy w załączniku: servers.zip - a w następnej części spróbujemy stworzyć klienta, czyli skomunikować się z naszym serwerem. Spis treści serii artykułów: Protokół HTTP w zastosowaniach IoT - część 1: trochę teorii Protokół HTTP w zastosowaniach IoT - część 2: budujemy serwer Protokół HTTP w zastosowaniach IoT - część 3: tworzymy klienta
  5. Protokół HTTP jest bardzo często uzywany w zastosowaniach IoT. Przede wszystkim decyduje tu prostota implementacji, dostępność bibliotek oraz możliwość współpracy z typowymi serwerami WWW bez konieczności instalacji dodatkowego oprogramowania. Szczególnie ta ostatnia cecha może być szczególnie przydatna z uwagi na ilość darmowych usług hostingowych, których nawet ograniczone parametry pozwalają na stworzenie prostej aplikacji niewielkim nakładem środków i przy użyciu minimalnej wiedzy. Niestety - z tą minimalna wiedzą nie jest już tak słodko. Autorzy popularnych bibliotek zakładają pewne minimum wiedzy u użytkowników i pomijają sprawy dla nich oczywiste. Użytkownicy zaś nie mogąc znaleźć jakichś informacji na temat podstaw - próbują coś zrobić metodą prób i błędów, co nikomu na zdrowie nie wychodzi. Spróbuję więc przybliżyć owo "minimum konieczne", aby nie trzeba było przekopywać się przez dokumenty typu RFC po ty tylko, by przesłać do serwera zmierzoną temperaturę na balkonie. Ten wpis brał udział konkursie na najlepszy artykuł o elektronice lub programowaniu. Sprawdź wyniki oraz listę wszystkich prac » Partnerem tej edycji konkursu (marzec 2020) był popularny producent obwodów drukowanych, firma PCBWay. Spis treści serii artykułów: Protokół HTTP w zastosowaniach IoT - część 1: trochę teorii Protokół HTTP w zastosowaniach IoT - część 2: budujemy serwer Protokół HTTP w zastosowaniach IoT - część 3: tworzymy klienta Zacznijmy jednak od czegoś prostszego, mianowicie od tego... czym jest URL. Każdy z pewnością zna to słowo, i większość z czytelników pewnie rozumie (nawet intuicyjnie) co to takiego. Aby jednak uniknąć jakichkolwiek nieporozumień proponuję zapoznać się z budową URL-a. URL (czyli Uniform Resource Locator) to znormalizowany sposób określania położenia dokumentu (zasobu) w Internecie. Ponieważ dla różnych schematów mogą istnieć różne części, skupmy się wyłącznie na protokole HTTP. Taki URL składa się z następujących części: Schemat - sposób, w jaki nasz dokument będzie przesyłany. W naszym przypadku będzie to zawsze http; Użytkownik - nazwa użytkownika, dla zasobów które mogą być serwowane różnie w zależności od użytkownika. Jeśli nie jest potrzebna, można ją opuścić wraz następujący po niej ewentualnym drukropkiem i hasłem oraz znakiem @; Hasło - jak sama nazwa wkazuje, hasło dostępu do dokumentu. Może wystąpić tylko wtedy, gdy podajemy użytkownika, a jeśli nie jest potrzebne, można je opuścić wraz z poprzedzającym dwukropkiem; Host - nazwa (lub adres IP) komputera, na którym umieszczony jest nasz dokument. Mimo, że dopuszczalne jest podanie w tym miejscu adresu IP, nazwa hosta wymagana jest jeśli serwer hostuje więcej niż jedną stronę www na tym samym adresie IP (czyli praktycznie wszędzie oprócz naszego domowego Arduino czy Raspberry); Port - port TCP, na którym nasłuchuje serwer. Jeśli jest to domyślny port (w przypadku http będzie to port 80) - może być pominięty wraz z poprzedzającym dwukropkiem; Ścieżka - czyli miejsce, gdzie na serwerze znajduje się nasz dokument. Zawsze rozpoczyna się znakiem '/', składa się z członów rozdzielonych znakiem '/' i może zawierać wyłącznie 7-bitowe znaki; Zapytanie - czyli parametr przesłany do skryptu na serwerze, jeśli ścieżka wskazuje na wykonywalny program a nie fizyczny dokument. Jeśli nie jest potrzebne, może być opuszczone wraz z poprzedzającym znakiem '?'. Używając popularnej pochodzącej z BNF notacji można to zapisać tak: <schemat>://[<użytkownik>[:<hasło>]@]<host>[:<port>]<ścieżka>?[<zapytanie>] Już słyszę pytanie: a co z fragmentem? Przecież wpisując adres do przeglądarki możemy zakończyć go znakiem '#' i etykietą informującą, który fragment ma się pokazać na ekranie... Otóż trzeba sobie uświadomić że to, co wpisujemy do przeglądarki nie jest tak naprawdę URL-em. Przeglądarki rozumieją pewien swój zapis - np. brak schematu, narodowe znaczki w ścieżce czy właśnie ten '#' i fragment. Pamiętać należy jednak, że w przypadku braku schematu przeglądarka użyje domyślnego 'http', narodowe znaczki zakoduje w pewien określony sposób a fragmentu w ogóle nie wyśle do serwera, będzie on potrzebny wyłącznie przeglądarce przy wyświetlaniu. Ponieważ dalej będziemy posługiwać się uproszczoną wersją URL-i, przyjmijmy że jego składnia będzie w większości przypadków następująca: http://<host><ścieżka>[?<zapytanie>] Należy tu wspomnieć o czymś jeszcze: o ile składnia URL-a nie narzuca żadnego specjalnego formatowania dla części "zapytanie", przyjęto zunifikowany zapis par <nazwa>=<wartość>, oddzielonych znakami '&'. I tu uwaga: w dalszej części będę posługiwał się również pojęciem URI (Unified Resource Identifier). Nie chcę wnikać w szczegóły, przyjmijmy więc uproszczenie, że URI identyfikuje zasób w obrębie serwera i składa się ze ścieżki oraz opcjonalnego zapytania (czyli de facto stanowi fragment URL-a). Tak więc wiemy już z czego się składa adres w Internecie, spróbujmy przyswoić sobie... podstawy protokołu HTTP Przede wszystkim musimy wiedzieć, że połączenie http składa się zawsze z zapytania wysłanego do serwera i odpowiedzi serwera. I koniec. Nie ma żadnej dalszej komunikacji, szczególnie po wysłaniu odpowiedzi serwer nie będzie pamiętać, o co go poprzednio prosiliśmy. Taki protokół nazywany jest bezstanowym, gdyż każde zapytanie rozpatrywane jest przez serwer niezależnie od innych. Oczywiście - serwer może wprowadzać dodatkowe mechanizmy zapewniające jakieś tam zależności (choćby mechanizm ciastek i sesji), ale nie należą one do protokołu ale do konkretnych wykonywanych na serwerze skryptów. Zacznijmy od zapytania - czyli tego, co klient (np. przeglądarka) przesyła do serwera. Zapytanie składa się z linii zakończonych parą znaków CRLF (czyli w notacji C++ "\r\n") i zawsze rozpoczyna się linią w postaci: <metoda> <URI> <protokół> Nas interesuje na razie metoda GET czyli "pobierz zawartość zasobu wskazanego przez URI" oraz obowiązujący protokół HTTP/1.1. Tak więc początkowa zawartość zapytania pobierająca np. zawartość głównej strony forum Forbota będzie miała postać: GET /forum/ HTTP/1.1 Następne linie stanowią nagłówki zapytania. Może ich być wiele - np. podających język przeglądarki czy rozpoznawane kodowanie, zawsze w postaci: <nazwa>: <wartość> Nas interesuje przede wszystkim nagłówek "Host" podający nazwę serwera. Warto również poinformować serwer, że oczekujemy na zakończenie połączenia po przesłaniu dokumentu, co załatwia nam nagłówek "Connection". Co prawda wartość "Close" jest dla tego nagłówka domyślną, ale warto przyswoić sobie zasadę podawania serwerowi wszystkiego co jest potrzebne do zwrócenia odpowiedzi - i niczego poza tym. Tak więc pełne zapytanie będzie wyglądać tak: GET /forum/ HTTP/1.1 Host: forbot.pl Connection: Close Każde zapytanie musi kończyć się pustą linią, aby poinformować serwer że już skończyły się nagłówki i teraz oczekujemy odpowiedzi. Możemy teraz spróbować przetestować najprostsze połączenie. W tym celu musimy jednak zaopatrzyć się w jakieś narzędzie, umożliwiające połączenie z siecią i przesłanie w obie strony tekstowych komunikatów. Mamy tu dwie możliwości. W przypadku Linuksa najwygodniejsze będzie użycie konsolowego narzędzia. Takim narzędziem może być telnet lub netcat. Ponieważ nie we wszystkich dystrybucjach są one zainstalowane na dzień dobry, może wystąpić konieczność doinstalowania. Dla Debiana i pochodnych (Raspbian, Ubuntu, Mint itp.) będzie to: sudo apt install telnet lub sudo apt install netcat I tu uwaga: w większości przypadków ostatnie polecenie zainstaluje nam netcat-traditional, czyli tradycyjną wersję programu. Z różnych przyczyn (szczególnie przy testach połączeń UDP) lepsza może by wersja openbsd, tak więc jeśli mamy taką możliwość warto ją zainstalować: sudo apt install netcat-openbsd Składnia obu poleceń dla naszych celów jest podobna: telnet <adres> <port> lub nc -C <adres> <port> Opcja -C w drugim przypadku włącza tryb przekazywania sekwencji CRLF zamiast LF (czyli po naciśnięciu ENTER) - co jak wspomniałem wcześniej jest wymagane dla połączeń http. Inną możliwością jest użycie znanego programu PuTTY. Nie będę tu opisywać procedur instalacji dla wszystkich możliwych systemów operacyjnych, należy jednak wspomnieć o ustawieniach: W ustawieniach sesji należy zaznaczyć "Connection type: RAW" (czyli "surowe" połączenie, bez żadnych dodatkowych protokołów) i "Close window on exit: Never" (bo nie chcemy, aby okno zamykało się od razu po odebraniu danych nie dając nam czasu na przeczytanie). W ustawieniach terminala natomiast "Implicit CR on every LF" (inaczej linie na terminalu "rozjadą się"). Takie ustawienia możemy sobie zapisać pod jakąś mądrą nazwą (np. tak jak w moim przypadku "raw http") i używać w wielu następnych testach. Spróbujmy więc na początek połączyć się z jakimś ogólnodostępnym serwerem. Na początek spróbujmy serwera Google. Wpisujemy więc następujące polecenie (lub łączymy się poprzez PuTTY z serwerem): nc -C www.google.pl 80 Oczywiście musimy sporo przewinąć w górę aby dojść do początku transmisji (przy okazji widać, że pozornie prosta strona Google wcale nie jest taka prosta). Możemy teraz zobaczyć dwa najważniejsze nagłówki odpowiedzi: HTTP/1.1 200 OK Taki nagłówek będzie zawsze pierwszą linią wysyłaną przez serwer. Składa się z trzech części: Protokół - w tym przypadku HTTP/1.1 Kod odpowiedzi - w tym przypadku 200 (czyli "wszystko w porządku"). Spis wszystkich kodów możemy znaleźć np. w Wikipedii. Opis odpowiedzi - w tym przypadku "OK" Drugi nagłówek to Content-Type. Musi być zawsze obecny w odpowiedzi i zawiera po prostu typ mime przesłanego dokumentu. Spróbujmy teraz wymusić na serwerze inną odpowiedź. Jak wiemy, wpisując do przeglądarki "google.pl" w jakiś magiczny sposób adres zmienia się na "www.google.pl" - zobaczmy dlaczego: Jak widzimy - serwer odpowiedział informacją o tym, że dokumentu należy szukać pod innym adresem, podając go przy okazji w nagłówku Location. A co będzie, jeśli w ogóle nie podamy nagłówka Host? Przecież w wielu przypadkach (np. domowy RPi) na serwerze jest tylko jeden serwis... spróbujmy. Tym razem zamiast z Google (który trochę nietypowo traktuje błędy) połączymy się z serwerem Forbota: Jak widać - serwer traktuje to jako błąd. Ale co z protokołem HTTP/1.0? Wtedy przecież, w początkach Internetu, wystarczył adres IP... Spróbujmy! Widzimy więc, że tym razem serwer nie uznał naszego zapytania za błędne. Natomiast to, że nic konkretnego nam nie napisał oprócz tego, że działa - wynika z faktu, że bez nagłówka Host nie jest w stanie stwierdzić do którego serwisu wysłane jest zapytanie! I to na dziś tyle - w następnej części spróbujemy zrobić prosty serwer. Spis treści serii artykułów: Protokół HTTP w zastosowaniach IoT - część 1: trochę teorii Protokół HTTP w zastosowaniach IoT - część 2: budujemy serwer Protokół HTTP w zastosowaniach IoT - część 3: tworzymy klienta
  6. Chcielibyście, aby urządzenie z ESP8266 wysyłało do Was e-maile lub powiadomienia push? Może zastanawialiście się, jak połączyć najnowsze DIY z asystentem głosowym od Google? Te pozornie trudne zadania można bardzo łatwo rozwiązać za pomocą popularnego IFTTT! [blog]https://forbot.pl/blog/praktyczny-poradnik-laczenia-esp-z-ifttt-if-this-then-that-id41663[/blog] IFTTT_przyklad_IO.zip
  7. Interaktywna zabawka dla kotów gwarantująca zabawę w każdej chwili, żaden kot nie oprze się uciekającej czerwonej kropce. Jest to niewielkie pudełeczko z wbudowanym modułem wifi i banalnie prostą obsługą. Główne funkcje: sterowanie dowolnym urządzeniem z przeglądarką internetową. losowe ruchy lasera o zmiennej prędkości. ustawianie czasu jak długo ma działać. ustawianie harmonogramów automatycznego włączenia. regulacja jasności lasera. regulacja zakresu ruchu i prędkości lasera. możliwość sterowania z dowolnego miejsca na świecie przez internet. sterowanie za pomocą google asystenta. prosta konfiguracja. Zabawka może być zasilana dowolną ładowarką od telefonu, może to być również powerbank. Przy pierwszym uruchomieniu zabawki, zostanie uruchomiona nowa sieć wifi ..::LASERCAT::.. wystarczy połączyć się z nią i wskazać naszą sieć domową, a po zrestartowaniu urządzenie automatycznie podłączy się do niej i już możemy korzystać z zabawki. Z tyłu znajduje się wejście zasilania micro USB, jak w telefonie oraz przycisk. Krótkie wciśnięcie to włączenie/wyłączenie lasera, przytrzymanie przez 3 sek. powoduje rozłączenie obecnej sieci wifi i uruchomienie ponownej konfiguracji. Gdy urządzenie jest już podłączone do naszej sieci wifi to po wpisaniu adresu zabawki w przeglądarce internetowej zobaczymy panel sterujący: Zastosowany laser jest małej mocy, taki sam jak w innych tego typu zabawkach czy bazarkowych wskaźnikach. Dodatkowo dla bezpieczeństwa jest możliwość ustawienia mocy świecenia lasera od 0% do 100%. Pozostałe ustawienia pozwolą dostosować zakres ruchów do miejsca w którym znajduje się zabawka i określić czy kropka ma się poruszać tylko po podłodze, czy częściowo wchodzić na ścianę co może dostarczyć dodatkowej frajdy dla kota. Schemat jest bardzo prosty: Widok płytki PCB: Jak zwykle w garażowym zaciszu metodą "żelazkową" - elektronicy używają żelazka zdecydowanie częściej jak ich partnerki - powstaje mała płytka. Płytka została zabezpieczona przed utlenianiem lakierem PVB16. Całą robotę wykonuje tutaj tani i lubiany układ ESP8266, który posiada moduł WiFi. Dioda laserowa jest zasilana źródłem prądowym dodatkowo kluczowanym z PWM-a co pozwala płynnie regulować jasność od 0% do 100%. Skoro już bebechy mam, to teraz trzeba to wszystko złożyć w całość. Obudowę wykonałem ze sklejki wyciętej laserowo, składanej na wczepy palcowe. No to składamy: Dodanie serwomechanizmów do których przyczepiony jest laser. Oczywiście bez trytytki projekt by się nie udał No i sprzęt jest gotowy, ale co nam po sprzęcie jak on zupełnie nie wie co ma robić? Nie wie, że teraz trzeba machać tym laserkiem tak żeby kot ganiał w tę i we w tę Trzeba to wszystko zaprogramować. Uruchamiamy nasze ulubione IDE czyli Visual Studio Code z wtyczką PlatformIO i zaczynamy pisać program. Soft został napisany z wykorzystaniem Arduino Core, a na całość składa się kilka części: główny program sterujący silniczkami, wyznaczanie losowych ścieżek. serwer www, który udostępnia ładny panel sterowania. konfiguracja sieci WiFi z wykorzystaniem Captive Portal. multicast DNS. stworzenie strony www (html + css + javascript). obsługa komunikacji po websockecie. zdalne wgrywanie plików przez stronę www, np. zmiana wyglądu głównej strony. zdalna aktualizacja oprogramowania bez zbędnych kabli. W oczekiwaniu na gotowe oprogramowanie tester cierpliwie czeka. Panel sterujący dostępny z poziomu przeglądarki internetowej nie jest hostowany nigdzie na zewnątrz, całość znajduje się w zabawce, wykorzystałem bootstrapa plus kilka dodatkowych komponentów. Zastosowany mDNS pozwala połączyć się z urządzeniem wpisując w przeglądarce adres "lasercat.local" zamiast adresu IP. Niestety na chwilę obecną android nie wspiera tego typu rozwiązań, ale na iPhonach działa to bardzo dobrze. Na filmie mała prezentacja z trochę już wybawionym głównym testerem Elroyem a poniżej pokazano jak można włączyć zabawkę po prostu mówiąc do telefonu "Ok Google, włącz laser"
  8. Pojawiła się potrzeba wykonania prostego sterownika do bramy garażowej, który miałby powiadamiać mieszkańców czy aktualnie garaż jest zamknięty czy otwarty oraz w dowolnej chwili sprawdzić status. Tak powstało niewielkie urządzenie montowane na szynę DIN. Jest zasilane z dowolnej ładowarki od telefonu, posiada zabezpieczenie przed odwrotną polaryzacja zasilania. Sterownik ma kilka wejść/wyjść; IN1 - dolna krańcówka od zamknięcia garażu. IN2 - górna krańcówka od pełnego otwarcia garażu. wyjście przekaźnikowe NO do zdalnego otwierania/zamykania bramy. RS485 - pozwala podłączyć czujnik odległości wykrywający czy auto jest w garażu. czujnik temperatury DS18B20. przycisk do resetowania ustawień WiFi i uruchomienia ponownej konfiguracji. W sterowniku zastosowałem popularny układ ESP8266 w wersji WemosD1 mini. Jak widać za wiele rzeczy tu nie ma, oprócz ESP znajduje się przekaźnik, DS18B20 oraz transceiver RS485. Projekt miał być prosty, szybki i jednostkowy dlatego nie zastosowałem dodatkowych stopni ochrony wejść w postaci np. optoizolacji. Tradycyjnie płytka powstała na żelazku i wytrawiona w kwasie. Polutowana i zabezpieczona lakierem do PCB. Schemat ideowy: Wspomniany wcześniej czujnik odległości jest zbudowany z wykorzystaniem ultradźwiękowego czujnika HC-SR04 i Arduino Nano, które cyklicznie wysyła informacje do głównego sterownika. Schemat czujnika: Sterownik ma zaimplementowany serwer WWW co pozwala na sterowanie praktycznie dowolnym urządzeniem z przeglądarką. A panel sterowania prezentuje się tak: Dodałem obsługę powiadomień push na telefon z wykorzystaniem mechanizmu IFTTT (if this then that). Wystarczy zainstalować tą aplikacje na telefonie, a w sterowniku wprowadzić unikalny klucz aplikacji powiązany z konkretnym telefonem. Aktualizacja oprogramowanie wykorzystuje mechanizm OTA i sprowadza się do wgrania pliku przez panel www. Dodatkowo wystawione jest proste API, które pozwala na integracje z większością systemów smart home typu Domoticz, Home Assistant itp.
  9. Witajcie. Chciałbym Wam przedstawić prosty sposób wykorzystania modułu ESP-32, który użyłem do stworzenia urządzenia, za pomocą którego możecie śledzić poziom zainteresowania wybranym repozytorium GitHub. Dzięki wbudowanym dwóm wyświetlaczom, będziecie na bieżąco informowani o: aktualnej liczbie gwiazdek dziennej liczbie gwiazdek aktualnej liczbie obserwujących aktualnej liczbie forków Początkowo chciałem skorzystać ze starszego modułu ESP8266 12-F, jednak napotkałem problem który objawiał się przy wykonywaniu requesta do api GitHub. Podczas oczekiwania na odpowiedź z serwera, na wyświetlaczach zanikały wszystkie cyfry i wyglądało to jakby coś było nie tak z urządzeniem. Z pomocą przyszedł układ ESP-32, który oparty jest na dwóch rdzeniach. Była to świetna okazja aby zapoznać się z tym modułem, ponieważ wcześniejsze projekty wykonywałem na ESP8266. Użycie nowszej odsłony ESP, pozwoliło mi wysyłać requesty asynchronicznie. Do działania ramki wymagane jest połączenie z siecią poprzez WiFi. Tutaj świetnie sprawdziła się biblioteka "WiFi Manager", która umożliwiła mi szybkie podłączenie ramki do dowolnej sieci. Jeżeli chodzi o zasilanie to jest to napięcie 5V, które podaje poprzez wtyki USB. W obudowie ramki znajdują się trzy przyciski. Pierwszy służy jako włącznik. Pozostałe dwa to włączniki chwilowe. Po wciśnięciu pierwszego na wyświetlaczu prezentowany jest adres IP, który wykorzystujemy przy konfiguracji. Natomiast drugi przycisk resetuje liczbę gwiazdek z dnia. Do układu podłączona jest 3 kolorowa dioda LED, która informuje nas o stanie połączenia: CZERWONY – brak sieci, błąd podczas pobierania danych ZIELONY – połączenie sieciowe ustanowione, dane pobrane poprawnie NIEBIESKI – tryb access pointu ( zanik sieci ) Domyślnie odświeżanie danych odbywa się co 90 sekund. Oczywiście interwał można zmienić. Ale należy uważać, aby nie wykonywać do api GitHub więcej niż 60 requestów na godzinę, ponieważ serwer ma ustawiony RateLimit. Po przekroczeniu ilości zapytań zostaniemy zablokowani na godzinę. Jak już wspomniałem wyżej, pod adresem IP jaki przydzielony został do urządzenia działa prosty web server, który serwuje nam stronę z konfiguracją, gdzie musimy wprowadzić nazwę użytkownika repozytorium oraz nazwę repozytorium które chcemy obserwować. Po zapisaniu konfiguracji w pamięci EEPROM urządzenie jest restartowane i gotowe do użycia. Dodatkowym atutem urządzenia jest automatyczna aktualizacja oprogramowania poprzez HTTPS OTA. Sprawdzanie wersji następuje podczas uruchomienia oraz po północy. Urządzenie jest w pełni bezobsługowe. Gdy wystąpi zanik sieci, ESP cały czas będzie próbowało nawiązać połączenie za pośrednictwem zapamiętanych poświadczeń. Jeśli sieć nie będzie dostępna, przełączy się w tryb access pointu ( ssid: "GITHUB-FRAME"). Jeśli nie zostanie wybrana nowa sieć w menadżerze sieci, to po czasie 3 minut, nastąpi restart i proces się powtórzy. Tak pokrótce, wygląda zasada działania całego układu. Poniżej przedstawię Wam główne etapy budowy całej ramki. A więc zaczynamy. LISTA ELEMENTÓW: ESP-32 WROOM DevKit 1.0 – 1 szt. Wyświetlacz LED 4-Cyfrowy TM1637 – 0.56" – 1 szt. Wyświetlacz LED 4-Cyfrowy TM1637 - 0.36" – 1 szt. 4-pinowy przewód, żeński – żeński – raster 2.54 – 4 szt. Gniazdo + wtyk, JST – JST – 2 szt. Gniazdo + wtyk, mikro JST – mikro JST – 2 szt. Płytka uniwersalna PCB 50x70mm PI-01 – 1 szt. Rezystor węglowy – 220 ohm – 5 szt. Rezystor węglowy – 2,2k ohm – 3 szt. Zworki do płytek stykowych - 1 zestaw Wtyk goldpin kątowy 4 pinowy, raster 2,54mm – 4 szt. Dioda LED 5mm RGB wsp. Anoda – 1 szt. Dioda LED 3mm biała – 3 szt. Przełącznik chwilowy okrągły – 10mm – 2 szt. Przełącznik kołyskowy ON-OFF – 1 szt. Kabel USB A – USB micro – 1 szt. Zasilacz 5V z gniazdem USB A – 1 szt. Rurki termokurczliwe - 1 szt. Ramka IKEA RIBBA – 21x30cm ( ważne żeby była dość głęboka, aby zmieścić elektronkę ) – 1 szt. Papier samoprzylepny do drukarki – 1 szt. Rura elektroinstalacyjna RLM 16 – 1 szt. NARZĘDZIA: Lutownica Cyna Obcążki Wiertarka lub wkrętarka Wiertła: 7, 10, 13 Pistolet do kleju na gorąco Nóż Drukarka ZAŁOŻENIA: Stabilność działania Intuicyjna obsługa Szybka adaptacja w miejscu instalacji Estetyka Plan Początkowo ramka miała powstać pod konkretnie wybrane repozytorium i wyświetlać tylko liczbę gwiazdek. Ale stwierdziłem, że i tak pobieram inne dane z endpointa api to czemu miałbym ich nie wyświetlić. Postanowiłem, że dodam dwa nowe klucze: "forks" oraz "watchers" i wyświetlać je kolejno w 5 sekundowym odstępie czasowym. Jeżeli chodzi o repozytorium, to dając możliwość wprowadzenia własnych ustawień url-a, zwiększyłem tym samym skalowalność przedsięwzięcia. Do tego doszły automatycznie aktualizacje software-u. Więc taką ramkę może stworzyć każda osoba, która chociaż trochę ma pojęcie o informatyce i niekoniecznie zna się na programowaniu. BUDOWA Prace nad ramką rozpocząłem od budowy prototypu metodą na "pająka". W tym celu skorzystałem z płytki prototypowej, przycisków typu "tact switch", paru zworek oraz kilkunastu przewodów do połączenia wszystkiego w całość. Całe oprogramowanie zostało napisane za pośrednictwem Arduino IDE czyli w języku C. Gdy miałem już działający prototyp, rozpocząłem prace nad przeniesieniem całości na uniwersalną płytkę PCB. Zadanie wydawałoby się proste ale wymagało procesu planowania. W tym celu wykorzystałem oprogramowanie Fritzing. Oprogramowanie to umożliwia stworzenie całej dokumentacji projektu. Na tę chwilę wykorzystałem tylko narzędzie do stworzenia szkicu płytki prototypowej. Mając gotowy już projekt z rozlokowaniem wszystkich elementów i połączeń. Mogłem przystąpić do lutowania. Jak widać na zdjęciach, podczas montażu elementów używałem uchwytu, który stabilnie trzyma płytkę w miejscu. Bardzo ułatwił mi pracę. Po przylutowaniu wszystkich elementów elektronicznych, wlutowałem przewody z gniazdami, dzięki którym będę mógł odłączyć układ od samej konstrukcji ramki Teraz przyszedł czas na najtrudniejszy etap jakim było dostosowanie drewnianej ramki do potrzeb projektu. W programie Photoshop stworzyłem szablon do wiercenia i wycinania potrzebnych otworów. Szablony te znajdziecie również w repozytorium projektu. Po wydrukowaniu szablonu przykleiłem kartkę do “pleców ramki” i wyciąłem wszystkie otwory. Trochę trzeba się do tego przyłożyć i mieć sporo cierpliwości. Cięcie, pasowanie, cięcie, pasowanie aż do skutku. Ufff. W końcu mogłem przystąpić do zamontowania wyświetlaczy oraz diod LED. Z pomocą przyszedł mi klej na gorąco. Trzyma mocno i pewnie, wystarczająco do tego typu prac. Trzy diody LED umieściłem w przyciętych krążkach z białej rury pcv ( tych do prowadzenia przewodów po ścianach ) a górę zaślepiłem kawałkiem plastiku w którym zamocowałem diody. A tak całość prezentuje się od frontu Za pomocą 4 żyłowych przewodów zakończonych wtykami żeńskimi, połączyłem wszystkie elementy z główną płytką. W celu szybszej identyfikacji przewodów, oznaczyłem każde połączenie za pomocą lakierów do paznokci ( pozdrawiam swoją żonę Magdalenę ). Główny układ przykleiłem do pleców ramki również za pomocą kleju na gorąco. Na koniec pomalowałem cały front na biało farbą emulsyjną, ponieważ papier który używa się w drukarkach ma małą gramaturę co sprawia, że jest półprzezroczysty. Dzięki podbiciu koloru tła biel będzie intensywniejsza. W ostatecznej wersji grafikę wydrukowałem na papierze fotograficznym, który jest na tyle gruby, że malowanie okazało się być zbędne. SOFTWARE Cały program opiera się na dwóch pętlach. Pierwsza pętla Task1, sprawdza czy użytkownik wprowadził url repozytorium z którego mają zostać pobrane dane. Jeżeli konfiguracja została wprowadzona, program wywołuje funkcję getData(), która odpowiedzialna jest za pobranie danych z API. Interwał tej pętli definiuje zmienna requestInterval, która domyślnie posiada wartość 90 ( czyli 90 sekund). Druga pętla Task2, służy do wyświetlania odpowiednich danych na wyświetlaczach oraz podświetlania ikon. Tutaj również sprawdzany jest stan na pinach 27 i 15 ( przyciski BUTTON_IP oraz BUTTON_RESET_TODAY). Interwał tej pętli to około 15 sekund. Po północy następuje sprawdzenie dostępności nowszej wersji oprogramowania oraz resetowany jest licznik gwiazdek otrzymanych w ciągu całego dnia. Poniżej znajdziecie link do repozytorium z projektem: OPROGRAMOWANIE + SZABLONY DO DRUKU PODSUMOWANIE Przyznam się szczerze, że prototyp urządzenia miałem już gotowy rok temu. Ale ze względu na gruntowny remont mieszkania musiałem odsunąć hobby na dalszy plan. Rozciągnięcie projektu w czasie sprawiło, że przy każdym powrocie zawsze coś zmieniałem, rozbudowywałem. Wszystko wtedy można przemyśleć kilka razy i na spokojnie zastanowić się nad rozwiązaniem jakiegoś problemu. Na co dzień zajmuję się programowaniem front-endu, ale dzięki takim projektom mogę połączyć moje główne zainteresowania: majsterkowanie, elektronikę, grafikę i jak już wcześniej wspomniałem, programowanie i stworzyć coś namacalnego i cieszącego oko. Zachęcam wszystkich do twórczego działania i poszerzania swojej wiedzy. Tego typu projekty dadzą Wam satysfakcję, świetną zabawę oraz sporo nauczą. Także klawiatury, lutownice, piły, śrubokręty, wiertarki w dłoń i do działania! Instrukcję już macie Do następnego projektu! Pozdrawiam.
  10. Parę lat temu podczas remontu u rodziców postanowiłem usprawnić im parę rzeczy w mieszkaniu. Stworzyłem sterownik, który integrował ze sobą sterowanie oświetleniem i ogrzewaniem. Pomimo prostej obsługi nie przewidziałem tego, że moi rodzice będą bali się tego używać, myśleli że coś zepsują... niestety są na bakier z elektroniką. Tak powstała druga wersja, która jest dla nich absolutnie bezobsługowa w kwestii ustawień czy sterowania ogrzewaniem. Podstawowe cechy komunikacja Wifi integracja z Firebase zdalny dostęp z dowolnego miejsca na świecie Odtwarzanie komunikatów głosowych sterowanie za pomocą asystenta Google pilot radiowy 2,4GHz sterowanie oświetleniem sterowanie ogrzewaniem dwupunktowy pomiar temperatury detekcja otwartego okna automatyczne aktualizowanie czasu z serwera NTP łatwe dokładanie bezprzewodowych czujników, np. zalania duży i czytelny wyświetlacz Tym razem uwzględniłem obawy rodziców i w każdej chwili mam dostęp do wszystkich ustawień. Dla niecierpliwych filmik (polecam włączyć dźwięk) Główny moduł Całe sterowanie oparłem o układ ESP12. Jest to układ o bardzo dużych zasobach z wbudowanym wifi. Niestety ma on bardzo mało wyprowadzeń, dlatego aby obsłużyć wszystkie dodatkowe elementy takie jak: LCD ST7565 DS18B20 Enkoder Trzy przyciski Kilka wyjść Moduł MP3 (JQ8400-FL) RFM73 Wykorzystałem 16 bitowy ekspander na SPI MCP23S17. Wymagało to dostosowania kilku bibliotek np. LCD czy enkodera, aby zamiast pinów ESP używały MCP23S17. To było największym wyzwaniem dla mnie, aby zrealizować obsługę trzech układów SPI, gdzie dwa z nich były częściowo sterowane poprzez pierwszy czyli MCP23S17. Dodatkowo przydzielanie czasu na poszczególny element trzeba było odpowiednio rozdzielić, aby obsługa enkodera nie gubiła kroków. Zdalny dostęp Sterownik działa jako klient w sieci wifi, nie chciałem przekierowywać portów na routerze, ani stawiać dodatkowego VPNa. Wykorzystałam tutaj RealTime Database od Google czyli Firebase. Jest darmowe, względnie proste w obsłudze wprost z układu ESP, a dostęp do Firebase posiada każdy kto ma konto w Google. Wykorzystanie tego mechanizmu daje nam bardzo duże możliwości. Możemy hostować tam własną stronę, która będzie naszym frontendem, możemy wykorzystać “cloud functions”, które są praktycznie zasobami node.js, jest to bardzo fajne, bo możemy mieć własny serwer node.js w chmurze. Mając cloud functions i bazę danych, możemy z łatwością podpiąć pod to inne usługi, np. asystenta Google, który pozwoli sterować urządzeniem naturalną mową a nie nagranymi wcześniej próbkami komend. Pod nasz system możemy podpiąć również IFTTT (if this then that) i jeszcze bardziej zautomatyzować obsługę urządzenia. ESP co kilka sekund odczytuje bazę danych z Firebase i sprawdza czy stan jakiegoś elementu uległ zmianie, jeśli tak to znaczy, że zdalnie zmieniliśmy parametry pracy. W drugą stronę, jeśli to sterownik lokalnie zmieni jakąś wartość to od razu aktualizuje ją w Firebase. Asystent Google i komunikaty głosowe Rodzice swoje lata już mają i pamięć nie ta, dlatego dołożyłem system komunikatów głosowych. Pilot, który będzie zawsze pod ręką tuż obok pilota od TV, będzie (czeka na obudowę) jasno opisany i wystarczy nacisnąć jeden przycisk a sterownik powie jaka jest temperatura w mieszkaniu, jaka jest na zewnątrz, czy ogrzewanie aktualnie pracuje albo ile razy dzisiaj było włączane. Pomyślałem, że fajnym dodatkiem będzie wgranie również instrukcji obsługi, gdzie sterownik powie do czego służą poszczególne przyciski. Ze sterownikiem można rozmawiać w sposób naturalny z wykorzystaniem asystenta Google, nie musimy być nawet w pobliżu sterownika, mówimy do telefonu czy komputera znajdując się w dowolnym miejscu na świecie. Sam Google asystent nie steruje bezpośrednio urządzeniem, z wykorzystaniem mechanizmu Dialogflow, który bazuje na machine learning zmienia parametry pracy w Firebase, a urządzenie docelowe pobiera je i wykonuje. Zależności między komponentami pokazałem na poniższej grafice. Moduł radiowy 2,4GHz W sterowniku znajduje się RFM73, jest to niewielki układ radiowy wykorzystany tutaj jako pilot, we wcześniejszej wersji tradycyjny pilot na podczerwień kiepsko się sprawdzał, nie zawsze było się w polu widzenia odbiornika. Dodatkowe czujniki Posiadając na pokładzie układ RFM73 możemy dowolnie rozbudować system o dodatkowe czujniki, np. czujnik zalania w łazience, czujnik oświetlenia czy dodatkowe czujniki temperatury. Dokładanie dodatkowych czujników nie wiąże się z przeprogramowaniem głównego sterownika, ponieważ wszystkie reguły obsługi możemy umieścić w Firebase, a sterownik je pobierze. Poniżej kilka fotek, a jako trzecią rękę polecam taki stojak:
  11. Witajcie Chciałbym przedstawić tu swój projekt Sterownika do akwarium pracującego w systemie automatyki domowej opartej na Domoticzu. Pomysł i historia projektu Ponad rok temu wpadliśmy z żoną na pomysł założenia akwarium, pomysł jak pomysł, ale im więcej czytałem tym więcej kombinowałem, a jako że jestem elektronikiem to i pomysłów przybywało mi z każdym dniem Pierwsza próba budowy sterownika polegała na wykorzystaniu Arduino nano i stworzeniu samodzielnego układu pracującego lokalnie. Tyle że już w trakcie pisania kodu stwierdziłem że sterowanie sterowaniem, ale fajnie by było móc zrobić coś zdalnie, a przede wszystkim móc sprawdzić zdalnie co dzieje się w akwarium (temperatura, stan lamp itp). Tak zaczęła się moja przygoda z Domoticzem i szeroko pojętym IoT. Opis projektu Sam sterownik zbudowany jest na Arduino Pro mini, natomiast do komunikacji z serwerem wykorzystana jest platforma Mysensors zbudowana na modułach NRF24L01. Jak wcześniej pisałem sterownik pracuje w systemie mojej domowej automatyki opartej o serwer Domoticza, pracujący na RaspberryPi 3B. Na chwile obecną sterownik realizuje następujące funkcje: cztery kanały ON/OFF (przekaźniki 10A 250V), sterowanie: grzałka, filtr, falownik, chłodzenie (wentylatory w pokrywie). Z uwagi na bezpieczeństwo mieszkańców akwarium filtr podpięty jest pod styki NC przekaźnika, tzn, domyślnie jest on włączony, a możemy go wyłączyć. 16 kanałów PWM (z tranzystorami IRF520 jako elementy wykonawcze), sterowanie lampami LED, ściemnianie podświetlenia wyświetlacza, sterownie pracą falownika, rezerwa. funkcja "Karmienie" i "Serwis", załączane z lokalnej klawiatury, pierwsza umożliwia czasowe (5min) wyłączenie filtra, falownika i wentylatorów, druga umożliwia wyłączenie trybu automatyki i przejście w tryb serwisowy (przydatne przy pracach porządkowych w zbiorniku). pomiar temperatury w akwarium (czujniki DS18B20), układ zrobiony jest tak że może obsłużyć nieskończenie wiele termometrów, w praktyce w mniejszym zbiorniku mam 2 w większym 3 i to wystarcza. Dodatkowo można wybrać czy automatyka sterowania grzaniem/chłodzeniem korzysta z jednego z termometrów czy z wartości średniej wszystkich wskazań. zabezpieczenie przed przegrzaniem i wychłodzeniem wody (bezwarunkowe wyłączenie/włączenie grzania/chłodzenia w temperaturach skrajnych) pomiar temperatury zewnętrznej pomiar poziomu oświetlenia zewnętrznego Schemat blokowy sterownika przedstawiam na poniższym rysunku Jak widać większość układów pracuje na magistrali I2C, sprawia to że całość jest projektem rozwojowym, a wszelkie możliwe dodatkowe funkcje ograniczone są tylko wyobraźnią i.... pojemnością pamięci wykorzystanego Arduino Kanały PWM zrealizowane są na module PCA 9685, a jako elementy wykonawcze służą tranzystory IRF520 (oczywiście można zastosować inne MOSFETy), moduł z przekaźnikami podłączony jest do magistrali I2C poprzez expander PCF8574. Zarządzanie sterownikiem Podstawowe sterowanie pracą sterownika możliwe jest z lokalnej klawiatury, natomiast i informacje o pracy sterownika (temperatura, czas, poziom światła poszczególnych kanałów LED, stan urządzeń wykonawczych itp) wyświetlane są bezpośrednio na wyświetlaczu LCD 20x4. Pełne sterownie możliwe jest poprzez serwer Domoticza, za pośrednictwem strony www (poniżej przykładowy zrzut ekranu) i/lub aplikacji na androida. Teraz czas na kilka zdjęć elementów sterownika: Moduł arduino z przetwornica 3,3V, expanderem PCF 8574 i modułem NRF24L01 zmontowane są na płytce uniwersalnej, która osadzona jest na module przekaźników tworząc "kanapkę" Widok "kanapki" płytki uniwersalnej z arduino i modułu z przekaźnikami Moduł wykonawczy na tranzystorach IRF (sześć kanałów do sterowania lampami LED) Moduł przekaźników Wyświetlacz LCD Problemy napotkane przy realizacji projektu konieczne było wymuszenie niższej częstotliwości pracy platformy mysensors z powodu zegara jakim taktowane jest zastosowane arduino niestabilna praca modułów NRF, konieczne było dodanie elektrolitów (dałem 330u bo takie miałem :)) jak najbliżej pinów zasilających modułu (lutowałem bezpośrednio na tych pinach) okresowe zawieszanie i/lub przekłamanie odczytu temperatury z termometrów DS18B20, nie doszedłem przyczyny, wiec dodałem możliwość zdalnego resetu arduino (wiem że to tylko zaleczenie problemu a nie rozwiązanie, ale tymczasowo zdaje egzamin) zbyt małą pamięć programu zastosowanego modułu arduino, zastanawiam się czy nie przejść na ESP8266 z EasyESP konieczność zbudowania obudowy
×
×
  • Utwórz nowe...