Skocz do zawartości

Przeszukaj forum

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

  • Szukaj wg tagów

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

Typ zawartości


Kategorie forum

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

Szukaj wyników w...

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


Data utworzenia

  • Rozpocznij

    Koniec


Ostatnia aktualizacja

  • Rozpocznij

    Koniec


Filtruj po ilości...

Data dołączenia

  • Rozpocznij

    Koniec


Grupa


Znaleziono 21 wyników

  1. Arduino jest taką fajną platformą, że chyba podoba się tylko osobom zainteresowanym. Dlatego też moja żona zawsze skacze z radości, gdy dokupuje sobie nowe przekaźniki, czujniki czy też „cholerne kable walające się po szafkach”. Ja natomiast uwielbiam wykonywania przedmiotów użytkowych, mających zastosowanie w życiu codziennym. Wiem, że system podlewający zioła nie jest niczym nowym i skomplikowanym, jednakże wbrew pozorom dla człowieka nie mającego wiele wspólnego z programowaniem i elektroniką, może być kłopotliwe. Tym bardziej, że informacje ułatwiające złożenie takiego zestawu są rozproszone w sieci i nie znalazłem jeszcze dobrego tutorial -a po polsku. Dlatego postaram się opisać to jak najdokładniej, aby ktoś taki jak ja mógł to zrozumieć Projekt na początku miał kilka założeń: 1. Znalezienie doniczki, która będzie dobrze wyglądała (nie zostanie skazana na banicję przez innego mieszkańca) 2. Doniczka musi mieć zbiorniki na wodę na tyle duże, żebym nie musiał martwić się podlewaniem ziół w kuchni, które umierały mi bardzo szybko – albo za mało wody albo odwrotnie. 3. System podlewania ma być indywidualny dla każdego rodzaju z ziół i działać raz lub dwa dziennie. 4. Wszystko musi być najtańsze jak się da. 5. Projekt musi dobrze wyglądać. Znalezienie odpowiedniej doniczki, która zmieści się na szafce (i będzie dobrze wyglądać) okazał się niemożliwy do zrealizowania. Nic takiego na rynku nie ma, dlatego też musiałem zrobić ją sam. Najkorzystniejszym cenowo materiałem okazało się PCV o grubości 3 mm. Występuje w kolorze białym i czarnym, są sklepy internetowe gdzie możemy zamówić już przycięte formatki. Poniżej pokazuję projekt doniczki i rozpisane wielkości formatek. Formatki z PCV potrzebne do wykonania doniczki ze zbiornikiem wody (wymiary w mm, grubość 3 mm): 1. 183 x 200 mm – 2 szt 2. 400 x 180 mm – 2 szt 3. 400 x 200 mm – 1 szt 4. 392 x 120 mm – 3 szt Zdjęcia i projekt: Elementy kleimy najpierw kropelką/super glue lub innym klejem kontaktowym. Później uszczelniamy połączenia poprzez nakładanie kleju do rur PCV (z atestem dla instalacji wodnej) za pomocą strzykawki. Po prostu nabieramy klej i grubą warstwę wciskamy w szczeliny. Klej schnie 24h, a każdy z elementów trzeba zabezpieczyć oddzielnie, co więcej nie ma możliwości aby zrobić to już po złożeniu doniczki – nie będziemy mieć dostępu do niektórych krawędzi. Dlatego uszczelniać klejem do PCV należy etapami. Na końcu warto zrobić test szczelności, w moim przypadku robiłem 3 poprawki. Również warto przed wklejeniem tylnej ściany doniczki zamontować do niej pompki wody oraz wyprowadzić okablowanie na zewnątrz. W moim przypadku po prostu wywierciłem dziurę, którą uszczelniłem klejem do PCV. Pompki mogą być dowolne, pracujące z odpowiednim napięciem dla naszego źródła zasilania (u mnie 5V) Ponieważ doniczka z PCV nie spełnia przynajmniej dwóch punktów z założeń projektu. Musiałem wykombinować osłonkę, która będzie wodoodporna i umożliwi dolewanie wody od góry pojemnika. Padło na hexagonalną sklejkę, akurat najdroższy z elementów tego projektu. Formatki, osłonka drewniana (wymiary w mm, grubość 15 mm): 1. 220 x 215 – 2 szt 2. 415 x 215 – 1 szt 3. 415 x 40 – 1 szt 4. 415 x 190 – 1 szt 5. 50 x 415 – 1 szt 6. 35 x 415 – 1 szt Wszystko jest połączone na konfirmaty bo tak mi pasowało wizualnie. Można zastosować też wkręty lub po prostu ją skleić na kołki. Elementy elektroniczne: 1. Wemos D1 mini (lub inna płytka oparta na ESP8266, np. NodeMCU) 2. Czujnik wilgotności gleby 3. Przekaźnik z logiką 3,3V 4. Moduł z zasilaniem 5v i 3,3V 5. Pompki akwarystyczne zasilane z 5V 6. Przetwornik ADC - ADS1115 Tylna ściana doniczki specjalnie ma niepełne plecy, aby łatwo było zamontować całą elektronikę. Do sterowania wykorzystałem D1 Mini oparty na ESP8266 ze względu na wbudowane wifi. Ponieważ wykorzystuje 3 sztuki czujników wilgotności gleby, niezbędny był przetwornik ADS1115, który może odczytywać sygnał ADC dla czterech urządzeń. To ma znaczenie bo D1 Mini mógłby obsługiwać tylko jeden z nich, więc musiałbym w projekcie wykorzystać aż 4 takie urządzenia. Dodatkowo wprowadziłem zewnętrzny układ zasilania (pompki nie dawały rady przy natężeniu prądu z D1) oraz przekaźniki uruchamiające pompki w zależności od wilgotności. Sterowanie oparłem na gotowym rozwiązaniu ESP Easy z którego korzystam już przy innych czujnikach i które trochę już znam. Tak jak pisałem wcześniej, nie potrafię programować i nie to mnie kręci w tej platformie. Natomiast ESP Easy posiada web UI i ogólnie jest łatwo konfigurowalne bez znajomości języka programowania. W tym projekcie jedyne czego trzeba się nauczyć to reguły, banalnie proste do zrozumienia. Instalacja i konfiguracja jest dobrze opisana na stronie głównej projektu: https://www.letscontrolit.com/wiki/index.php/ESPEasy Tak wygląda strona z urządzeniami w ESP po odpowiedniej konfiguracji. Pierwsze trzy to czujniki wilgotności, kolejne trzy to przełączniki do sterowania przekaźnikami uruchamiającymi pompki. Jest jedna rzecz, której nie znalazłem w żadnym poradniku dotyczącym przygotowania systemu do podlewania kwiatków. Jest to kalibracja czujników. Powinno to być oczywiste i logiczne, jednak dla mnie nie było. Na początku przyjąłem, że wartości maksymalne przetwornika należy przyjąć jako referencyjne i na podstawie tego określać procent wilgotności gleby. Mój błąd był większy niż moje samozadowolenie. Dopiero po ciemnej stornie anglojęzycznego internetu znalazłem informacje o kalibracji i jej sposobie. Należy sprawdzić jego wskazania w suchej ziemi oraz w mokrej. Więc najlepszym sposobem jest wysuszenie odpowiedniej ilości w piekarniku: Zanotowaniu wskazań dla wysuszonej i ostygniętej ziemi ( przesypałem ją do słoika żeby odpowiednio wsadzić czujnik). A później powtórzeniu pomiarów dla ziemi mokrej ale bez stojącej gleby. Później wystarczy wpisać nasze wskazania w webUI ESP Easy, która ma już dwupunktową kalibrację dla przetwornika ADS1115 i wskazaniu naszych pomiarów. Przy pomiarze gdzie była sucha ziemia wpisujemy 0, przy mokrej 100. Dzięki temu mamy od razu podany wynik w procentach. Ostatnim etapem jest napisanie reguł sterujących podlewaniem. W moim przypadku codziennie o 7.00 rano dokonywany jest pomiar wilgotności gleby, jeśli wilgotność jest niższa niż 55% (lub 45% w przypadku jednego z nich) uruchamiana jest pompka na czas kilku sekund. Później odczekuje 30 sek i znowu dokonuje pomiaru. Jeśli wilgotność jest niższa, znowu dostarczana jest woda. Algorytm powtarza się łącznie 3 razy dla każdego z czujników. Wystarcza to spokojnie aby utrzymać odpowiednią wilgotność gleby. Oczywiście można było się pokusić o wprowadzenie zmiennych zależnych od odchylenia od normy. Nawet nie byłoby to bardzo trudne, jednakże całość miała być prosta. Reguły: On Clock#Time=All,07:00 do // codziennie o 7.00 rano wykoanć taskrun,1 // pomiar z zadania nr 1 if [Z1#Analog]<55 // sprawdzić czy jest niższy niż 55 pulse,14,0,20000 // jeśli jest to uruchomić pompkę na 20 sek, jeśli nie nie podejmuje zadań endif timerSet,1,30 // timer uruchominy na 30 sek aby woda mogła wsiąknąć taskrun,1 // pomiar z zadania nr 1 if [Z1#Analog]<55 // itd... pulse,14,0,10000 endif timerSet,2,30 taskrun,1 if [Z1#Analog]<55 pulse,14,0,8000 endif endon On Clock#Time=All,07:00 do // if [Z2#Analog]<55 pulse,13,0,10000 endif timerSet,3,30 taskrun,2 if [Z2#Analog]<55 pulse,13,0,5000 endif timerSet,4,30 taskrun,2 if [Z2#Analog]<55 pulse,13,0,5000 endif endon On Clock#Time=All,07:00 do // if [Z3#Analog]<45 pulse,12,0,10000 endif timerSet,5,30 taskrun,3 if [Z3#Analog]<45 pulse,12,0,5000 endif timerSet,6,30 taskrun,3 if [Z3#Analog]<45 pulse,12,0,5000 endif endon Pewnie kilka rzeczy zrobiłem w sposób trudniejszy niż można było to zrobić albo niezgodnie ze sztuką ale chyba to w tej całej zabawie jest najfajniejszego.
  2. AJAX umożliwia przekazywanie danych pomiędzy klientem a serwerem WWW bez konieczności przeładowania strony. Dodając do tego timer w JavaScript możemy uzyskać świeże dane na stronie generowanej przez ESP8266. Na początek stwórzmy w PHP najprostszą stronę WWW prezentującą aktualną godzinę pobieraną z serwera (nie "JavaScriptovy" czas z przeglądarki - w końcu docelowo chcemy pobierać dane z czujników podłączonych do "serwera WWW" postawionego na ESP8266): <? if ($_REQUEST["time"]) { echo date("G:i:s"); exit; } ?> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title>AJAX test</title> </head> <body> <span id="time"></span> <script> myTimer(); var myVar = setInterval(myTimer, 1000); function myTimer() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { document.getElementById("time").innerHTML = this.responseText; } }; xhttp.open("GET", "test.php?time=1", true); xhttp.send(); } </script> </body> </html> Timer co sekundę przywołuje funkcję myTimer(); var myVar = setInterval(myTimer, 1000); a ta pobiera zawartość podstrony test.php?time=1 i wstawia ją do elementu o nazwie "time" (document.getElementById("time").innerHTML = this.responseText;). Mamy tu tylko jeden przekazywany parametr. Co zrobić, by aktualizować kilka różnych wartości? Przywykłem do podstrony przekazującej parametry rozdzielone znakiem ; (odczyt1;odczyt2;odczyt3). Dzięki JavaScriptowej funkcji split możemy podzielić taki ciąg znaków na tablice, a potem przydzielić jej części do elementów SPAN o określonym ID. <? if ($_REQUEST["time"]) { echo date("d.m.y;G:i:s"); exit; } ?> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title>AJAX test</title> </head> <body> Date: <span id="date"></span><br> Time: <span id="time"></span> <script> myTimer(); var myVar = setInterval(myTimer, 1000); function myTimer() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var str = this.responseText; var values = str.split(";"); document.getElementById("date").innerHTML = values[0]; document.getElementById("time").innerHTML = values[1]; } }; xhttp.open("GET", "test.php?time=1", true); xhttp.send(); } </script> </body> </html> Działa! PHP po zainstalowania serwera np NGINX możemy uruchomić na Raspberry Pi. W połączeniu (komenda system()) z zewnętrznym skryptem Python lub programem w C korzystającym z WiringPi otrzymamy stronę z odczytami czujników hostowaną na Raspberry Pi! Spróbujmy wreszcie zaprogramować mikrokontroler ESP8266. Podłączmy najpierw czujnik BME280. /* I2C D4 - SCL D3 - SDA */ #include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <Wire.h> #include <Adafruit_Sensor.h> #include <Adafruit_BME280.h> char* ssid = "Weather"; //const char *password = ""; ESP8266WebServer server(80); Adafruit_BME280 bme; void setup() { Serial.begin(9600); Wire.begin(D3, D4); Wire.setClock(100000); if (!bme.begin(0x76)) //changed from default I2C adress 0x77 { Serial.println("Nie odnaleziono czujnika BMP085 / BMP180"); while (1) { } } IPAddress apIP(192, 168, 1, 1); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); // WiFi.softAP(ssid, password); WiFi.softAP(ssid); server.on("/", handleRoot); server.on("/sensors", handleSensors); server.begin(); } void loop() { server.handleClient(); } void handleRoot() { String content = "<html> <head><title>Weather</title></head><body>"; content += "<DIV style=\"display:table; font-size: large;\"><DIV style=\"border-style: solid;\">BME280:<BR>Temperature: <span id=\"tempBME\"></span>C<br>Humidity: <span id=\"humBME\"></span>%<br>Pressure: <span id=\"presBME\"></span>Pa<br></DIV>"; content += "<script>myTimer();var myVar = setInterval(myTimer, 1000);function myTimer() {var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var str = this.responseText; var values = str.split(\";\"); document.getElementById(\"tempBME\").innerHTML = values[0]; document.getElementById(\"humBME\").innerHTML = values[1]; document.getElementById(\"presBME\").innerHTML = values[2];} }; xhttp.open(\"GET\", \"sensors\", true); xhttp.send();}</script>"; content += "</body></html>"; server.send(200, "text/html", content); } void handleSensors() { String content = String(bme.readTemperature()) + ";" + String(bme.readHumidity()) + ";" + String((int)bme.readPressure()) + ";"; server.send(200, "text/html", content); } Rozdzielanie danych przecinkiem czy średnikiem nie jest sposobem "zbyt profesjonalnym". Przy dużej ilości zmiennych łatwo też o pomyłkę. Dlatego lepiej wtedy stosować bardziej odpowiednie formaty danych: JSON czy XML. Ze względu na "bliskość" JSON z JavaScript skupię się tylko na nim. Gotowa biblioteka ArduinoJson wygeneruje dane za nas. Więcej informacji znajdziemy w rozdziale Serialize with ArduinoJson dokumentacji technicznej. /* I2C D4 - SCL D3 - SDA */ #include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <Wire.h> #include <Adafruit_Sensor.h> #include <Adafruit_BME280.h> #include <ArduinoJson.h> //https://github.com/bblanchon/ArduinoJson char* ssid = "Weather"; //const char *password = ""; ESP8266WebServer server(80); Adafruit_BME280 bme; void setup() { Serial.begin(9600); Wire.begin(D3, D4); Wire.setClock(100000); if (!bme.begin(0x76)) //changed from default I2C adress 0x77 { Serial.println("Nie odnaleziono czujnika BMP085 / BMP180"); while (1) { } } IPAddress apIP(192, 168, 1, 1); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); // WiFi.softAP(ssid, password); WiFi.softAP(ssid); server.on("/", handleRoot); server.on("/sensors", handleSensors); server.begin(); } void loop() { server.handleClient(); } void handleRoot() { String content = "<html> <head><title>Weather</title></head><body>"; content += "<DIV style=\"display:table; font-size: large;\"><DIV style=\"border-style: solid;\">BME280:<BR>Temperature: <span id=\"tempBME\"></span>C<br>Humidity: <span id=\"humBME\"></span>%<br>Pressure: <span id=\"presBME\"></span>Pa<br></DIV>"; content += "<script>myTimer();var myVar = setInterval(myTimer, 1000);function myTimer() {var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var str = this.responseText; var values = JSON.parse(str); document.getElementById(\"tempBME\").innerHTML = values.temp; document.getElementById(\"humBME\").innerHTML = values.hum; document.getElementById(\"presBME\").innerHTML = values.press;} }; xhttp.open(\"GET\", \"sensors\", true); xhttp.send();}</script>"; content += "</body></html>"; server.send(200, "text/html", content); } void handleSensors() { String content; StaticJsonBuffer<400> jsonBuffer; JsonObject& root = jsonBuffer.createObject(); root["temp"] = bme.readTemperature(); root["hum"] = bme.readHumidity(); root["press"] = (int)bme.readPressure(); root.printTo(content); server.send(200, "text/html", content); } Strona prezentować się będzie tak samo jak poprzednio. Zyskamy za to wygodny i czytelny sposób dopisywania nowych danych root["temp"] = bme.readTemperature(); oraz ich odczytywania w kodzie źródłowym strony: document.getElementById(\"tempBME\").innerHTML = values.temp; Nie przejmujemy się już numeracją elementów długiej tablicy. Sytuację możemy też odwrócić tworząc stronę WWW, której formularz sterować będzie pracą naszego fizycznego urządzenia. Oczywiście nadal niekonieczne będzie przeładowanie strony, by wysłać dane. #include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> char* ssid = "Weather"; //const char *password = ""; int ledValue = 0; ESP8266WebServer server(80); void setup() { Serial.begin(9600); IPAddress apIP(192, 168, 1, 1); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); // WiFi.softAP(ssid, password); WiFi.softAP(ssid); server.on("/", handleRoot); server.begin(); pinMode(D1, OUTPUT); analogWriteRange(100); //http://esp8266.github.io/Arduino/versions/2.0.0/doc/reference.html analogWriteFreq(500); } void loop() { server.handleClient(); analogWrite(D1, ledValue); } void handleRoot() { if (server.hasArg("ledVal") && server.arg("ledVal").toInt() >= 0 && server.arg("ledVal").toInt() <= 100) { ledValue = server.arg("ledVal").toInt(); Serial.print("ledVal "); Serial.println(ledValue); server.send(200, "text/html", ""); return; } //https://www.w3schools.com/howto/howto_js_rangeslider.asp //https://stackoverflow.com/questions/9713058/send-post-data-using-xmlhttprequest String content = "<!DOCTYPE html>" "<html>" "<head>" "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" "<title>Luminosity setter</title>" "<style>" ".slidecontainer {" "width: 1024px;" "margin: 0 auto;" "text-align: center;" "}" ".slider {" " -webkit-appearance: none;" " width: 100%;" " height: 25px;" " background: #d3d3d3;" " outline: none;" " opacity: 0.7;" " -webkit-transition: .2s;" " transition: opacity .2s;" // " margin-left: 20px;" // " margin-right: 20px;" "}" ".slider:hover {" " opacity: 1;" "}" ".slider::-webkit-slider-thumb {" " -webkit-appearance: none;" " appearance: none;" " width: 25px;" " height: 25px;" " background: #4CAF50;" " cursor: pointer;" "}" ".slider::-moz-range-thumb {" " width: 25px;" " height: 25px;" " background: #4CAF50;" " cursor: pointer;" "}" "</style>" "</head>" "<body>" "<div class=\"slidecontainer\">" " <input type=\"range\" min=\"0\" max=\"100\" value=\"" + String(ledValue) + "\" class=\"slider\" id=\"myRange\">" " <p>Value: <span id=\"demo\"></span>%</p>" "</div>" "<script>" "document.getElementById(\"demo\").innerHTML = document.getElementById(\"myRange\").value;" "document.getElementById(\"myRange\").oninput = function() {" " document.getElementById(\"demo\").innerHTML = this.value;" "var data = new FormData();" "data.append('ledVal', this.value);" "var xhr = new XMLHttpRequest();" "xhr.open('POST', '/', true);" "xhr.onload = function () {" " console.log(this.responseText);" "};" "xhr.send(data);" "}" "</script>" "</body>" "</html>"; server.send(200, "text/html", content); } W tym przypadku przesunięcie suwaka na stronie sterującej wywoła natychmiastową zmianę jasności diody LED podłączonej odpowiednim rezystorem do pinu D1.
  3. Witam, jaki jest najlepszy sposób na przesłanie danych z czujnika (załóżmy temperatury, Dallas) do Arduino. Chciałbym bezprzewodowo wysłać dane z czujnika do Arduino żeby później przekazać je Raspberry Pi. Czy mógłby mi ktoś konkretnie wytłumaczyć jak mógłbym zrobić to z ESP8266, lub NRF24l01?
  4. Od dawna interesowały mnie pomiary warunków meteorologicznych w mojej miejscowości, pierwsza stacja meteorologiczna, którą zbudowałem około roku 2010, wykonana była na mikrokontrolerze Atmega32. Do komunikacji z światem wykorzystywała moduł LAN Wiznet 7010a. Stacja ta była oprogramowana w języku BASCOM. Projekt który chcę zaprezentować dzisiaj działa już od roku 2018 i został oprogramowany w środowisku Arduino. Stacja została podzielona na 2 moduły, pierwszy pomiarowy oparty jest na klonie Arduino Nano oraz drugi odbiorczy którego sercem jest ESP8266 NodeMCU v3, służy on również do wyświetlania aktualnych pomiarów na wyświetlaczu LED dot matrix o wymiarach 8x56 punktów. Na pracach stolarskich się nie będziemy skupiać napiszę tylko że klatka meteorologiczna została wykonana z drewna sosnowego i umieszczona na wysokości 2 m. Moduł Pomiarowy Czujniki jakie zastosowałem to dwie sztuki DS18B20 pierwszy zajmuje się pomiarem temperatury przy gruncie na wysokości 5cm, drugi pełni rolę zapasowego czujnika temperatury na wypadek uszkodzenia się głównego czujnika BME280. Do pomiaru prędkości wiatru wykorzystuję wiatromierz firmy Maplin na jeden obrót wiatromierza przypadają 2 impulsy z kontaktronu który jest w nim zamontowany, producent dostarcza również odpowiedni wzór według którego można obliczyć rpm oraz prędkość wiatru w km/h. Dane mierzone przez wiatromierz możemy podzielić na dwie wartości, pierwsza to chwilowa prędkość, druga prędkość w porywach, aby uśrednić wartości mierzone program zlicza impulsy z 5s a następnie dokonuje odpowiednich obliczeń. Zebrane dane przesyłane są do drugiego urządzenia poprzez moduły radiowe które działają na częstotliwości 433,92 MHz. W tym celu zastosowana została biblioteka RCSwitch. Każda mierzona wartość jest wysyłana jako osobna transmisja. aby rozróżnić pomiary z konkretnych czujników mierzona wartość mnożona jest przez 100 a następnie dodawana jest liczba 100 000 dla pierwszego czujnika, 200 000 dla drugiego itd. Przykład kodu który realizuje tę funkcję poniżej: // temperatura sensor BME codetosend = temp * 100 + (1 * 100000); mySwitch.send(codetosend, 24); // wilgotnosc sensor BME codetosend = hum * 100 + (2 * 100000); mySwitch.send(codetosend, 24); Moduł Wewnętrzny Obudowa, która idealnie nadawała się do implementacji wewnętrznego modułu pochodzi z tunera IPTV Motorola VIP1910-9. Przedni panel został wykonany z ciemnego półprzepuszczalnego plastiku który idealnie nadaje się do umieszczenia w nim wyświetlacza. Sercem urządzenia jest układ ESP8266. "Moduł wewnętrzny" został również wyposażony w czujnik temperatury oraz wilgotności DHT22, dodatkowo w celu prezentacji zmierzonych wartości dołączone zostało 7 szt. modułów wyświetlacza LED dot matrix z układem MAX7219. Do obsługi tej matrycy zastosowałem bibliotekę Max72xxPanel.h która współpracuje z biblioteką Adafruit_GFX.h w ten sposób nie byłem zmuszony implementować do rozwiązania własnych czcionek. Matryca ta oprócz modułowej konstrukcji umożliwia również sterowaniem jasnością podświetlania, w tym celu aby uprzyjemnić użytkowanie w porach nocnych odbiornik został wyposażony w fotorezystor dzięki któremu potrafi określić natężenie oświetlenia otoczenia i odpowiednie ustawienie podświetlenia. Na wyświetlaczu w pierwszej kolejności wyświetlam aktualną godzinę oraz temperaturę wewnątrz pomieszczenia oraz wilgotność, po około jednej minucie wyświetlane są informacje odczytane z stacji meteo czyli temperatura wilgotność i ciśnienie, postanowiłem nie wyświetlać tutaj informacji dotyczących prędkości wiatru oraz temperatury przy gruncie. Decyzję tą podjąłem na podstawie użytkowania innego podobnego rozwiązania, akurat jak chcemy odczytać godzinę to wyświetlane są inne informacje. Dodatkowo w godzinach nocnych, które zostały ustawione w sztywnych ramach czasowych między 21:00 a 7:00 informacje odczytane z stacji meteo zostały okrojone tylko do temperatury. W projekcie zostały zastosowane 2 rodzaje animacji pierwsza z nich, przesuwa tekst z prawej strony wyświetlacza na lewą, z możliwością zatrzymania w interesujących momentach. Drugi rodzaj to pionowa animacja. Mikrokontroler również poprzez protokół NTP i bibliotekę time.h pobiera aktualną godzinę i datę. Za odbiór danych z pierwszego układu odpowiedzialny jest moduł radiowy którego obsługą tak jak w poprzednim module zajmuje się biblioteka RCswitch. Poniżej fragment programu który demonstruje w jaki sposób odbierane i dekodowane są dane: rc = mySwitch.getReceivedValue(); // czujnik temperatury powietrza BME280 if (abs(rc)>=50000&& abs(rc)<150000) { rc=(rc-100000)/100; if (rc > -50 and rc < 60) { temp1 = rc; Serial.print("Czujnik BME280 - temperatura: \t"); Serial.println(rc); matrix.drawPixel(55,0,1); matrix.write(); } } // czujnik wilgotności BME280 if (abs(rc)>=150000 && abs(rc)<250000) { rc=(rc-200000)/100; if (rc > 5 and rc <= 100) { hum = rc; Serial.print("Czujnik BME280 - wilgotnowsc: \t"); Serial.println(rc); matrix.drawPixel(55,1,1); matrix.write(); } } Dzięki zastosowaniu zewnętrznej anteny oraz odbiornika opartego na superheterodynie, zasięg w otwartym terenie to około 250 m. Po odebraniu danych z pierwszego układu poprzez moduł radiowy następuje przekazanie ich do serwera z systemem Domoticz. Domoticz to bardzo lekki system automatyki domowej, który pozwala monitorować i konfigurować różne urządzenia, przełączniki, czujniki takie jak temperatura, opady deszczu, wiatr, promieniowanie ultrafioletowe (UV), zużycie energii elektrycznej, zużycie gazu, zużycie wody i wiele więcej. Wykresy dostępne są również na stronie www http://meteo.palowice.net Poniżej film z działania odbiornika, smużenie animacji które występuje na filmiku ludzie oko nie rejestruje. Gdyby kogoś interesował kod to również zamieszczam: meteo.zip
  5. 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"
  6. Witam, od kilku dni próbuję podziałać coś z moim ESP8266. Chodzi mi o to aby wgrać do niego obsługę komend AT abym mógł go połączyć z Arduino i sterować nim za pomocą Blynka. Broblem w tym że w żaden sposób nie mogę tego zrobić. Gdy chcę przetestować wpisując "AT" w monitorze portu szeregowego to nie mogę tego zrobić bo ESP jak głupie wali cały czas czymś takim jak na zdj. Dioda od ESP cały czas szybko miga. Proszę o pomoc bo już nie mam siły.
  7. 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.
  8. Witam wszystkich, otóż nie dawno zakupiłem moduł WiFi ESP8266 + NodeMCU v2 - 4MB zamówiony na botland.pl, podłączyłem, oprogramowałem 2 silniczki dc, dioda układu podczas podłączenia poprzez usb świeciła się na niebiesko, wszystko było w porządku, układ odłożyłem na kilka dni, po czym wyciągnąłem z przepaści szufladkowej :D, podłączyłem pod ten sam kabel usb, dioda układu zamrugała i zgasła oraz nie mogłem wgrać żadnego programu, ktoś wie co się stało, dlaczego układ przestał działać?, dodam że próbowałem podłączać inne kable usb, wgrać nowy firmware, nowe sterowniki i nic, po podłączeniu dioda się nie świeci a układ nie reaguje na nic, dodatkowo kiedy podłączam układ pod zewnętrzne zasilanie(3 baterie AA) dioda normalnie się świeci, szukałem po forum ale nie znalazłem podobnej sytuacji, że układ działał i nagle przestał, z góry dzięki za odpowiedzi.
  9. Witam. mam problem z modułem ESP8266 - 01, ponieważ po podłączeniu komputer ani smartfon nie może go wykryć. Moduł połączony jest do Arduino Pro Mini 5V 16Mhz poprzez konwerter poziomów logicznych. Firmware wgrany do modułu zgodnie z filmikiem Elektroprzewodnika na YT: https://www.youtube.com/watch?v=RaYZxqUTNWU Do zasilania modułu próbowałem użyć konwerter typu CP2102 jak również zasilacz(parametry w załączniku) z przetwornicą impulsową LM2596S. Podejrzewam tutaj problem z zasilaniem, ponieważ wcześniej moduł czasami wykrywał sieć, teraz jest totalna kaplica. Próbowałem do przetwornicy wlutować po kondensatorze 10uF na wejście i wyjście oraz kondensator 10nF pomiędzy vcc oraz gnd modułu ale nie dało to rezultatu. Próbwałem też innych zasilaczy jak np. ładowarka do starej Nokii. W związku z tym moje pytanie: jak powinienem zasilić ten moduł ? użyć 2 baterii alkaidowych 1,5V? zmienić przetwornicę na inną? zastosować jakiś filtr do eliminacji tętnień napięcia przetwornicy? zaprojektować taki filtr? jeśli tak to jakich elementów użyć? czy może to po prostu wadliwe ESP? Proszę o wskazówki osób które najlepiej miały już z tym modułem styczność.
  10. Dzień dobry, jestem świeżym użytkownikiem forum jak i Arduino. Zwracam się z prośbą o pomoc w rozwiązaniu problemu. Chcę wysyłać dane z Arduino Uno przy pomocy modułu ESP8266-01 (piny 2 i 3) do serwera MySQL (Xampp) przy pomocy skryptu PHP na serwerze i metody GET. Po uruchomieniu kodu (załącznik) moduł łączy się z siecią, problem jest jednak w połączeniu z serwerem. Połączenie albo nie zostaje nawiązane w ogóle, albo w pierwszym obrocie pętli (czasem kilka pierwszych obrotów) łączy się z serwerem, ale zwraca błędy. Później już połączenie z serwerem nie zostaje nawiązane. Informacje z Serial Monitora: [WiFiEsp] Connecting to 192.168.43.165 !!!Connected!!! [WiFiEsp] >>> TIMEOUT >>> [WiFiEsp] Data packet send error (2) [WiFiEsp] Failed to write to socket 3 [WiFiEsp] Disconnecting 3 [WiFiEsp] >>> TIMEOUT >>> [WiFiEsp] Connecting to 192.168.43.165 [WiFiEsp] >>> TIMEOUT >>> --> connection failed [WiFiEsp] Connecting to 192.168.43.165 [WiFiEsp] >>> TIMEOUT >>> --> connection failed i tak dalej... Po przeszukaniu podobnych tematów na forach polsko- i angielskojęzycznych niestety nie znalazłem rozwiązania. Jako, że dopiero zaczynam przygodę z Arduino, być może błąd jest bezpośrednio w kodzie, a ja nie potrafię go wychwycić. Mój kod: #include <WiFiEsp.h> #include <WiFiEspClient.h> #include <WiFiEspUdp.h> #include <SoftwareSerial.h> #include <PubSubClient.h> #include <SPI.h> char ssid[] = "OWN_NETWORK"; // network SSID char pass[] = "12345678"; // network password char server[] = "192.168.43.165"; int status = WL_IDLE_STATUS; // the Wifi radio's status WiFiEspClient espclient; SoftwareSerial soft(2,3); // RX, TX void setup() { Serial.begin(115200);// initialize serial for debugging soft.begin(115200); // initialize serial for ESP module WiFi.init(&soft); // initialize ESP module delay(5000); if (WiFi.status() == WL_NO_SHIELD) { Serial.println("WiFi shield not present"); while (true); // don't continue } while ( status != WL_CONNECTED) { Serial.print("Attempting to connect to "); Serial.println(ssid); status = WiFi.begin(ssid, pass); } } void loop() { if (espclient.connect(server, 80)) { Serial.println("!!!Connected!!!"); espclient.println("Connection: close\r\n"); espclient.println(); } else { Serial.println("--> connection failed\n"); } delay(5000); } Znalazłem natomiast informacje o bibliotece MySQL Connector, która pozwala na bezpośrednie połączenie z bazą danych i wykonanie kwerendy. Rozwiązanie wydaje się być lepsze, jednak tutaj podobnie utknąłem na etapie połączenia z bazą danych. Połączenie zupełnie nie jest nawiązywane, tzn. zwracane jest w kółko "Connecting to..." i timeout . Kod dla podglądu: IPAddress ip(192,168,43,199); IPAddress server_addr(192,168,43,165); char ssid[] = "OWN_NETWORK"; // network SSID char pass[] = "12345678"; // network password char user[] = "arduino"; // MySQL user login username char password[] = "haslo"; // MySQL user login password int status = WL_IDLE_STATUS; // the Wifi radio's status WiFiEspClient espclient; SoftwareSerial soft(2,3); // RX, TX MySQL_Connection conn((Client *)&espclient); void setup() { Serial.begin(115200); // initialize serial for debugging soft.begin(115200); // initialize serial for ESP module WiFi.init(&soft); // initialize ESP module delay(5000); if (WiFi.status() == WL_NO_SHIELD) { Serial.println("WiFi shield not present"); while (true); // don't continue } while ( status != WL_CONNECTED) { Serial.print("Attempting to connect to "); Serial.println(ssid); status = WiFi.begin(ssid, pass); } } void loop() { if (conn.connect(server_addr, 3306, user, password)) { delay(2000); Serial.println("Print po udanym polaczeniu"); } else { Serial.println("Connection failed."); } delay(10000); } Niestety zmagam się z tym problemem od kilku dni i brakuje mi już pomysłów co może być nie tak. Ponadto dodam, że urządzenia w sieci się wzajemnie pingują, wchodząc na serwer przez telefon mogę uruchomić skrypt PHP, który działa poprawnie.
  11. Opis konstrukcji Dość modny ostatnio temat, poruszający jakość powietrza, powstał w celu wykonania pomiarów wpływu kominka w domu na zapylenie. W ten oto sposób powstała stacja pogodowa z prezentacją pomiarów na LCD 2004 z I2C oraz możliwością udostępnienia danych dla Domoticz lub ThingSpeak. Sercem stacji jest układ ESP8266-12F na adapterze ESP Shild. Całość umieszczono na PCB zaprojektowanym w EAGLE. Płytka jest zaprojektowana w sposób umożliwiający szybką wymianę poszczególnych elementów. Może być ona wykorzystywana do programowania ESP z wykorzystaniem złącza PROG (po podpięciu się konwerterem USB-UART), jak również w innych projektach wykorzystujących I2C, wejście analogowe ESP, wejścia cyfrowe. Jako zasilanie wykorzystałem zasilacz 12V 1A, których mam kilkanaście. Dla potrzeb zasilania czujników potrzebujemy zasilania 5V o wydajności prądowej ok 1A. W tym celu wykorzystano przetwornicę impulsową step down - przetwornica DC-DC Mini 360 . Przetwornica, jak i inne elementy jest wymienna (na goldpinach). Takie rozwiązanie wymusiło stosowanie tego samego układu w innych projektach, gdzie miałem dostępne zasilania 24-30VDC). Projekt spodobał się znajomym, więc płytka została od razu wykonana w kilku egzemplarzach na frezarce mojego wykonania. Wygląd PCB od strony druku można zobaczyć na zdjęciach. Realizacja pomiarów: W założeniach miałem mierzyć tylko zawartość pyłów ale w szufladach zalegało jeszcze kilka innych czujników. Stąd też dodatkowe pomiary. Pyły: PM1; PM2,5; PM10 - czujnik PMS5003 Ciśnienie, temperatura, wilgotność - czujnik BME280 Wskaźnik CO2 - czujnik MQ135 Wyniki prezentowane są na LCD oraz przez WiFI korzystając z oprogramowania EasyEsp. Istnieje możliwość konfiguracji oprogramowania w celu przesyłania pomiarów do Domoticz lub ThingSpeak. Oprogramowanie W założeniach miałem napisać własny soft wykorzystując biblioteki dostępne dla Arduino IDE, ale z braku czasu poszedłem na łatwiznę i wykorzystałem EasyEsp. Soft wgrywamy za pomocą oprogramowania Esp8266Flasher - wykorzystujemy połączenie po USB - UART (złącze PROG na PCB). Konfiguracja Proces konfiguracji jest dokonywany z poziomu strony WWW oprogramowania ESPEasy i jest dość intuicyjny. Wszystkie parametry wpisujemy w zakładkach odpowiedzialnych za obsługę sieci, czujników iitp. Oczywiście proces konfiguracji opisany jest dokładnie na stronie projektu ESPEasy. Dla osób nie obeznanych w tej tematyce zamieszczam plik konfiguracyjny mojego projektu wraz z dokumentacją zdjęciową (Konfiguracja ESP). Podsumowanie Na chwilę obecną brak jest obudowy, ale układ powstał jako prototyp i każdy adresat układu ma ją wykonać we własnym zakresie. Sam zrobię to jak skończę inne projekty. Konstrukcja ma sporo wad: 1. Brak kalibracji czujników. 2. Pomiar MQ135 to tylko wskazanie przesunięte o 400 ppm (~poziom CO2 w atmosferze, nie uwzględniam wpływu temperatury i wilgotności). 3. Brak dzielnika napięcia na A0 (ESP ma pomiar 0-1V, MQ135 może dać do 5 V przy 5000 ppm), jednak zakładam, że nie będę miał stężenia ponad 1000ppm w domu. Później przetnę ścieżkę na PCB i dam dzielnik na analogu (co niestety zmniejszy dokładność pomiarów) lub zabezpieczę wejście analogowe diodą zenera. 4. Gotowe oprogramowanie z wieloma wadami, w planach zmiana na własny soft i wysyłanie informacji na Cayenne IOT. Zalety: 1. Prosta modułowa konstrukcja, uniwersalna płytka PCB stosowana przeze mnie w innych projektach. 2. Gotowy soft możliwy do wgrania i konfiguracji dla zupełnych laików. Jeśli ktoś jest zainteresowany dodatkowymi materiałami, to proszę o kontakt PW. W załączniku zamieszczam: 1. EAGLE - schemat w EAGLE 9.1.2 wraz z rysunkiem ścieżek. 2. ESP - oprogramowanie w wykorzystanej wersji. 3. ESP8266Flasher - soft do wgrania oprogramowania. Podczas testów pomiar pyłów miałem na zewnątrz przy mrozach ponad 300 szczytowo i pokrywało się to z lokalną stacją w Połańcu (odchyłka była w granicach 5%). Czujnik PM5003 pracuje u mnie w cyklu 60 sekund pomiary/ 30 minut uśpienie. Żywotność czujnika laserowego to 8000 h. Częstszych pomiarów nie potrzebuję do swoich potrzeb. Czas 60 sekund wystarcza do odpowiedniego wygrzania czujnika i ustabilizowania się pomiarów. Cała stacja pobiera zaraz po starcie ok. 3 W, a po nagrzaniu czujnika MQ135 pobór energii spada do ok. 1,8 W. ESP8266Flasher.zip EAGLE.zip ESP.zip Konfiguracja_ESPEasy.zip
  12. Witam, nabyłem płytkę ESP8266 z którą "chciałbym się zaprzyjaźnić". W internecie co prawda jest dużo różnych wiadomości na ten temat, aż trudno wybrać, te właściwe. Nie znalazłem żadnego sensownego kursu. Proszę napiszcie co polecacie na początek "zabawy" z ESP8266.
  13. Za chwilę mnie coś strzeli... Program najprostszy chyba z możliwych (bez żadnych bibliotek): #include <Arduino.h> #include <i2s_reg.h> #include <i2s.h> int g_samrate = 8000; uint32_t last_micro_call; uint32_t idle_micros = 100000; void init_audio() { i2s_begin(); i2s_set_rate(g_samrate); last_micro_call = micros(); } void put_sample(int16_t sample) { uint32_t usample; usample = sample & 0x0000ffff; usample |= usample << 16; if (micros() - last_micro_call > idle_micros) { last_micro_call = micros(); yield(); } // wersja 1 najprostsza - działa źle i2s_write_sample(usample); /* // wersja 2 - to samo while (i2s_is_full()) { delay(1); last_micro_call = micros(); }*/ /* // wersja 3 - to samo while (!i2s_write_sample_nb(usample)) { delay(1); last_micro_call = micros(); }*/ } void finalize_audio() { for (int i=0; i<1000 && !i2s_is_full(); i++) i2s_write_sample_nb(0); i2s_end(); } void setup(void) { } void loop(void) { int i; init_audio(); for (i=0;i<4000;i++) { float a = i * (3.141592/10); int16_t s = sin(a) * 4096; put_sample(s); } finalize_audio(); delay(500); } Efekt działania: co prawda pika sobie bardzo ładnie, ale mniej więcej połowa (całkowicie losowo) "piknięć" jest głośniejsza i zniekształcona - tak jakby wejściowa próbka przesunęła się o bit w lewo. Miałem podobny efekt kiedyś gdy próbowałem coś zrobić z TDA1543 ale uznałem, że to pewnie wina połączeń na paszczatej płytce stykowej albo 8kHz to dla niego za mało... Co ciekawsze - podobny program, ale używający hacku PWM działa ślicznie... Ktoś coś... jakieś pomysły? Bo mi się skończyły Po sprawdzeniu: zamiana na bibliotekę ESP8266Audio i użycie AudioOutputI2S nic nie zmienia.
  14. Mamy budynek gospodarczy oddalony od domu o kilka(naście) metrów i chcemy upewnić się, że panują tam warunki odpowiednie dla zwierząt. Jak do tej pory nasze kury nie były zainteresowane dostępem do Internetu, nie mamy więc tam zasięgu sieci WiFi. Musimy więc jakoś "podkablować radiowo" dane i zaprezentować je w naszym pokoju. Założeniami projektu, o który prosił mnie kolega było: bezprzewodowe transmitowanie danych z czujnika do domu, przy czym sam czujnik "nie ma dostępu do Internetu" wyświetlanie aktualnego odczytu na LCD oraz aktywacja alarmu w razie spadku temperatury poniżej zadanej wartości zapisywanie wyników na stronie WWW dostępnej poprzez Internet, dzięki czemu warunki można sprawdzać będąc poza domem Czujnik z nadajnikiem Nadajnik zasilany jest "kradnąc" prąd ze stale włączonego zasilacza instalacji alarmowej. Dlatego też konieczne było zastosowanie przetwornicy zmniejszającej napięcie do 5V. Składniki: Arduino Pro Mini 328 - 5V/16MHz termometr BMP280 moduł nadajnika RF433MHz przetwornica step-down LM2596 Pamiętajmy, że BMP280 toleruje napięcie 3.3V na linii danych, a nasze Arduino wykorzystuje 5V. Najlepiej więc podłączyć szynę I2C poprzez dzielnik napięcia lub konwerter poziomów logicznych. Ja wstawiłem szeregowo rezystor 4.7kΩ na obu pinach I2C, co nie jest rozwiązaniem "profesjonalnym" i zalecanym, ale (chwilowo) działa #include <RCSwitch.h> //https://github.com/sui77/rc-switch #include <Wire.h> #include <SPI.h> #include <Adafruit_BMP280.h> Adafruit_BMP280 bmp; RCSwitch mySwitch = RCSwitch(); void setup() { Serial.begin(9600); if (!bmp.begin(0x76)) //changed from default I2C adress 0x77 { Serial.println("Nie odnaleziono czujnika BMP085 / BMP180"); while (1) { } } mySwitch.enableTransmit(10); } void loop() { int temperature = (int)bmp.readTemperature(); mySwitch.send(temperature, 24); delay(10000); } Odbiornik ESP8266 odbiera dane z modułu RF433MHz i regularnie przesyła je na stronę WWW. Stanowi ją skrypt PHP, który zapisuje do bazy danych oraz prezentuje wyniki. Wystarczy, by ESP pobrało adres skryptu i metodą GET przekazało dane (http://mojadomena.pl/skryptodbiorczy.php?haslo=tajnehaslo&temperatura=21). Składniki: ESP8266 moduł odbiornika RF433MHz wyświetlacz LCD 2x16 z konwerterem I2C LCM1602 buzzer Najtańsze moduły RF433 oferują jedynie kilkadziesiąt cm zasięgu. Musimy wykonać własną antenę z kawałka drutu, by uzyskać użyteczną komunikację. #include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <ESP8266HTTPClient.h> #include <RCSwitch.h> //https://github.com/sui77/rc-switch #include <Wire.h> #include <LiquidCrystal_I2C.h> //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library #define MinimumTemperature 16 #define TempAlarmPin D1 LiquidCrystal_I2C lcd(0x27, 16, 2); HTTPClient httpc; const char* ssid = ""; const char* password = ""; const String accesspassword = "nojakieshaslo"; const String scripturl = "http://domena.saf/receivedata.php"; const char* WiFiHostname = "kurnik"; unsigned long lastTimeLCD = 0; int temperature = 0; RCSwitch mySwitch = RCSwitch(); void setup() { pinMode(TempAlarmPin, OUTPUT); digitalWrite(TempAlarmPin, LOW); Serial.begin(9600); Wire.begin(D3, D4); Wire.setClock(100000); lcd.begin(); lcd.backlight(); mySwitch.enableReceive(4); // D2 pinMode(13, OUTPUT); WiFi.hostname(WiFiHostname); WiFi.softAPdisconnect(); WiFi.disconnect(); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); } void loop() { if (mySwitch.available()) { temperature = mySwitch.getReceivedValue(); Serial.print("Received "); Serial.println(temperature); mySwitch.resetAvailable(); if (temperature <= MinimumTemperature) { digitalWrite(TempAlarmPin, HIGH); } else { digitalWrite(TempAlarmPin, LOW); } } if (lastTimeLCD == 0 || millis() - lastTimeLCD >= 60000) { lastTimeLCD = millis(); lcd.clear(); lcd.print(String(temperature) + "C"); String url = scripturl + "?password=" + accesspassword + "&temperature=" + temperature; httpc.begin(url); int httpCode = httpc.GET(); //Send the request if (httpCode > 0) { //Check the returning code String payload = httpc.getString(); //Get the request response payload Serial.println(payload); } } } Skrypt PHP: <?php DEFINE ('DB_USER', ''); DEFINE ('DB_PASSWORD',''); DEFINE ('DB_HOST','localhost'); DEFINE ('DB_NAME',''); DEFINE ('DB_TABLE_NAME','zagroda'); DEFINE ('PasswordToSave','nojakieshaslo'); $variables = array( array("temperature", "float"), ); for($i = 0; $i < count($variables); $i++) { if($_REQUEST[$variables[$i][0]] != "") { $savetodatabase = 1; break; } } try{ $pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';port=3306', DB_USER, DB_PASSWORD); //echo 'Connected to database'; }catch(PDOException $e){ echo 'Cannot connect to database<br />'; } $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); if($savetodatabase && $_REQUEST["password"] == PasswordToSave) { if(!tableExists($pdo, DB_TABLE_NAME)){ $query= "CREATE TABLE ".DB_TABLE_NAME." (ID int NOT NULL AUTO_INCREMENT, "; for($i = 0; $i < count($variables); $i++) { $query= $query.$variables[$i][0]." ".$variables[$i][1].", "; } $query= $query."update_time BIGINT, PRIMARY KEY (ID))"; $pdo->exec($query); //echo '<br><br>'.$query.'<br><br>'; } $query="INSERT IGNORE INTO ".DB_TABLE_NAME." (ID,"; if($_REQUEST["temperature"] != ""){ $query = $query."temperature,update_time"; } $query= $query.") VALUES (1,"; if($_REQUEST["temperature"] != ""){ $query = $query."'".mysql_real_escape_string($_REQUEST["temperature"])."',"; } $query= $query."'".time()."') ON DUPLICATE KEY UPDATE "; if($_REQUEST["temperature"] != ""){ $query = $query."temperature=VALUES(temperature),update_time=VALUES(update_time)"; } //echo '<br><br>'.$query.'<br><br>'; $pdo->exec($query); } else { if($_REQUEST["values"] == "1"){ $query="SELECT * FROM `".DB_TABLE_NAME."` ORDER BY `ID` DESC LIMIT 10"; $stmt = $pdo -> query($query); $row = $stmt->fetch(/* PDO::FETCH_ASSOC */); echo $row[1].';'.$row[2].';'; $stmt->closeCursor(); } else{ echo '<html><head><meta charset="UTF-8"><title>Chicken coop</title></head> <body><table border="3"> <tr><td><b>Temperature</b></td> <td><b>Time</b></td></tr> <tr><td><span id="temp"></span>°C</td> <td><span id="time"></span></td></tr> </table> <script>myTimer();var myVar = setInterval(myTimer, 3000);function myTimer() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var str = this.responseText; var values = str.split(";"); document.getElementById("temp").innerHTML = values[0]; document.getElementById("time").innerHTML = Unix_timestamp(values[1]); } }; xhttp.open("GET", "receivedata.php?values=1", true); xhttp.send();} function Unix_timestamp(t) { var dt = new Date(t*1000); var day = dt.getDate(); var month = "0" + (dt.getMonth()+1); var year = dt.getFullYear(); var hr = dt.getHours(); var m = "0" + dt.getMinutes(); var s = "0" + dt.getSeconds(); return day+ "." + month.substr(-2) + "." + year + " " + hr+ ":" + m.substr(-2) + ":" + s.substr(-2); } </script> </body></html>'; } } function tableExists($pdo, $table) { try { $result = $pdo->query("SELECT 1 FROM $table LIMIT 1"); } catch (Exception $e) { return FALSE; } return $result !== FALSE; } ?> Jeżeli zechcemy dopisać do tego zapisywanie historii (przy obecnych założeniach projektu prezentowany jest jedynie najnowszy odczyt) i wykres zmian w czasie, polecam zapoznać się z biblioteką Google Charts. Przykład jej wykorzystania razem z AJAXem. Możemy również wykonać kolejny wyświetlacz np do innego pokoju czy nawet innego budynku, który odczytywał będzie wartości ze wspomnianej strony WWW. Wykorzystałem do tego znane nam ESP oraz wyświetlacz siedmiosegmentowy sterowany chipem MAX7219. #include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266HTTPClient.h> #include <ESP8266WebServer.h> #include <EEPROM.h> #include "LedControl.h" //http://github.com/wayoda/LedControl #include <MD5Builder.h> #define WiFimodeButton 14 //D5 String scripturl = ""; String ssid = "", pass = ""; String host, url, http; const int httpPort = 80; const char* WiFiHostname = "Henhouse"; const String accesspassword = "nojakieshaslo"; unsigned long lastTimeLCD = 0, lastDataRead; unsigned int intervalDataRead = 5; WiFiClient client; ESP8266WebServer server(80); HTTPClient httpc; // EasyESP or NodeMCU Pin D8 to DIN, D7 to Clk, D6 to LOAD, no.of devices is 1 LedControl lc = LedControl(D8, D7, D6, 1); void setup() { pinMode(WiFimodeButton, INPUT_PULLUP); Serial.begin(9600); /* The MAX72XX is in power-saving mode on startup, we have to do a wakeup call */ lc.shutdown(0, false); /* Set the brightness to a medium values */ lc.setIntensity(0, 1); /* and clear the display */ lc.clearDisplay(0); EEPROM.begin(512); EEPROM.get(0, intervalDataRead); WiFi.softAPdisconnect(); WiFi.disconnect(); int i; if (ssid == "") { for (i = 100; i < 170; i++) { if (EEPROM.read(i) == NULL) { break; } ssid += char(EEPROM.read(i)); } for (i = i + 1; i < 250; i++) { if (EEPROM.read(i) == NULL) { break; } pass += char(EEPROM.read(i)); } } if (scripturl == "") { for (i = i + 1; i < 450; i++) { if (EEPROM.read(i) == NULL) { break; } scripturl += char(EEPROM.read(i)); } } if (digitalRead(WiFimodeButton) == LOW) { //access point part Serial.println("Creating Accesspoint"); IPAddress apIP(192, 168, 1, 1); WiFi.mode(WIFI_AP); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); WiFi.softAP("aquarium", "aquarium123"); Serial.print("IP address:\t"); Serial.println(WiFi.softAPIP()); } else { //station part Serial.print("connecting to..."); Serial.println(ssid); WiFi.hostname(WiFiHostname); WiFi.mode(WIFI_STA); WiFi.begin(ssid.c_str(), pass.c_str()); /* while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } */ Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } server.on("/", handleRoot); server.on("/login", handleLogin); const char * headerkeys[] = {"Cookie"} ; size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*); //ask server to track these headers server.collectHeaders(headerkeys, headerkeyssize); server.begin(); } void loop() { if (lastDataRead == 0 || (millis() - lastDataRead >= intervalDataRead * 1000)) { //host = scripturl.substring(0, scripturl.indexOf('/')); httpc.begin("http://" + scripturl + "?values=1"); //Specify request destination int httpCode = httpc.GET(); //Send the request if (httpCode > 0) { //Check the returning code String payload = httpc.getString(); //Get the request response payload Serial.println(payload); //Print the response payload String temperature1 = explode(';', payload, 0); //String temperature2 = explode(';', payload, 2); Serial.print(temperature1); //Serial.print(" "); //Serial.println(temperature2); lc.clearDisplay(0); auto printLED = [](unsigned char beginLEDsegment, String printString)->void { int i; for (i = 0; i < printString.length(); i++) { lc.setChar(0, beginLEDsegment - i, (printString[i] == '.') ? printString[i + 1] : printString[i], (printString[i + 1] == '.') ? true : false); if (printString[i] == '.') { break; } } //lc.setChar(0, beginLEDsegment - i - 1, 'C', false); lc.setRow(0, beginLEDsegment - i - 1, B01001110); //"C" }; printLED(7, String(temperature1)); //printLED(3, String(temperature2)); if (payload.length() > 4) { lastDataRead = millis(); } else { lastDataRead += 1000; } } httpc.end(); } server.handleClient(); } void handleRoot() { if (!is_authentified()) { server.sendHeader("Location", "/login"); server.sendHeader("Cache-Control", "no-cache"); server.send(301); return; } int i; if (server.hasArg("SSID") && server.arg("SSID") != "" && server.hasArg("wifipass") && server.arg("wifipass") != "") { ssid = server.arg("SSID"); pass = server.arg("wifipass"); for (i = 0; i < server.arg("SSID").length(); i++) { EEPROM.write(i + 100, server.arg("SSID")[i]); } i += 100; EEPROM.write(i, NULL); for (i = 0; i < server.arg("wifipass").length(); i++) { EEPROM.write(i + 101 + server.arg("SSID").length(), server.arg("wifipass")[i]); } //i += 3 + server.arg("wifipass").length(); EEPROM.write(server.arg("SSID").length() + server.arg("wifipass").length() + 101, NULL); EEPROM.commit(); } if (server.hasArg("scripturl") && server.arg("scripturl") != "") { scripturl = server.arg("scripturl"); for (i = 0; i < scripturl.length(); i++) { EEPROM.write(i + 102 + ssid.length() + pass.length(), scripturl[i]); } EEPROM.write(ssid.length() + pass.length() + scripturl.length() + 102, NULL); EEPROM.commit(); } if (server.arg("intervalDataRead").toInt() > 0) { intervalDataRead = server.arg("intervalDataRead").toInt(); EEPROM.put(0, intervalDataRead); EEPROM.commit(); } if (server.hasArg("SSID") && server.arg("SSID") != "" && server.hasArg("wifipass") && server.arg("wifipass") != "") { ESP.restart(); } String content = "<html><head><title>Henhouse</title></head><body>"; content += "<form action='/' method='POST'>Home WiFi Network Name (SSID): <input type='text' name='SSID' value='" + String(ssid) + "'><br>"; content += "Password: <input type='password' name='wifipass'><br>"; content += "URL to datalogging script (without http://): <input type='text' name='scripturl' id='scripturl' value='" + scripturl + "'><br>"; content += "Read values every [seconds]: <input type='text' name='intervalDataRead' value='" + String((int)intervalDataRead) + "'><br>"; content += "<script language=\"javascript\">function stringReplace() {var s = document.getElementById(\"scripturl\").value;var removeThis = /^(http?|https):\\/\\//;s = s.replace(removeThis, '');document.getElementById(\"scripturl\").value = s;}</script>"; content += "<input type='submit' value='Submit' onClick=\"stringReplace();\"></form><br>"; content += "</body></html>"; server.send(200, "text/html", content); } bool is_authentified() { Serial.println("Enter is_authentified"); if (server.hasHeader("Cookie")) { Serial.print("Found cookie: "); String cookie = server.header("Cookie"); Serial.println(cookie); String cookielogin = "WEATHERSTATION=" + md5(accesspassword); if (cookie.indexOf(cookielogin) != -1) { Serial.println("Authentification Successful"); return true; } } Serial.println("Authentification Failed"); return false; } void handleLogin() { String msg; if (server.hasHeader("Cookie")) { Serial.print("Found cookie: "); String cookie = server.header("Cookie"); Serial.println(cookie); } if (server.hasArg("DISCONNECT")) { Serial.println("Disconnection"); server.sendHeader("Location", "/login"); server.sendHeader("Cache-Control", "no-cache"); server.sendHeader("Set-Cookie", "WEATHERSTATION=0"); server.send(301); return; } if (server.hasArg("PASSWORD")) { if (server.arg("PASSWORD") == accesspassword) { server.sendHeader("Location", "/"); server.sendHeader("Cache-Control", "no-cache"); String cookielogin = "WEATHERSTATION=" + md5(accesspassword); server.sendHeader("Set-Cookie", cookielogin); server.send(301); Serial.println("Log in Successful"); return; } msg = "Wrong username/password! try again."; Serial.println("Log in Failed"); } String content = "<html><body><form action='/login' method='POST'>"; content += "Password:<input type='password' name='PASSWORD' placeholder='password'><br>"; content += "<input type='submit' name='SUBMIT' value='Submit'></form>" + msg + "<br>"; server.send(200, "text/html", content); } String md5(String str) { MD5Builder _md5; _md5.begin(); _md5.add(String(str)); _md5.calculate(); return _md5.toString(); } String explode(char character, String string, unsigned int position) { unsigned int i, ii; unsigned int counter = 0; for (i = 0; i < string.length(); ++i) { if (string[i] == character) { counter++; i++; } if (counter >= position) { break; } } for (ii = i + 1; ii < string.length(); ii++) { if (string[ii] == character) { break; } } return string.substring(i, ii); }
  15. Pomysł na ten projekt zrodził się w momencie, gdy zwykłe sterowanie oświetleniem (włącz/wyłącz) stało się nie wystarczające. Elementy Moduł ściemniacza ESP8266 12E Enkoder rotacyjny KY-040 Zasilacz Hi-Link HLK-PM01 Moduł z przetwornicą AMS1117 Moduł z BMP280 Budowa Do połączenia wszystkich elementów wykorzystałem zaprojektowane przez siebie płytki PCB, podzielone na sekcje zasilania oraz mikrokontrolera. W sekcji zasilania poza zasilaczem zastosowałem zabezpieczenia w postaci warystora (s10k250) oraz bezpiecznika rurkowego. W części z mikrokontrolerem wyprowadzone zostały wejścia: UART, GPIO0 (do aktualizacji), zasilanie modułu ściemniacza oraz sterowania tym modułem (pin detekcji zera sieci, sterowanie triakiem), wyjście na diodę kontrolną, wyjście RJ45 do fizycznego panelu sterowania, gniazdko elektryczne do podłączenia lampki. Całość została zamknięta w puszcze natynkowej. Do fizycznego sterowania użyty został enkoder przytwierdzony do drewnianej szkatułki w której umieszczono również czujnik BMP280. Drewniane pudełko połączone jest do „modułu centralnego” kablem Ethernetowym. Ogólna zasada działania modułu ściemniacza Sposób działania ściemniacza jest bardzo prosty. Prąd przemienny płynący w gniazdu elektrycznym ma postać podobną do sinusoidy. Jeżeli załączenie prądu nastąpi po upływie 30% czasu trwania każdej połówki sinusoidy to żarówka będzie świecić na 70% jasności, ponieważ tylko tyle energii zostanie jej przekazane. Za detekcję zera sieci (początek połówki sinusoidy) odpowiada optotraik umieszczony w module, w monecie przejścia przez zero wykonywane jest przerwanie w mikrokontrolerze, triak przepuszcza prąd przez określony czas. W wyliczeniach czasu muszą zastać uwzględnione między innymi częstotliwość prądu, czy wielkość każdego kroku przyciemnienia. Szczegółowo zasada działania podobnego ściemniacza została opisana w książce Mirosława Kardasia Mikrokontrolery AVR Język C Podstawy programowania. Do swojego celu wykorzystałem bibliotekę przygotowana przez twórców modułu. Funkcjonalności: Możliwość ustawienia intensywności świecenia podłączonej żarówki w przedziale 0-100%. Możliwość ustawienia wygaszenia światła po określonym czasie, ściemnianie następuje płynnie. Możliwość sprawdzenia temperatury otoczenia, a po poprawnej kalibracji również ciśnienia. Możliwość kontroli ściemniacza fizycznym pokrętłem (krok 5 punktów procentowych). Możliwość kontroli poprzez sieć lokalną. Szczegółowy stan urządzenia udostępniany jest w formacie XML. Implementacja casu pobieranego z serwerów NTP. Witryna zarządzania Strona internetowa powstała przy wykorzystaniu biblioteki jQuary oraz technologii Bootstrap w celu zachowania responsywności. Do sterowania poziomem światła służy suwak poczatkowo automatycznie ustawiany w pozycji aktualnej wartości, dodatkowo nad nim widoczny jest pasek postępu obrazujący aktualny stan jasności, zastosowany został w celu poprawienia widoczności w przypadku sterowania przy wykorzystaniu ekranów dodatkowych. Do precyzyjnego ustawiania wartości zamieszczone zostały przyciski „+” oraz „-”. W zakładce „ustawienia” użytkownik ma możliwość sprawdzenia szczegółowych danych o statusie urządzenia, zmiany częstotliwości pomiaru temperatury oraz wymuszenia aktualizacji czasu.
  16. Woltomierz, amperomierz, watomierz, termometr, higrometr, barometr, czujnik zewnętrznej termopary, luksomierz w jednym urządzeniu. Projekt typu "weź wszystko co masz pod ręką, podłącz i zaprogramuj, będzie fajnie!" Dane logować można z terminala portu szeregowego (w tym do pliku - Windows/Linux). Wyniki pomiarów prezentowane są również na stronie WWW, a odczyty odświeżają się na żywo dzięki zastosowaniu technologii AJAX. Część do pomiaru prądu przypomina mój poprzedni projekt multimetru, przy czym wykorzystałem dokładniejszy moduł oparty na INA226 zamiast INA219. Podobnie jak tam zasilacz dla mierzonego obwodu podłączamy z boku do gniazda DC, a odbiornik "karmi się" z gniazd bananowych 4 mm. /* I2C D4 - SCL D3 - SDA */ #include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <Wire.h> #include <Adafruit_Sensor.h> #include <Adafruit_BME280.h> //#include <Adafruit_INA219.h> #include <INA226.h> //https://github.com/jarzebski/Arduino-INA226 #include <BH1750.h> //https://github.com/claws/BH1750 #include "max6675.h" //https://github.com/adafruit/MAX6675-library #include <LiquidCrystal_I2C.h> char* ssid = "Arduino Multimeter"; //const char *password = ""; //http://github.com/adafruit/MAX6675-library/issues/9#issuecomment-168213845 const int thermoDO = D6; const int thermoCS = D7; const int thermoCLK = D8; ESP8266WebServer server(80); Adafruit_BME280 bme; //Adafruit_INA219 ina219; INA226 ina226; BH1750 lightMeter; MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO); LiquidCrystal_I2C lcd(0x27, 20, 4); unsigned long lastTimeLCD = 0; void setup() { Serial.begin(9600); Wire.begin(D3, D4); Wire.setClock(100000); if (!bme.begin(0x76)) //changed from default I2C adress 0x77 { Serial.println("Nie odnaleziono czujnika BMP085 / BMP180"); while (1) { } } uint32_t currentFrequency; //ina219.begin(); // Default INA226 address is 0x40 ina226.begin(); // Configure INA226 ina226.configure(INA226_AVERAGES_1, INA226_BUS_CONV_TIME_1100US, INA226_SHUNT_CONV_TIME_1100US, INA226_MODE_SHUNT_BUS_CONT); // Calibrate INA226. Rshunt = 0.01 ohm, Max excepted current = 4A ina226.calibrate(0.01 * 1.318, 3); lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE); IPAddress apIP(192, 168, 1, 1); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); // WiFi.softAP(ssid, password); WiFi.softAP(ssid); server.on("/", handleRoot); server.on("/sensors", handleSensors); server.begin(); lcd.begin(); // lcd.backlight(); } void loop() { server.handleClient(); if (lastTimeLCD == 0 || millis() - lastTimeLCD >= 500) { lastTimeLCD = millis(); lcd.clear(); /* lcd.print(ina219.getBusVoltage_V()); lcd.print("V "); lcd.print(ina219.getCurrent_mA()); lcd.print("mA "); lcd.print(ina219.getPower_mW()); lcd.print("mW"); lcd.setCursor(0, 1); */ float volt = ina226.readBusVoltage(); float amp = 1000.0 * ina226.readShuntCurrent(); float power = 1000.0 * ina226.readBusPower(); int lux = lightMeter.readLightLevel(); float tempThero = thermocouple.readCelsius(); float temp = bme.readTemperature(); float hum = bme.readHumidity(); float pres = bme.readPressure(); lcd.print(volt); lcd.print("V "); lcd.print(amp, 1); lcd.print("mA "); lcd.setCursor(0, 1); lcd.print(power, 0); lcd.print("mW "); lcd.print(lux); lcd.print(" lx "); lcd.print(tempThero); lcd.print("C"); lcd.setCursor(0, 2); lcd.print(temp, 1); lcd.print("C "); lcd.print(hum, 1); lcd.print("% "); lcd.print(pres, 0); lcd.print("Pa"); Serial.println(String(volt) + "V " + String(amp, 1) + "mA " + String(power, 0) + "mW " + String(lux) + " lx " + String(tempThero) + "C " + String(temp, 1) + "C " + String(hum, 1) + "% " + String(pres, 0) + "Pa"); } } void handleRoot() { String content = "<html> <head><title>Ardunio Multimeter</title></head><body>"; content += "<DIV style=\"display:table; font-size: large;\"><DIV style=\"border-style: solid;\">BME280:<BR>Temperature: <span id=\"tempBME\"></span>C / <span id=\"tempBMEF\"></span>F<br>Humidity: <span id=\"humBME\"></span>%<br>Pressure: <span id=\"presBME\"></span>Pa<br></DIV><DIV style=\"border-style: solid;\">INA219:<BR>Voltage: <span id=\"voltage\"></span>V<br>Current: <span id=\"current\"></span>mA<br>Power: <span id=\"power\"></span>mW<br></DIV> <DIV style=\"border-style: solid;\">BH1750:<BR>Illuminance: <span id=\"illuminance\"></span>lx</DIV> <DIV style=\"border-style: solid;\">MAX6675:<BR>Temperature: <span id=\"thermocouple\"></span>C / <span id=\"thermocoupleF\"></span>F</DIV></DIV>"; content += "<script>myTimer();var myVar = setInterval(myTimer, 1000);function myTimer() {var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var str = this.responseText; var values = str.split(\";\"); document.getElementById(\"tempBME\").innerHTML = values[0]; document.getElementById(\"tempBMEF\").innerHTML = (values[0]*9/5 + 32).toFixed(2); document.getElementById(\"humBME\").innerHTML = values[1]; document.getElementById(\"presBME\").innerHTML = values[2]; document.getElementById(\"voltage\").innerHTML = values[3]; document.getElementById(\"current\").innerHTML = values[4]; document.getElementById(\"power\").innerHTML = values[5]; document.getElementById(\"illuminance\").innerHTML = values[6]; document.getElementById(\"thermocouple\").innerHTML = values[7]; document.getElementById(\"thermocoupleF\").innerHTML = (values[7]*9/5 + 32).toFixed(2);} }; xhttp.open(\"GET\", \"sensors\", true); xhttp.send();}</script>"; content += "</body></html>"; server.send(200, "text/html", content); } void handleSensors() { //String content = String(bme.readTemperature()) + ";" + String(bme.readHumidity()) + ";" + String(bme.readPressure()) + ";" + String(ina219.getBusVoltage_V()) + ";" + String(ina219.getCurrent_mA()) + ";" + String(ina219.getPower_mW()) + ";" + String(lightMeter.readLightLevel()) + ";" + String(thermocouple.readCelsius()) + ";"; String content = String(bme.readTemperature()) + ";" + String(bme.readHumidity()) + ";" + String(bme.readPressure()) + ";" + String(ina226.readBusVoltage()) + ";" + String(1000.0 * ina226.readShuntCurrent()) + ";" + String(1000.0 * ina226.readBusPower()) + ";" + String(lightMeter.readLightLevel()) + ";" + String(thermocouple.readCelsius()) + ";"; server.send(200, "text/plain", content); } Z zestawem łączy się prymitywna aplikacja napisana w C++. Potrafi zapisywać pomiary do pliku tekstowego i baz danych MySQL oraz SQLite. Jej pracę kończymy w bardzo "brutalny" sposób: Ctrl + C (podobnie, jak w loggerze dla poprzedniego multimetru). Przy pisaniu skorzystałem z poradnika (uwaga, strona zawiera dziwne, procesorożerne skrypty, które mogą nawet zawiesić przeglądarkę, radzę zatrzymać jej ładowanie [przycisk "X"] tuż po otwarciu) Server and client example with C sockets on Linux. Kompilacja: g++ webclient2mysql.cpp -L/usr/lib/mysql -lmysqlclient -l sqlite3 -o webclient2mysql -std=c++17 Jako parametry wywoływanego programu podajemy adres strony www urządzenia z wartościami pomiarów oraz interwał czasu (w sekundach), z jakim mają być zapisywane pomiary, np: ./webclient2mysql http://192.168.1.1/sensors 5 //g++ webclient2mysql.cpp -L/usr/lib/mysql -lmysqlclient -l sqlite3 -o webclient2mysql -std=c++17 //www.binarytides.com/server-client-example-c-sockets-linux/ /* #define DB_USER "" #define DB_PASSWORD "" #define DB_HOST "" #define DB_PORT 3307 #define DB_NAME "" */ #define DB_TABLE_NAME "logeddata" #define DB_SQLite_FILE "database.db" #define LOG_FILE "log.txt" #define CSV_DELIMITER ';' #define CSV_DELIMITER_COUNT 8 //Quantity of delimiters in one CSV row. #include <stdio.h> //printf #include <string.h> //strlen #include <sys/socket.h> //socket #include <arpa/inet.h> //inet_addr #include <unistd.h> #include <netdb.h> #include <iostream> #include <string> #include <vector> #include <chrono> #include <ctime> #include <thread> // std::this_thread::sleep_for #include <regex> #ifdef DB_HOST #include <mysql/mysql.h> //libmysqlclient-dev #endif #ifdef DB_SQLite_FILE #include <sqlite3.h> //sudo apt-get install sqlite3 libsqlite3-dev #endif #ifdef LOG_FILE #include <fstream> #endif const std::vector<std::string> explode(const std::string& s, const char& c); const std::string variables[][2] = { {"temperature", "float"}, {"humidity", "float"}, {"pressure", "float"}, {"voltage", "float"}, {"current", "float"}, {"power", "float"}, {"luminosity", "float"}, {"temperaturethermocouple", "float"}}; int main(int argc, char *argv[]) { if (argc != 3) { puts("Usage: ./webclient device_webpage_URL time_interval"); return 0; } const std::string s = argv[1]; std::smatch match; std::regex rgx("\/\/(.+?)\/"); std::string hostname, port; if (std::regex_search(s.begin(), s.end(), match, rgx)) { hostname = std::string(match[1]); if (hostname.find(":") != std::string::npos) { port = hostname.substr(hostname.find(":") + 1); hostname = hostname.substr(0, hostname.find(":")); } else { port = "80"; } } rgx = "\/\/.+?(\/.+)"; std::string path; if (std::regex_search(s.begin(), s.end(), match, rgx)) path = std::string(match[1]); std::cout << hostname << " " << port << " " << path << "\n"; int sock; struct sockaddr_in server; char server_reply[2000]; #ifdef DB_HOST MYSQL mysql; mysql_init(&mysql); if(mysql_real_connect(&mysql, DB_HOST, DB_USER, DB_PASSWORD, DB_NAME, DB_PORT, NULL, 0)) printf("Connected to database!\n"); else printf("Can't connect to database: %d, %s\n", mysql_errno(&mysql), mysql_error(&mysql)); #endif #ifdef DB_SQLite_FILE char * error = 0; int rc; sqlite3 * db; sqlite3_stmt * steatment; sqlite3_open(DB_SQLite_FILE, & db); #endif #ifdef LOG_FILE std::ofstream myfile; myfile.open(LOG_FILE, std::ios::out | std::ios::app); #endif std::string query; #ifdef DB_HOST query = "CREATE TABLE IF NOT EXISTS " + std::string(DB_TABLE_NAME) +" (ID int NOT NULL AUTO_INCREMENT,"; for(int i = 0; i < sizeof(variables) / sizeof(variables[0]); i++) { query += variables[i][0] + " " + variables[i][1] + ","; } query += "date_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (ID))"; std::cout << query << std::endl; mysql_query(&mysql, query.c_str()); #endif #ifdef DB_SQLite_FILE query = "CREATE TABLE IF NOT EXISTS " + std::string(DB_TABLE_NAME) +"(ID integer primary key,"; for(int i = 0; i < sizeof(variables) / sizeof(variables[0]); i++) { query += variables[i][0] + " " + variables[i][1] + ","; } query += "date_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP)"; sqlite3_exec(db, query.c_str(), 0, 0, & error); // std::cout << error; #endif query = ""; while (1) { //Create socket sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { printf("Could not create socket"); } puts("Socket created"); server.sin_addr.s_addr = inet_addr(hostname.c_str()); server.sin_family = AF_INET; server.sin_port = htons(stoi(port)); //Connect to remote server if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) { perror("connect failed. Error"); return 1; } puts("Connected\n"); std::string message = "GET " + path + " HTTP/1.1\r\nHost: " + hostname + "\r\n\r\n"; puts(message.c_str()); //Send some data if (send(sock, message.c_str(), strlen(message.c_str()), 0) < 0) { puts("Send failed"); return 1; } std::string httpContent = ""; char cur; while ( read(sock, &cur, 1) > 0 ) { //Receive a reply from the server httpContent += cur; } httpContent = httpContent.substr(httpContent.find("\r\n\r\n") + 4); std::time_t currentTime = std::time(nullptr); std::vector<std::string> results; results = explode(httpContent, CSV_DELIMITER); std::cout << results.size() << "\n"; if(results.size() == CSV_DELIMITER_COUNT){ std::cout << currentTime << " " << httpContent << "\n"; #ifdef LOG_FILE myfile << currentTime << " "; for (int i = 0; i < sizeof(variables) / sizeof(variables[0]); i++){ myfile << " " << results[i]; } myfile << " " << std::endl; #endif query = "INSERT INTO " + std::string(DB_TABLE_NAME) + " ("; for (int i = 0; i < sizeof(variables) / sizeof(variables[0]); i++){ query += variables[i][0]; if(i < sizeof(variables) / sizeof(variables[0]) - 1) {query += ",";} } query += ") VALUES ("; for (int i = 0; i < sizeof(variables) / sizeof(variables[0]); i++){ query += "'" + results[i] + "'"; if(i < sizeof(variables) / sizeof(variables[0]) - 1) {query += ",";} } query += ")"; // query = "INSERT INTO " + std::string(DB_TABLE_NAME) + " (temperature, humidity, pressure" + ") VALUES ("+ "'" + std::string(results[0]) +"'," + "'" + std::string(results[1]) +"',"+ "'" + std::string(results[2]) +"'" +")"; #ifdef DB_HOST mysql_query(&mysql, query.c_str()); #endif #ifdef DB_SQLite_FILE sqlite3_exec(db, query.c_str(), 0, 0, & error); //std::cout << error; #endif } close(sock); std::this_thread::sleep_for(std::chrono::seconds(atoi(argv[2]))); } #ifdef LOG_FILE myfile.close(); #endif #ifdef DB_HOST mysql_close(&mysql); #endif #ifdef DB_SQLite_FILE sqlite3_close( db ); #endif return 0; } const std::vector<std::string> explode(const std::string& s, const char& c) //http://www.cplusplus.com/articles/2wA0RXSz/ { std::string buff{""}; std::vector<std::string> v; for(auto n:s) { if(n != c) buff+=n; else if(n == c && buff != "") { v.push_back(buff); buff = ""; } } if(buff != "") v.push_back(buff); return v; } Podgląd bazy SQLite z wynikami pomiarów (SQLite Browser) Składniki: ESP8266 wyświetlacz LCD 4x20 z konwerterem I2C LCM1602 moduł miernika napięcia i natężenia prądu z magistralą I²C INA226 BME280 czujnik termopary MAX6675 czujnik natężenia światła BH1750 bezpiecznik (zastosowałem PPTC 3A) gniazdo oraz wtyki bananowe 4 mm gniazdo DC 5.5/2.1 mm
  17. Kolega Robert (SP9-10126-KR) poprosił mnie o wykonanie sterownika do akwarium. Założeniem była "symulacja dnia" (płynne rozjaśnianie i ściemnianie oświetlenia LED o świcie i zmierzchu) oraz monitorowanie temperatury wody z alarmem (w razie awarii fabrycznej grzałki z termostatem do akwariów). Pracę urządzenia można doglądać za pomocą strony WWW. Parametry odświeżają się na żywo dzięki zastosowaniu technologii AJAX. Jeżeli temperatura nie znajdzie się w ustawionym przedziale zostanie włączony alarm. Czas synchronizowany jest automatycznie korzystając z serwera NTP. Ciekowostką jest możliwość konfiguracji nazywy SSID sieci WiFi i hasła do niej przez samego użytkownika poprzez stronę sterująca. Standardowo urządzenie łączy się z siecią WiFi. W przypadku, gdy zapamiętana konfiguracja jest niezgodna z parametrami sieci domowej, wystarczy nacisnąć i przytrzymać przycisk przed podłączeniem do prądu. ESP8266 uruchomi się w trybie Access Pointa, z którym będziemy mogli się połączyć i na stronie http://192.168.1.1 dokonamy poprawek. Po podłączeniu do sieci domowej WiFi możemy też nacisnąć wspomniany przycisk w trakcie pracy urządzenia, co pozwoli na wyświetlenie uzyskanego z DHCP IP. Ułatwi to wejście na stronę konfiguracyjną. Przy uruchomieniu następuje odczyt ustawień sieci #include <EEPROM.h> String ssid = "", pass = ""; [...] setup(){ if (ssid == "") { int i; for (i = 100; i < 170; i++) { if (EEPROM.read(i) == NULL) { break; } ssid += char(EEPROM.read(i)); } for (int ii = i + 1; ii < 250; ii++) { if (EEPROM.read(ii) == NULL) { break; } pass += char(EEPROM.read(ii)); } } WiFi.mode(WIFI_STA); WiFi.begin(ssid.c_str(), pass.c_str()); [...] server.on("/", handleRoot); server.on("/sensors", handleSensors); server.begin(); } Za odbieranie danych z formularza WWW i zapisywanie ich w pamięci odpowiada void handleRoot() { [...] if (server.hasArg("SSID") && server.arg("SSID") != "" && server.hasArg("wifipass") && server.arg("wifipass") != "") { for (i = 0; i < server.arg("SSID").length(); i++) { EEPROM.write(i + 100, server.arg("SSID")[i]); } i += 100; EEPROM.write(i, NULL); for (i = 0; i < server.arg("wifipass").length(); i++) { EEPROM.write(i + 101 + server.arg("SSID").length(), server.arg("wifipass")[i]); } EEPROM.write(server.arg("SSID").length() + server.arg("wifipass").length() + 101, NULL); EEPROM.commit(); ESP.restart(); } } Cały kod wygląda następująco: #include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <EEPROM.h> #include <WiFiUdp.h> #include <Time.h> // http://www.pjrc.com/teensy/td_libs_Time.html #include <ESP8266mDNS.h> #include <OneWire.h> //https://github.com/adafruit/ESP8266-Arduino/tree/esp8266/libraries/OneWire #include <DallasTemperature.h> //https://github.com/milesburton/Arduino-Temperature-Control-Library #include <Wire.h> #include <LiquidCrystal_I2C.h> //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library #define PWMpin 15 //(D8) #define WiFimodeButton 12 //D6 #define PumpPin 13 //(D7) #define TempAlarmPin 14 //(D5) #define ONE_WIRE_BUS 4 //D2 /*char ssid[] = ""; // your network SSID (name) char pass[] = ""; // your network password */ String ssid = "", pass = ""; const char* WiFiHostname = "aquarium"; unsigned char screenCount = 0, tmBegin, tmEnd, dusk, pumpBegin, pumpEnd, tempMin, tempMax; bool lastTimeLCD, tempAlarm, lastButtonStatus = HIGH; unsigned int localPort = 2390; // local port to listen for UDP packets int PWM = 0; float temp = 0; /* Don't hardwire the IP address or we won't get the benefits of the pool. Lookup the IP address for the host name instead */ //IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server IPAddress timeServerIP; // time.nist.gov NTP server address const char* ntpServerName = "time.nist.gov"; const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets // A UDP instance to let us send and receive packets over UDP WiFiUDP udp; ESP8266WebServer server(80); OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); LiquidCrystal_I2C lcd(0x27, 16, 2); tmElements_t tm; time_t getNtpTime(); unsigned long last_time_sync = 0, last_NTP_check = 0; void setup() { pinMode(PWMpin, OUTPUT); pinMode(WiFimodeButton, INPUT_PULLUP); pinMode(TempAlarmPin, OUTPUT); Serial.begin(9600); EEPROM.begin(512); Wire.begin(0, 2); //D2, D4 Wire.setClock(100000); sensors.begin(); lcd.begin(); lcd.backlight(); tmBegin = EEPROM.read(0); tmEnd = EEPROM.read(1); dusk = EEPROM.read(2); pumpBegin = EEPROM.read(3); pumpEnd = EEPROM.read(4); tempAlarm = EEPROM.read(5); tempMin = EEPROM.read(6); tempMax = EEPROM.read(7); // We start by connecting to a WiFi network // WiFi.mode(WIFI_AP_STA); WiFi.softAPdisconnect(); WiFi.disconnect(); if (ssid == "") { int i; for (i = 100; i < 170; i++) { if (EEPROM.read(i) == NULL) { break; } ssid += char(EEPROM.read(i)); } for (int ii = i + 1; ii < 250; ii++) { if (EEPROM.read(ii) == NULL) { break; } pass += char(EEPROM.read(ii)); } } if (digitalRead(WiFimodeButton) == LOW) { //access point part Serial.println("Creating Accesspoint"); IPAddress apIP(192, 168, 1, 1); WiFi.mode(WIFI_AP); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); WiFi.softAP("aquarium", "aquarium123"); Serial.print("IP address:\t"); Serial.println(WiFi.softAPIP()); } else { //station part Serial.print("connecting to..."); Serial.println(ssid); WiFi.hostname(WiFiHostname); WiFi.mode(WIFI_STA); WiFi.begin(ssid.c_str(), pass.c_str()); /* while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } */ Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } /* MDNS.begin("aquarium"); MDNS.addService("http", "tcp", 80);*/ Serial.println("Starting UDP"); udp.begin(localPort); Serial.print("Local port: "); Serial.println(udp.localPort()); server.on("/", handleRoot); server.on("/sensors", handleSensors); server.begin(); } void loop() { Serial.println(ssid); Serial.println(pass); Serial.println(WiFi.status()); if (millis() - last_time_sync > 10 * 24 * 60 * 60 * 1000 || last_time_sync == 0) { unsigned long unixt = unix_time(); if (unixt > 1512876158) { setTime(unixt); last_time_sync = millis(); } } Serial.print("Time library "); Serial.print(current_time(0)); breakTime(now(), tm); if (now() < 1512876158) { PWM = 0; } else if (tmBegin < tmEnd && tm.Hour >= tmBegin && tm.Hour < tmEnd || tmBegin > tmEnd && (tm.Hour >= tmBegin || tm.Hour < tmEnd)) { if (tm.Hour * 60 + tm.Minute < tmBegin * 60 + dusk || tm.Hour * 60 + tm.Minute < tmEnd * 60 - dusk) { PWM = 1023 - map(tmBegin * 60 * 60 + dusk * 60 - (tm.Hour * 60 * 60 + tm.Minute * 60 + tm.Second), 0, dusk * 60, 0, 1023); } if (tm.Hour * 60 + tm.Minute > tmEnd * 60 - dusk) { if (tmBegin < tmEnd) { PWM = 1023 - map(tm.Hour * 60 * 60 + tm.Minute * 60 + tm.Second - (tmEnd * 60 * 60 - dusk * 60), 0, dusk * 60, 0, 1023); } else { PWM = map(tmEnd * 60 * 60 - (tm.Hour * 60 * 60 + tm.Minute * 60 + tm.Second), 0, dusk * 60, 0, 1023); } } if (tm.Hour * 60 + tm.Minute >= tmBegin * 60 + dusk && tm.Hour * 60 + tm.Minute <= tmEnd * 60 - dusk || tmBegin > tmEnd && (tm.Hour * 60 + tm.Minute > tmBegin * 60 + dusk || tm.Hour * 60 + tm.Minute < tmEnd * 60 - dusk)) { PWM = 1023; } } /*else if (tmBegin < tmEnd && tm.Hour > tmBegin && tm.Hour < tmEnd || tmBegin > tmEnd && (tm.Hour > tmBegin || tm.Hour < tmEnd)) { PWM = 1023; } else if (tm.Hour == tmBegin) { PWM = map(tm.Minute, 0, 59, 0, 1023); } else if (tm.Hour == tmEnd) { PWM = map(59 - tm.Minute, 0, 59, 0, 1023); }*/ else { PWM = 0; } analogWrite(PWMpin, PWM); Serial.print(" "); Serial.println(PWM); server.handleClient(); if (now() % 2 != lastTimeLCD) { lastTimeLCD = now() % 2; sensors.requestTemperatures(); if (digitalRead(WiFimodeButton) == LOW && lastButtonStatus == HIGH && WiFi.status() == WL_CONNECTED) { screenCount++; } lastButtonStatus = digitalRead(WiFimodeButton); temp = sensors.getTempCByIndex(0); if (tempAlarm && (temp < (float)tempMin || temp > (float)tempMax)) { digitalWrite(TempAlarmPin, HIGH); } else { digitalWrite(TempAlarmPin, LOW); } if (tm.Hour >= pumpBegin && tm.Hour < pumpEnd) { digitalWrite(PumpPin, HIGH); } else { digitalWrite(PumpPin, LOW); } lcd.clear(); switch (screenCount % 2) { case 0: lcd.print(current_time(1)); lcd.setCursor(0, 1); lcd.print(temp, 1); lcd.print("C Day"); lcd.print(tmBegin); lcd.print("-"); lcd.print(tmEnd); break; case 1: lcd.print(WiFi.localIP().toString()); break; } } } void handleRoot() { int i; if (server.hasArg("begin") && server.arg("begin").toInt() >= 0 && server.arg("begin").toInt() <= 24 && server.hasArg("end") && server.arg("end").toInt() >= 0 && server.arg("end").toInt() <= 24 && server.hasArg("dusk") && server.arg("dusk").toInt() >= 0 && server.arg("dusk").toInt() <= 255) { tmBegin = server.arg("begin").toInt(); tmEnd = server.arg("end").toInt(); dusk = server.arg("dusk").toInt(); EEPROM.write(0, tmBegin); EEPROM.write(1, tmEnd); EEPROM.write(2, dusk); if (server.hasArg("pumpBegin") && server.arg("pumpBegin").toInt() >= 0 && server.arg("pumpBegin").toInt() <= 24 && server.hasArg("pumpEnd") && server.arg("pumpEnd").toInt() >= 0 && server.arg("pumpEnd").toInt() <= 24) { pumpBegin = server.arg("pumpBegin").toInt(); pumpEnd = server.arg("pumpEnd").toInt(); EEPROM.write(3, pumpBegin); EEPROM.write(4, pumpEnd); } // if (server.arg("tempMin").toInt() >= 0 && server.arg("tempMin").toInt() <= 100 && server.hasArg("tempMax") && server.arg("tempMax").toInt() >= 0 && server.arg("tempMax").toInt() <= 100 && server.arg("tempMax").toInt() > server.arg("tempMin").toInt()) if (server.arg("tempMin").toInt() >= 0 && server.arg("tempMin").toInt() <= 100 && server.arg("tempMax").toInt() >= 0 && server.arg("tempMax").toInt() <= 100 && server.arg("tempMax").toInt() > server.arg("tempMin").toInt()) { tempAlarm = server.arg("tempAlarm").toInt(); tempMin = server.arg("tempMin").toInt(); tempMax = server.arg("tempMax").toInt(); EEPROM.write(5, tempAlarm); EEPROM.write(6, tempMin); EEPROM.write(7, tempMax); } EEPROM.commit(); } if (server.hasArg("SSID") && server.arg("SSID") != "" && server.hasArg("wifipass") && server.arg("wifipass") != "") { for (i = 0; i < server.arg("SSID").length(); i++) { EEPROM.write(i + 100, server.arg("SSID")[i]); } i += 100; EEPROM.write(i, NULL); for (i = 0; i < server.arg("wifipass").length(); i++) { EEPROM.write(i + 101 + server.arg("SSID").length(), server.arg("wifipass")[i]); } //i += 3 + server.arg("wifipass").length(); EEPROM.write(server.arg("SSID").length() + server.arg("wifipass").length() + 101, NULL); EEPROM.commit(); ESP.restart(); } String content = "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"><title>Aquarium controller</title></head><body>"; /* for (i = 2; i < 100; i++) { if (EEPROM.read(i) == 5) { break; } content += char(EEPROM.read(i)); } content += ";"; for (int ii = i + 1; ii < 150; ii++) { if (EEPROM.read(ii) == 5) { break; } content += char(EEPROM.read(ii)); } */ content += "<DIV style=\"display:table; font-size: large; border-style: solid;\">Current time: <span id=\"time\"></span><BR>Temperature: <span id=\"temp\"></span>°C<BR>PWM: <span id=\"PWM\"></span>%<br>"; // content += "<div id=\"refresh\"> </div><script>var myVar = setInterval(myTimer, 1000); function myTimer() {var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { document.getElementById(\"refresh\").innerHTML = this.responseText;} }; xhttp.open(\"GET\", \"sensors\", true); xhttp.send();}</script>"; content += "\"Artificial day\" from " + String((int)tmBegin) + ":00 to " + String((int)tmEnd) + ":00. Dusk duration: " + String((int)dusk) + " minutes.</DIV>"; content += "<script>myTimer();var myVar = setInterval(myTimer, 1000);function myTimer() {var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var str = this.responseText; var values = str.split(\";\"); document.getElementById(\"time\").innerHTML = values[0]; document.getElementById(\"temp\").innerHTML = values[1]; document.getElementById(\"PWM\").innerHTML = values[2]; } }; xhttp.open(\"GET\", \"sensors\", true); xhttp.send();} var i = 0;</script>"; content += "<form action='/' method='POST'>Begin: <input type='number' pattern='[0-9]' maxlength='2' size='2' name='begin' value='" + String((int)tmBegin) + "'> UTC<br>"; content += "End: <input type='number' pattern='[0-9]' maxlength='2' size='2' name='end' value='" + String((int)tmEnd) + "'> UTC<br>"; content += "Dusk: <input type='number' pattern='[0-9]' maxlength='2' size='2' name='dusk' value='" + String((int)dusk) + "'> minutes<br>"; content += "Air pump operates between <input type='number' pattern='[0-9]' maxlength='2' size='2' name='pumpBegin' value='" + String((int)pumpBegin) + "'> and <input type='number' pattern='[0-9]' maxlength='2' size='2' name='pumpEnd' value='" + String((int)pumpEnd) + "'><br>"; content += "<input type=\"checkbox\" name=\"tempAlarm\" value=\"1\""; if (tempAlarm) { content += " checked"; } content += ">TempAlarm <input type='number' pattern='[0-9]' maxlength='2' size='2' name='tempMin' value='" + String((int)tempMin) + "'> - <input type='number' pattern='[0-9]' maxlength='2' size='2' name='tempMax' value='" + String((int)tempMax) + "'><br>"; content += "<a href=\"\" onclick=\"i = i+1; if(i % 2 == 0) {wifi.style.display = 'none';} else {wifi.style.display = 'block';} return false;\">Change WiFi settings</a>"; content += "<div id=\"wifi\" style=\"display:none;\">Home WiFi Network Name (SSID): <input type='text' name='SSID' value='" + String(ssid) + "'><br>"; content += "Password: <input type='password' name='wifipass'></div><br>"; content += "<input type='submit' value='Submit'></form> <br>"; content += "</body></html>"; server.send(200, "text/html", content); } void handleSensors() { String content = current_time(0) + ";" + String(temp) + ";" + String(100.0 * PWM / 1023.0); server.send(200, "text/html", content); } String current_time(bool shortstring) { breakTime(now(), tm); String timeStr; if (!shortstring) { if (tm.Year > 0) { tm.Year -= 30; } else { tm.Year = 0; } } timeStr = String(tm.Day) + "." + String(tm.Month); if (!shortstring) { timeStr += "." + String(tm.Year); } timeStr += " " + String(tm.Hour) + ":"; if (tm.Minute < 10) { timeStr += "0"; } timeStr += String(tm.Minute) + ":"; if (tm.Second < 10) { timeStr += "0"; } timeStr += String(tm.Second) + "UTC"; return timeStr; } // send an NTP request to the time server at the given address //https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/NTPClient/NTPClient.ino unsigned long sendNTPpacket(IPAddress& address) { //Serial.println("sending NTP packet..."); // set all bytes in the buffer to 0 memset(packetBuffer, 0, NTP_PACKET_SIZE); // Initialize values needed to form NTP request // (see URL above for details on the packets) packetBuffer[0] = 0b11100011; // LI, Version, Mode packetBuffer[1] = 0; // Stratum, or type of clock packetBuffer[2] = 6; // Polling Interval packetBuffer[3] = 0xEC; // Peer Clock Precision // 8 bytes of zero for Root Delay & Root Dispersion packetBuffer[12] = 49; packetBuffer[13] = 0x4E; packetBuffer[14] = 49; packetBuffer[15] = 52; // all NTP fields have been given values, now // you can send a packet requesting a timestamp: udp.beginPacket(address, 123); //NTP requests are to port 123 udp.write(packetBuffer, NTP_PACKET_SIZE); udp.endPacket(); } time_t unix_time() { if (WiFi.status() == WL_CONNECTED && ((unsigned long)(millis() - last_NTP_check) > 15000 || last_time_sync == 0)) { last_NTP_check = millis(); //get a random server from the pool WiFi.hostByName(ntpServerName, timeServerIP); sendNTPpacket(timeServerIP); // send an NTP packet to a time server // wait to see if a reply is available delay(1000); int cb = udp.parsePacket(); if (!cb) { //Serial.println("no packet yet"); } else { //Serial.print("packet received, length="); //Serial.println(cb); // We've received a packet, read the data from it udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer //the timestamp starts at byte 40 of the received packet and is four bytes, // or two words, long. First, esxtract the two words: unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); // combine the four bytes (two words) into a long integer // this is NTP time (seconds since Jan 1 1900): unsigned long secsSince1900 = highWord << 16 | lowWord; //Serial.print("Seconds since Jan 1 1900 = " ); //Serial.println(secsSince1900); // now convert NTP time into everyday time: Serial.println("Unix time sync "); // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: const unsigned long seventyYears = 2208988800UL; // subtract seventy years: unsigned long epoch = secsSince1900 - seventyYears; // print Unix time: if (epoch > 1512876158) { return epoch + 2; } else { return 0; } } } } Składniki: ESP8266 wodoodporny termometr oparty na układzie DS18B20 wyświetlacz LCD 2x16 z konwerterem I2C LCM1602 przetwornica step-down LM2596 zapewniająca zasilanie 3.3V dla elektroniki, konwertując z 12V na wejściu N-kanałowy tranzystor MOSFET do sterowania PWM oświetleniem LED buzzer
  18. Cześć, dziś pokażę Wam, w jaki sposób złożyć do kupy moduł ESP8266 (w tym wypadku płytkę WeMos D1 mini) oraz matrycę LED na sterowniku MAX7219. Oprócz tego będziemy potrzebować paru przewodów połączeniowych, lutownicy z cyną, obudowy do zegarka oraz przewód microUSB do zasilenia naszego układu. Całość projektu jest dostępna w repozytorium na moim Githubie. Obudowa Jako obudowę użyłem sklejki o grubości 6 milimetrów, którą zamówiłem na aukcji Allegro razem z docięciem. Całość obudowy z dostawą kosztowała mnie mniej niż 40 złotych. Zaprojektowałem obudowę korzystając z programu Inkscape. Następnie została ona sklejona i pokryta lakierobejcą w kolorze jasnego dębu. Na front obudowy nakleiłem mleczną plexi o grubości 3 mm, którą dostałem w lokalnym sklepie za 5 złotych. Elektronika Tutaj będzie dużo łatwiej, gdyż wystarczy połączyć tylko odpowiednie piny. Połączyłem zgodnie z adnotacją w bibliotekach do obsługi MAX7219. Należy pamiętać, że WeMos D1 operuje na napięciu 3.3V na szczęście sterownik MAX7219 akceptuje je i nie będzie problemu aby układ ruszył. Na zdjęciu połączenie za pomocą przewodów z haczykami. Kod programu Przed kompilacją programu, należy dodać do Arduino IDE obsługę płytek opartych na ESP8266 (opisał to SOYER w tym temacie). Będziemy potrzebować trzech bibliotek, które nie są standardowo dołączone do Arduino IDE: MD_MAX72XX - dzięki której nasz procesor skomunikuje się ze sterownikiem MAX7219 MD_Parola - biblioteka rozszerzająca funkcje MD_MAX72XX NTPClient - dzięki niej zyskamy możliwość pobierania czasu z Internetu bez potrzeby używania modułu RTC. Po dodaniu bibliotek, kod wygląda następująco: #include <MD_Parola.h> #include <MD_MAX72xx.h> #include <SPI.h> #include <ESP8266WiFi.h> #include <NTPClient.h> #include <ESP8266WebServer.h> #include <WiFiUdp.h> #define HARDWARE_TYPE MD_MAX72XX::FC16_HW #define MAX_DEVICES 4 #define CLK_PIN D5 // or SCK #define DATA_PIN D7 // or MOSI #define CS_PIN D8 // or SS MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES); WiFiUDP ntpUDP; const char *ssid = ""; const char *password = ""; NTPClient timeClient(ntpUDP, "0.pl.pool.ntp.org", 3600, 60000); WiFiServer server(80); uint8_t frameDelay = 25; textEffect_t scrollEffect = PA_SCROLL_LEFT; #define BUF_SIZE 512 char curMessage[BUF_SIZE]; char newMessage[BUF_SIZE]; bool time_interval = false; String data; unsigned int run_seconds = 0; String timeCheck(){ timeClient.update(); data = timeClient.getFormattedTime(); data.toCharArray(newMessage, BUF_SIZE); P.displayText(newMessage, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); return data; } String wifiCheck(){ WiFiClient client = server.available(); while(client.available()){ String req = client.readStringUntil('\r'); req = req.substring(5,req.length()-9); req.replace("%20", " "); client.flush(); String s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nReq: " + req + "</html>\n"; client.print(s); delay(1); client.stop(); switch(req[0]){ case 's': data = req.substring(2); P.displayScroll(curMessage, PA_LEFT, PA_SCROLL_LEFT, frameDelay); return data; break; case 'i': data = req.substring(2); P.setIntensity(data); break; } } } void setup() { P.begin(); P.displayClear(); P.displaySuspend(false); P.displayScroll(curMessage, PA_LEFT, PA_SCROLL_LEFT, frameDelay); P.setIntensity(0); curMessage[0] = newMessage[0] = '\0'; WiFi.begin(ssid, password); while ( WiFi.status() != WL_CONNECTED ) { delay ( 500 ); } timeClient.begin(); server.begin(); sprintf(curMessage, "%03d:%03d:%03d:%03d", WiFi.localIP()[0], WiFi.localIP()[1], WiFi.localIP()[2], WiFi.localIP()[3]); } void loop() { if ( (millis()/1000 - run_seconds) > 30 ){ run_seconds = millis()/1000; time_interval = true; } if (time_interval){ data = timeCheck(); time_interval = false; } data = wifiCheck(); data.toCharArray(newMessage, BUF_SIZE); if (P.displayAnimate()) { strcpy(curMessage, newMessage); P.displayReset(); } } W zależności od zakupionego modułu trzeba będzie wybrać wersję hardware sterownika MAX7219. Definiujemy ją w linijce 10: #define HARDWARE_TYPE MD_MAX72XX::FC16_HW Obecnie dostępne typy sprzętowe to: FC16_HW, PAROLA_HW, GENERIC_HW, ICSTATION_HW. Musimy tutaj dobrać wartość eksperymentalnie. Niewłaściwy typ sprzętowy powoduje błąd w kolejnosci wyświetlania oraz w kolejności animacji poszczególnych pikseli matrycy. Dodatkowa opcja (własny tekst) W kodzie dociekliwi zobaczą, że jest możliwość wyświetlenia własnego tekstu. Po uruchomieniu programu na wyświetlaczu przewinie się adres IP, który WeMos uzyskał z naszego routera. Po otworzeniu strony z poziomu przeglądarki (lub wywołaniu polecenia curl) na stronę: http://<IP>/s=<TEXT> na wyświetlaczu będzie on przewijany od prawej do lewej strony przez maksymalnie 30 sekund. Dodatkowa opcja (zmiana jasności wyświetlacza) Można również zmienić jasność wyświetlacza, albo na stałe (w kodzie linijka: 86) P.setIntensity(<liczba z zakresu 0-15>); lub poprzez stronę www pod adresem: http://<IP>/i=<LICZBA Z ZAKRESU 0-15> gdzie 0 - to wartość minimalna, a 15 odpowiada za maksymalny poziom świecenia. Zegarek prezentuje się następująco: obudowa.pdf
  19. Opis konstrukcji Projekt powstał z myślą udostępnienia danych ze sterownika PLC przez internet. Wymieniony sterownik posiada port komunikacyjny RS232 oraz wykorzystuje protokół komunikacyjny Modbus RTU. Podczas wykonywania prac uznałem, iż miłym dodatkiem będzie możliwość zdalnego załączania urządzeń. Całość oparłem o uniwersalną płytkę stworzoną jakiś czas temu dla ESP8266. Schemat płytki uniwersalnej ESP8266: Oczywiście można skorzystać z płytki zamieszczonej w moim projekcie o stacji pogodowej. Jest ona niejako kolejną wersją powyższej (ale brak na niej dzielnika napięcia dla A0). A poniżej wygląd PCB z programu EAGLE: Oczywiście pliki Eagle i wszystko co niezbędne do powielenia projektu w załącznikach. Nie będę wymieniał wszystkich elementów, ponieważ od tego jest schemat. Nadmienię tylko, iż na płytce znajduje się stabilizator AMS1117 3.3V, z tego względu max napięcie zasilania to 12V (najlepiej niższe ze względu na grzanie się wspomnianego stabilizatora). Esp jest wpinane na PCB na adapterze (by zapewnić modułową konstrukcję).Uzupełnieniem konstrukcji jest konwerter RS232-TTL. Konwerter RS232-TTL jest zasilony z płytki ESP8266 napięciem 3,3V, natomiast RX i TX zostały wprowadzone na IN1 i IN2 ze schematu. Całość zaprogramowałem z wykorzystaniem Arduino IDE. Dla własnych potrzeb wprowadziłem odczyt wartości temperatur, potwierdzenia pracy urządzeń oraz rozkazy załączenia dla PLC: Modbus RTU: Rejestr 40001 - Temperatura kominka Rejestr 40001 - Temperatura w zbiorniku CWU Rejestr 40001 - Temperatura w kolektorze próżniowym Rejestr 40001 - Temperatura wody podłogówka Rejestr 40001 - Napięcie baterii akumulatorów (od paneli fotowoltaicznych) Rejestr 00005 - Potwierdzenie pracy pieca gazowego Rejestr 00007 - Potwierdzenie pracy schładzania instalacji kolektorów próżniowych Rejestr 40020 - Rozkaz na załączenie pieca gazowego Rejestr 40021 - Rozkaz na załączenie instalacji schładzającej Oczywiście całość oprogramowania sterującego instalacją domową działa autonomicznie na PLC. Przytoczone tutaj zmienne to tylko przykład możliwości wykorzystania wykonanego urządzenia oraz adaptacji zamieszczonych kodów do ESP8266. Jako interface webowy wykorzystuję gotową aplikację Cayenne IoT: https://cayenne.mydevices.com/ Konstrukcja nie jest pozbawiona wad: nie jestem programistą, więc kod nie jest optymalny, działanie Cayenne My Devices na PC pozostawia wiele do życzenia (o wiele lepiej działa aplikacja na Android). W załączniku zamieszczam: Schemat w EAGLE wraz z rysunkiem ścieżek (wersja oryginalna, pierwotna oraz zmodyfikowana ze stacji meteo). Skompilowane pliki źródłowe oprogramowania (oraz wersja edytowalna). Dokładniejszy opis konstrukcji oraz konfiguracji całości. Bramka Modbus.zip ESP8266 - Robert - RTUMaster.zip esp8266_STACJA_METEO (2).zip esp8266_rs232.zip
  20. Masz gotowy wspaniały bulbulator oparty na chipie ESP8266 (np na modułach WiFi ESP8266 Wemos NodeMCU V3 32MB, ArduCam ESP8266-12E WiFi IoT, Adafruit Feather Huzzah ESP8266, Adafruit Huzzah ESP8266, SparkFun Thing - Dev Board - moduł WiFi ESP8266). Tworzy on stronę WWW, którą możesz zdalnie doglądać jego pracy. Niestety posiadasz łącze stałe ze zmiennym IP. W jaki sposób możesz więc połączyć się z nim będąc poza domem? Z pomocą przychodzą usługi typu Dynamic DNS. Jedną z nich jest polski serwis https://freedns.42.pl, z którego od lat korzystam. Zapewnia on przykładowy skrypt Pythona do uaktualniania rekordów DNS. Niestety nie skorzystamy z niego bezpośrednio w Arduino. Musimy więc napisać własny szkic. Celem podejrzenia danych wysyłanych do serwera nieco zmodyfikowałem gotowy skrypt Pythona podmieniając server = "https://freedns.42.pl/xmlrpc.php" na adres localhost swojego własnego programu napisanego w C++, który zapisał nagłówek i dane przekazywane do serwera. Tak oto powstał szkic Arduino zawierający trzy funkcje: publicIP() - sprawdzająca aktualne publiczne IP gethostbyname(String host) - sprawdzająca IP przypisane do domeny w rekordach DNS - rekord adresów (A) updateDynDNS() - odpowiedzialna za uaktualnienie wpisu w serwisie FreeDNS Możemy teraz cyklicznie wywoływać w pętli loop polecenia: String myIP = publicIP(); String hostIP = gethostbyname(dnsset.subdomain + "." + dnsset.domain); if (myIP != hostIP) { dnsset.newaddress = myIP; updateDynDNS(); Serial.println("FreeDNS IP: " + hostIP); Serial.println("Public IP: " + myIP); } Sprawdzamy więc nasze aktualne publiczne IP, potem IP przypisane do domeny w DNS. Jeśli się nie zgadzają - wtedy uruchamiamy "machinę" odpowiedzialną za komunikację z FreeDNS. Polecam użyć jakiegoś timera czy skorzystać z funkcji millis(), nie blokującego działanie innych funkcji delay() Cały kod źródłowy programu: #include <ESP8266HTTPClient.h> #include <ESP8266WiFi.h> #include <WiFiClientSecure.h> struct FreeDNSsettings { String domain; String subdomain; String newaddress; String user; String password; String ttl; } dnsset; void setup() { dnsset.domain = "TwojaDomena.pl"; dnsset.subdomain = "Nazwa subdomeny"; dnsset.user = "Uzytkownik w serwisie freedns.42.pl"; dnsset.password = "haslo uzytkownika"; dnsset.ttl = "120"; Serial.begin(9600); //Serial connection WiFi.softAPdisconnect(); WiFi.disconnect(); WiFi.mode(WIFI_STA); WiFi.begin("Siec WiFi", "i haslo do niej"); //WiFi connection while (WiFi.status() != WL_CONNECTED) { //Wait for the WiFI connection completion delay(500); Serial.println("Waiting for connection"); } } void loop() { if (WiFi.status() == WL_CONNECTED) { //Check WiFi connection status String myIP = publicIP(); String hostIP = gethostbyname(dnsset.subdomain + "." + dnsset.domain); if (myIP != hostIP) { dnsset.newaddress = myIP; updateDynDNS(); Serial.println("FreeDNS IP: " + hostIP); Serial.println("Public IP: " + myIP); } } else { Serial.println("Error in WiFi connection"); } delay(15 * 60 * 1000); } String publicIP() { String myIP; HTTPClient http; http.begin("http://ip.42.pl/raw"); //Specify request destination int httpCode = http.GET(); //Send the request if (httpCode > 0) { //Check the returning code myIP = http.getString(); //Get the request response payload } return myIP; } String gethostbyname(String host) { IPAddress ipAddress; WiFi.hostByName(host.c_str(), ipAddress); return String(ipAddress[0]) + String(".") + \ String(ipAddress[1]) + String(".") + \ String(ipAddress[2]) + String(".") + \ String(ipAddress[3]); } void updateDynDNS() { String xml = "<?xml version='1.0'?>\r\n" "<methodCall>\r\n" "<methodName>xname.updateArecord</methodName>\r\n" "<params>\r\n" "<param>\r\n" "<value><struct>\r\n" "<member>\r\n" "<name>name</name>\r\n" "<value><string>" + dnsset.subdomain + "</string></value>\r\n" "</member>\r\n" "<member>\r\n" "<name>zone</name>\r\n" "<value><string>" + dnsset.domain + "</string></value>\r\n" "</member>\r\n" "<member>\r\n" "<name>newaddress</name>\r\n" "<value><string>" + dnsset.newaddress + "</string></value>\r\n" "</member>\r\n" "<member>\r\n" "<name>oldaddress</name>\r\n" "<value><string>*</string></value>\r\n" "</member>\r\n" "<member>\r\n" "<name>updatereverse</name>\r\n" "<value><string>0</string></value>\r\n" "</member>\r\n" "<member>\r\n" "<name>user</name>\r\n" "<value><string>" + dnsset.user + "</string></value>\r\n" "</member>\r\n" "<member>\r\n" "<name>ttl</name>\r\n" "<value><string>" + dnsset.ttl + "</string></value>\r\n" "</member>\r\n" "<member>\r\n" "<name>password</name>\r\n" "<value><string>" + dnsset.password + "</string></value>\r\n" "</member>\r\n" "</struct></value>\r\n" "</param>\r\n" "</params>\r\n" "</methodCall>"; String http = "POST /xmlrpc.php HTTP/1.1\r\n" "Host: freedns.42.pl\r\n" "Accept-Encoding: gzip\r\n" "Content-Type: text/xml\r\n" "User-Agent: Python-xmlrpc/3.5\r\n" "Content-Length: " + String(xml.length()) + "\r\n" "\r\n"; xml = http + xml; WiFiClientSecure client; IPAddress ipAddress; WiFi.hostByName("freedns.42.pl", ipAddress); client.connect(ipAddress, 443); client.print(xml); } Biblioteka WiFiClientSecure występuje w środowisku ESP8266 relatywnie od niedawna. Jeśli macie problemy z kompilacją szkicu właśnie z jej powodu - uaktualnijcie obsługę ESP w Arduino IDE. Po niewielkich modyfikacjach przypuszczalnie (niestety chwilowo nie mogę tego sprawdzić) uaktualniacz DNS działać będzie również z mikrokontrolerem ESP32. Chyba wystarczy podmienić nazwy dołączanych bibliotek na: #include <HTTPClient.h> #include <WiFi.h> #include <WiFiClientSecure.h>
  21. Chęć sterowania urządzeniami w domu wzrasta z dnia na dzień. Dostępne są na rynku różne produkty ułatwiające dążenie do tego celu. Dobrym przykładem jest BlitzWolf BW-SHP6. Niestety jak się to ma w większości produktów producentów, oprogramowanie jest spersonalizowane pod producenta i dodatkowo cały ruch sieciowy przechodzi przez ich serwery. Związku z tym zaistniała potrzeba zmiany oprogramowania na OpenSources w tym przypadku Tashmota. To oprogramowanie daje możliwość sterowania w sieci LAN niezależnie od tego czy jest połączenie z Internetem. Daje też możliwość integracji z telefonem bez konkretnego systemu automatyzacji, jak i też połączenie z Domoticz lub Home Assistant. Potrzebujemy programator UART (link) lutownica (link) BlitzWolf BW-SHP2 (link) lub BlitzWolf BW-SHP6 smartfon z Android i aplikacją MacroDroid Po przygotowaniu potrzebnych nam przedmiotów Wyjmijmy gniazdko z pudełka. Jest ono małe i zgrabne co cieszy! Krok 1: Pobierz oprogramowanie Tasmota z GitHub. W moim przypadku plik sonoff.bin (język angielski). Krok 2: Pobierz oprogramowanie do wgrywania. Ja użyję tego z paczki EaspEasy. Krok 3: Rozkręć gniazdko. Odkręć jedną śrubkę znajdującą się w miejscu uziemienia i zdejmuj górną część gniazdka. Po zdjęciu obudowy można ujrzeć pady podpisane: 3.3V, 100 (podpinamy pod GND), RX, TX, GND. Krok 4: Przylutuj kabelki do padów. Pamiętaj, że RX podłącz z TX programatora! Krok 5: Podłącz do komputera i wgraj oprogramowanie. Sprawdź czy wybrano odpowiedni port COM. Krok 6: Odlutuj kabelki i skręć gniazdko. Krok 7: Po podłączeniu, gniazdko powinno emitować sieć Wi-Fi. Podłącz się do niej. Powinna się otworzyć automatycznie przeglądarka z już wprowadzonym IP gniazdka. Krok 8: Wprowadź dane do domowej sieci Wi-Fi i zaczekaj na ponowne uruchomienie. Krok 9: Domyślna konfiguracja jest ustawiona na Sonoff Basic, przejdź do ustawień (Configuration -> Configure Module). Krok 10: Z listy wybierz "BlitzWolf SHP (45)", następnie Save. Uruchomi się ponownie gniazdko. Gotowe! Gniazdko jest w pełni skonfigurowane. Dodatkowo można zmienić nazwę gniazdka (Configuration -> Confiogure Other) Integracja gniazdka z MacroDroid Pobierz aplikację MacroDroid z Google Play oraz przykładową konfigurację. Toggle_BW-SHP6_03.zip Otwórz aplikację, kliknij importuj i wybierz plik. Następnie dodaj standardowo widget MacroDroid z wyborem danego Makra. Dodatkowe ustawienia: #1 Wyłączenie diody LED (status Wi-Fi) http://ip/cm?cmnd=SetOption31%20on #2 Wyłączenie LED (po załączeniu wyjścia) http://192.168.44.33/cm?cmnd=ledstate%200
×
×
  • Utwórz nowe...