• Vítejte na XBMC-Kodi.cz
  • Česko-slovenská komunita fanoušků XBMC/Kodi
Vítejte návštevníku! Přihlášení Registrace


Hodnocení tématu:
  • 2 Hlas(ů) - 1.5 Průměr
  • 1
  • 2
  • 3
  • 4
  • 5
Domácí automatizace - bezpečně
#10
Dopsal jsem postup pro základní oživení Hass a doplňků. Pokud by něco bylo nepochopitelné dovysvětlím, popřípadě se doplní nějaké obrázky. Příště se zaměřím na oživení chytrého relé. Dlouhým procházení jsem dospěl k názoru, že je ideální řešení v podobě ESP8266. V případě Wemos je omezení dodělávka napájení a relé. Jako ideální hardware jsem našel Sonoff Basic, který se přehraje nějakým vlastním řešením z prostředí Arduino IDE. Tady je kód sonoff-arduino
Kód:
// Knihovny pro praci s WiFi
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h> // Dohledate ve spravci knihoven
#include <Ticker.h>
#include <EEPROM.h>
#include <TimeLib.h> // https://github.com/PaulStoffregen/Time
#include <WiFiUdp.h>
#include <OneWire.h> // Dohledate ve správci knihoven
#include <DallasTemperature.h> // Dohledate ve správci knihoven
#include "html.h" // HTML kod ovladaci stranky

// Cisla pinu LED, spinace a teplomeru
#define GPIO_LED 13
#define GPIO_TEPLOMER 14
#define GPIO_RELE 12

// Webovy server pojede na standardnim portu 80
ESP8266WebServer server(80);

// Objekt ticker z vestavene knihovny se postara o blikani LED
Ticker ticker;

// Nastartovani sbernice 1-Wire a teplomeru DS18B20
OneWire oneWire(GPIO_TEPLOMER);
DallasTemperature teplomer(&oneWire);

// Pomocne promenne pro synchronizaci casu
const char ntp_server[] = "us.pool.ntp.org";
const uint8_t zona = 1;
const uint8_t port = 8888;
uint8_t ntp_packet[48];
WiFiUDP Udp;

// Kontrolni promenna pro spinani podle casoveho planu
uint64_t kontrola_spinace = 0;

// Hodiny a minuty spinani podle casoveho planu
uint8_t zacatek_hodina, zacatek_minuta, konec_hodina, konec_minuta;

// Funkce tick rozsviti/zhasne LED
void tick() {
 digitalWrite(GPIO_LED, !digitalRead(GPIO_LED));
}

// Pokud se cip nemuze pripojit k Wi-Fi a spustil vlastni konfiguracni AP, rozblikej LED
void zacatekKonfigurace(WiFiManager *wmp) {
 Serial.println("Entered config mode");
 Serial.println(WiFi.softAPIP());
 //if you used auto generated SSID, print it
 Serial.println(wmp->getConfigPortalSSID());
 //entered config mode, make led toggle faster
 // kazdych 200 ms zavolej funkci tick
 ticker.attach(0.2, tick);
}

// Jakmile rezim konfigurace Wi-Fi skonci, ukonci blikani
// LED na Sonoff Basic ma opacnou logiku
// Pri LOW sviti a pri HIGH je zhasnuta
void konecKonfigurace() {
 ticker.detach();
 digitalWrite(GPIO_LED, HIGH);
}

// Halvni funkce, ktera se zpracuje po startu
void setup() {
 // Nastaveni smeru pinu GPIO na zapis
 pinMode(GPIO_LED, OUTPUT);
 pinMode(GPIO_RELE, OUTPUT);
 // Zhasni LED
 digitalWrite(GPIO_LED, HIGH);
 Serial.begin(115200);
 // Precti prvni 4 B z trvale pameti,
 // ve ktere jsou ulozene casy automatickeho spinani a vypinani
 EEPROM.begin(4);
 zacatek_hodina = EEPROM.read(0);
 zacatek_minuta = EEPROM.read(1);
 konec_hodina = EEPROM.read(2);
 konec_minuta = EEPROM.read(3);
  WiFiManager wifiManager;
 // Nastartuj teplomer DS18B20
 teplomer.begin();

 // Nastartuj WiFiManager, ktery se postara o pripojeni k Wi-Fi
 wifiManager.setAPCallback(zacatekKonfigurace);
 wifiManager.setSaveConfigCallback(konecKonfigurace);
 // IP parametry konfiguracni Wi-Fi site
 wifiManager.setAPStaticIPConfig(IPAddress(192, 168, 0, 1), IPAddress(192, 168, 0, 1), IPAddress(255, 255, 255, 0));

 // Pripoj se k Wi-Fi
 // Pokud zatim zadnou nemas v pameti, nebo je mimo dosah,
 // spust vlastni AP, ke kteremu se muze uzivatel pripojit a nastavit novou Wi-Fi
 if (!wifiManager.autoConnect("WiFiLampa")) {
   Serial.println("failed to connect and hit timeout");
   // Pokud se neco pokazi a nelze se pripojit, restartuj cip a zacni znovu
   ESP.reset();
   delay(1000);
 }
 else {
   Serial.print("Pripojen jako: ");
   Serial.println(WiFi.localIP());
 }

 // Ted uz jsem pripojeny k Wi-Fi, takze mohu pokracovat v programu
 // Pokud uzivatel zada do prohlizece IP adresu spinace,
 // posli mu HTML kod ulozeny v souboru html.h.
 // Behem HTTP komunikace zaroven sviti LED (problikne)
 // HTML kod se nacita primo z flashove pameti cipu a nezatezuje RAM
 server.on("/", []() {
   digitalWrite(GPIO_LED, LOW);
   server.send_P(200, "text/html", html);
   digitalWrite(GPIO_LED, HIGH);
 });

 // Server zaroven reaguje na nekolik HTTP dotazu ve formatu:
 // http://ipadresa/api?PARAMETR=HODNOTA
 // Pro nastaveni automatickeho spinani a vypinani tedy staci zavolat:
 // http://ipadresa/api?zacatek=HH:MM&konec=HH:MM
 server.on("/api", []() {
   digitalWrite(GPIO_LED, LOW);
   if (server.hasArg("zacatek") && server.hasArg("konec")) {
     if ((server.arg("zacatek") != NULL) && (server.arg("konec") != NULL)) {
       zacatek_hodina = server.arg("zacatek").substring(0, 2).toInt();
       zacatek_minuta = server.arg("zacatek").substring(3, 5).toInt();
       konec_hodina = server.arg("konec").substring(0, 2).toInt();
       konec_minuta = server.arg("konec").substring(3, 5).toInt();
       EEPROM.write(0, zacatek_hodina);
       EEPROM.write(1, zacatek_minuta);
       EEPROM.write(2, konec_hodina);
       EEPROM.write(3, konec_minuta);
       EEPROM.commit();
       server.send(200, "application/json", "{\"odpoved\":1}");
     }
     else {
       server.send(200, "application/json", "{\"odpoved\":0");
     }
   }
   // Pro sepnuti rele (zapnuti/vypnuti svetla):
   // http://ipadresa/api?stav=1 (nebo 0)
   else if (server.hasArg("stav")) {
     if (server.arg("stav") != NULL) {
       uint8_t stav = server.arg("stav").toInt();
       if (stav == 1) {
         Serial.println("Zapinam rele");
         digitalWrite(GPIO_RELE, HIGH);
         server.send(200, "application/json", "{\"odpoved\":1}");
       }
       else {
         Serial.println("Vypinam rele");
         digitalWrite(GPIO_RELE, LOW);
         server.send(200, "application/json", "{\"odpoved\":0}");
       }
     }
     else {
       server.send(200, "application/json", "{\"odpoved\":-1}");
     }
   }
   // Pro stazeni udaju (teplota, stav, cas na cipu, volna RAM) v JSON:
   // http://ipadresa/api?data=
   else if (server.hasArg("data")) {
     String data = "{\"odpoved\":1, \"zacatek\":\"#zacatek\", \"konec\":\"#konec\", \"stav\":#stav, \"cas\":\"#cas\", \"ram\":#ram, \"teplota\":#teplota}";
     char zacatek[6];
     char konec[6];
     char cas[9];
     teplomer.requestTemperatures();
     sprintf(zacatek, "%02d:%02d", zacatek_hodina, zacatek_minuta);
     sprintf(konec, "%02d:%02d", konec_hodina, konec_minuta);
     sprintf(cas, "%02d:%02d:%02d", hour(), minute(), second());

     // Nahrad hodnoty v AJAX sablone vyse
     // S tridou Arduino String zachazet s velkou rozvahou, pouziva dynamickou alokaci
     // Na cipech s malickou RAM ji mohou pri spatnem designu rychle zaplnit
     // Viz pamet typu heap, dynamicka alokace a riziko fragmentace RAM
     // https://www.gribblelab.org/CBootCamp/7_Memory_Stack_vs_Heap.html
     data.replace("#stav", String(digitalRead(GPIO_RELE)));
     data.replace("#zacatek", String(zacatek));
     data.replace("#konec", String(konec));
     data.replace("#cas", String(cas));
     data.replace("#ram", String((ESP.getFreeHeap() / 1000.0f), 2));
     data.replace("#teplota", String(teplomer.getTempCByIndex(0), 2));
     server.send(200, "application/json", data);
   }
   else {
     server.send(200, "application/json", "{\"odpoved\":0}");
   }
   digitalWrite(GPIO_LED, HIGH);
 });

 // Nastartovani UDP (synchronizace casu pomoci NTP serveru)
 Udp.begin(port);
 // Knihovna pro praci s casem bude kazdou hodinu
 // volat funkci, ktera bude stahovat cerstvy cas z NTP serveru
 setSyncProvider(ziskejNtpCas);
 setSyncInterval(3600);

 // Nastartovani HTTP serveru
 server.begin();

}

// Smycka loop se opakuje stale dokola
void loop() {
 // Zpracuj pozadavky HTTP klientu
 server.handleClient();

 // Jednou za minutu zkontroluj, jestli aktualni
 // cas neodpovida hodnotam pro automaticke sepnuti/vypnuti rele
 if (millis() > kontrola_spinace) {
   if ((zacatek_hodina == hour()) && (zacatek_minuta == minute())) {
     digitalWrite(GPIO_RELE, HIGH);
     Serial.println("Spinam rele podle casoveho planu");
   }
   if ((konec_hodina == hour()) && (konec_minuta == minute())) {
     digitalWrite(GPIO_RELE, LOW);
     Serial.println("Vypinam rele podle casoveho planu");
   }
   kontrola_spinace = millis() + 6e4;
 }

}

// Funcke pro ziskani aktualniho casu z NTP serveru skrze UDP protokol
time_t ziskejNtpCas()
{
 IPAddress ntp_server_ip;
 while (Udp.parsePacket() > 0);
 WiFi.hostByName(ntp_server, ntp_server_ip);
 odesliNtpPacket(ntp_server_ip);
 uint32_t start = millis();
 while (millis() - start < 1500) {
   int size = Udp.parsePacket();
   if (size >= 48) {
     Udp.read(ntp_packet, 48);
     unsigned long sekundy; // sekundy od roku 1900
     sekundy =  (unsigned long)ntp_packet[40] << 24;
     sekundy |= (unsigned long)ntp_packet[41] << 16;
     sekundy |= (unsigned long)ntp_packet[42] << 8;
     sekundy |= (unsigned long)ntp_packet[43];
     // Vrati pocet sekund a pripocita casovou zonu
     return sekundy - 2208988800UL + zona * SECS_PER_HOUR;
   }
 }
 // Pokud se dotaz nepodaril, vrat 0
 return 0;
}

// Funcke pro odeslani UDP paketu/framu na NTP server
void odesliNtpPacket(IPAddress &adresa) {
 memset(ntp_packet, 0, 48);
 ntp_packet[0]  = 0b11100011;
 ntp_packet[1]  = 0;
 ntp_packet[2]  = 6;
 ntp_packet[3]  = 0xEC;
 ntp_packet[12] = 49;
 ntp_packet[13] = 0x4E;
 ntp_packet[14] = 49;
 ntp_packet[15] = 52;
 Udp.beginPacket(adresa, 123);
 Udp.write(ntp_packet, 48);
 Udp.endPacket();
}
html.h
Kód:
// Pro prevod ceskych znaku v HTML kodu
// do zakladniho ASCII jsem pouzil prevod
// do formatu HEX NCR na webu:
// https://r12a.github.io/app-conversion/

static const char PROGMEM html[] = R"html(
<!DOCTYPE html>
<html lang="cs">
<head>
<title>WiFiLampa</title>
<link href="https://fonts.googleapis.com/css?family=Comfortaa&amp;subset=latin-ext" rel="stylesheet">
<script
 src="https://code.jquery.com/jquery-3.3.1.min.js"
 integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
 crossorigin="anonymous">
</script>
<style>
body{
 font-family: "Comfortaa", cursive;
 line-height: 150%;
 margin: 50px;
 text-align: center;
}
a{
 display: inline-block;
 width: 100px;
 padding: 5px;
 border: 1px solid steelblue;
 font-weight: bold;
}
a:link, a:visited, a:active{
 color: steelblue;
 background: white;
 text-decoration: none;
}
a:hover{
 color: white;
 background: steelblue;
 text-decoration: none;
}
</style>
<script>
// Tato funkce se zpracuje pote, co se stahne a vykresli cely HTML kod
$(function(){
 // Na zacatku si pres AJAX stahni JSON s informacemi
 $.get("/api?data=1", function(data){
   if(data.odpoved == 1){
     console.log(data);
     // Aktualizuj podle stazenych dat prvky na strance
     $("#cas").html(data.cas);
     $("#ram").html(data.ram);
     $("#teplota").html(data.teplota);
     $("#zacatek").val(data.zacatek);
     $("#konec").val(data.konec);
     // Podle stavu rele vykresli adekvatni tlacitko
     if(data.stav == 1){
       $("#rozsvitit").hide();
       $("#stav").html("&nbsp;sv&#x00ED;t&#x00ED;");
       $("#stav").css("color", "green");
     }
     else{
       $("#zhasnout").hide();
       $("#stav").html("&nbsp;je zhasnut&#x00E1;");
       $("#stav").css("color", "red");
     }
   }
   else{
     console.error("Chyba: " + data.odpoved);
   }
 });
 // Pokud klepnu na tlacitko nastaveni casu, odesli AJAXem nove casy automatickeho spinani
 $("#nastavit").click(function(){
   $.get("/api?zacatek=" + $("#zacatek").val() + "&konec=" + $("#konec").val(), function(data){
     if(data.odpoved == 1){
       console.log("Zmena casu automatickeho spinani a vypinani");
     }
     else{
       console.error("Chyba: " + data.odpoved);
     }
   });
 });
 // Po klepnuti na odkaz pro rozsviceni odesli AJAXem prikaz k rozsviceni
 $("#rozsvitit").click(function(){
   $.get("/api?stav=1", function(data){
     if(data.odpoved == 1){
       console.log("Rele sepnuto!");
       $("#rozsvitit").hide();
       $("#zhasnout").show();
       $("#stav").html("&nbsp;sv&#x00ED;t&#x00ED;");
       $("#stav").css("color", "green");
     }
     else{
       console.error("Chyba: " + data.odpoved);
     }
   });
 });
 // Po klepnuti na odkaz pro zhasnuti odesli AJAXem prikaz ke zhasnuti
 $("#zhasnout").click(function(){
   $.get("/api?stav=0", function(data){
     if(data.odpoved == 0){
       console.log("Rele vypnuto!");
       $("#rozsvitit").show();
       $("#zhasnout").hide();
       $("#stav").html("&nbsp;je zhasnut&#x00E1;");
       $("#stav").css("color", "red");
     }
     else{
       console.error("Chyba: " + data.odpoved);
     }
   });
 });
});
</script>
</head>
<body>
<h1>WiFiLampa<span id="stav"></span></h1>
<p>
 <a id="rozsvitit" href="#">Rozsv&#x00ED;tit</a>
 <a id="zhasnout" href="#">Zhasnout</a>
</p>
<p>
 Nastavit &#x010D;as automatick&#x00E9;ho sp&#x00ED;n&#x00E1;n&#x00ED; a vyp&#x00ED;n&#x00E1;n&#x00ED;
</p>
<p>
 Zapnout v <input id="zacatek" type="time" /> a vypnout v
 <input id="konec" type="time" />
 <input id="nastavit" type="button" value="Nastavit cas" />
</p>
<p>
 Aktu&#x00E1;ln&#x00ED; &#x010D;as: <span id="cas"></span>, teplota: <span id="teplota"></span>&nbsp;&#x00B0;C, voln&#x00E1; pam&#x011B;&#x0165;: <span id="ram"></span> kB
</p>
</body>
</html>
)html";
Nebo sáhnout po nějakém hotovém řešení např. Tasmota. Tady je výhoda rychlý rozvoj, stále možnost si Tasmotu přiohnout. Díky tomuto řešení máme k dispozici 10A Relé, s komunikací MQTT a s možností rozšíření o pousty senzorů s cenovkou do 100Kč. Takže jako hlavní nosný prvek bude právě Sonoff Basic.
 
Citovat
  


Příspěvků v tématu
RE: Domácí automatizace - bezpečně - od cuore - 20.4.2019, 21:04
Domácí automatizace - bezpečně - od Cinda - 10.10.2020, 16:53
Domácí automatizace - bezpečně - od Cinda - 11.10.2020, 12:03
Domácí automatizace - bezpečně - od cuore - 11.10.2020, 13:20
Domácí automatizace - bezpečně - od Cinda - 12.10.2020, 10:31
Domácí automatizace - bezpečně - od Cinda - 17.10.2020, 15:58
Domácí automatizace - bezpečně - od Cinda - 17.10.2020, 19:28
Domácí automatizace - bezpečně - od Cinda - 18.10.2020, 11:16

Přejít na fórum:


Prochází: 6 host(ů)