blog.ijacek007.cz

Blog o všem trochu jinak.

Dnes Vám představím velice povedený kousek čínské elektroniky, který si díky troše snažení můžete přeprogramovat k obrazu svému. Navíc jde o funkční celek, takže stačí jen upravit desku s plošným spojem, přeprogramovat elektroniku a máte krásný kousek Wi-Fi spínače na 220V, a to vše za velmi příjemnou cenu.

POZOR

Ač se celé zařízení dá rozebrat a testovat a programovat s bezpečným napětím, přesto se jedná o elektroniku připravenou na připojení na napětí 220V. Zapojení a provozování přístroje s takovým napětím vyžaduje znalost elektrikářských předpisů. Pokud takovými znalostmi nedisponujete, měli byste před připojením upraveného přístroje nechat zapojení zkontrolovat elektrikářem. Elektrické spotřebiče po špatné upravě mohou i zabíjet, proto to neberte na lehkou váhu!

Již před letošní zimou jsem přemýšlel, jak co nejjednodušeji pomocí prostředí Arduina naprogramovat spínání spotřebičů na 220V pomocí Wi-Fi. Složit podobnou elektroniku už přece jen chce pár součástek. Moduly ESP8266, které doma běžně používám, totiž mají nízké napětí i malý výkon na to, aby mohly spínat relátko, a tak bylo potřeba nejen naprogramovat samotný program do čipu, ale také složit veškerá spínání a relátka a to vše ještě pěkně pospolu uložit do nějaké krabičky, kde by to, pokud možno, nějaký ten čas odolalo. Nakonec jsem na to nenašel čas, a tak jsem ovládání pomocí ESP modulu odložil.


obrázek sonoff basic

Přitom existuje krásný kus čínské elektroniky, který již všechno v sobě má a stačí jej jen zapojit a používat, pokud však stejně jako já nechcete být závislí na aplikacích výrobce a jeho rozhraní, dá se tento kus elektroniky v celku jednoduše přeprogramovat. Krásou celého systému je nejen jednoduchost, se kterou lze celý systém přeprogramovat k obrazu svému, ale také cena celého zařízení nesoucí jméno sonoff basic pohybující se okolo 6 dolarů.


obrázek sonoff basic

Na zařízení jsem narazil na stránkách portálu živě.cz v seriálu o programování elektroniky. Hned jsem si tedy relátko objednal na webu www.itead.cc. Jakmile dorazil balíček z Číny, pustil jsem se do oživení plánů, které jsem před zimou opustil. Na jmenovaných stránkách portálu Živě.cz je krom postupu a popisu zapojení také zveřejněn zdrojový kód. Z něj jsem z velké části vycházel, ovšem trochu jsem jej poupravil, aby správně fungoval.


obrázek sonoff basic

Nejdříve však bylo nutné k relátku připájet kontakty, aby jej bylo možné spojit pomocí USB převodníku a nahrát na něj upravený kód. I když je relátko připojeno na 220V, má vlastní logiku na 3.3V a po připojení USB převodníku lze programovat bez nutnosti připojení velkého napětí. Celá elektronika krom spínacího relátka se totiž dá vcelku jednoduše napájet právě z připojeného převodníku pomocí USB.


obrázek arduino prostředi a sonoff basic

Po připojení k USB portu s připojeným USB převodníkem je nutné nastavit prostředí Arduino na správnou vývojovou desku, a to „Generic ESP8285“ a další potřebné údaje. Chvíli mi trvalo, než jsem narazil na správné nastavení, které u mého počítače fungovalo

Jak jsem již psal, vycházel jsem z úpravy zdrojového kódu zveřejněného na portále Živě.cz který jsem upravil dle vlastních potřeb. Relátko se rozbliká, zobrazí vlastní Wi-Fi, pomocí které jej můžete připojit do své bezdrátové sítě. Poté, co se připojí, si stáhne pomocí funkce TimeLib z časového serveru aktuální čas, ten se poté využívá pro automatické vypínání a spínání relátka a tento čas si sama elektronika jednou za hodinu zaktualizuje pomocí českého NTP serveru.


obrázek sonoff basic

Autor článku poté připojil do elektroniky obvod pro měření teploty vně relátka, nicméně já se rozhodl, že volný výstup z GP014 zatím nechám neobsazen. Integrované tlačítko, pomocí kterého se nahrává kód do paměti, jsem se rozhodl využít pro ruční spínání, pokud by bylo potřeba relátko sepnout či vypnout a neměl bych po ruce zrovna telefon.

Při testování programu zveřejněného na jmenovaném portálu jsem také zjistil, že autor nebral při psaní programu v potaz letní čas, a tak je během letního času čas na relátku špatně. Podle dostupných informací se totiž NTP servery nestarají o letní nebo zimní časové nastavení, ale vždy vracejí aktuální čas v zimním a je tedy na programu nebo příjemci NTP nastavení, aby k časové zóně v letním období přidali +1. Po dlouhém přemýšlení a studiu možného řešení jsem se nakonec rozhodl, že v paměti relátka vyčlením jednu adresu a zde budu manuálně z webového rozhraní zapínat nebo vypínat letní čas tak, aby byl vždy aktuální. Je to sice polovičaté řešení, nicméně plně plní funkci a navíc pomocí adresy API se dá později zautomatizovat


obrázek www stránka Wi-Fi relátka

V případě změny času by se prostě zavolala adresa spínače a čas se nastavil. Popřípadě také může celé relátko v pořádku fungovat i se špatným časem :-).


obrázek sonoff basic

Taky jsem přeprogramoval diodu, aby v případě, že relátko sepne, svítila a v případě, že je vypnuté, aby zhasla. Tak je jednoduše na první pohled vidět, jestli je relé zrovna vypnuto, nebo sepnuto. Jen poznamenám, že jakmile připojíte převodník, musíte podržet tlačítko GPI0 0, abyste mohly nahrát nový program.

Spotřeba zařízení

Protože je sonoff většinou 24 hodin denně zapojený v elektrické síti nastává otázka kolik si vlastně relátko připojené na Wifi spotřebuje energie. Aktuální konfigurace si z elektrické sítě veme 1.4W při rozpojeném relátku a 1.9W při sepnutí. Samozdřejmě měřeno bez jakékoliv zátěže kteou muže spínat.

Zde je program

// 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 "html.h" // HTML kod ovladaci stranky

// Cisla pinu LED, spinace a tlačítka
#define GPIO_LED 13
#define GPIO_TL 0
#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;


int signalstate = 1;
int signalstatelast = 1;

// Pomocne promenne pro synchronizaci casu
const char ntp_server[] = "tik.cesnet.cz";
uint8_t zona = 1 ;
uint8_t zonanew ;
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, letni_cas;

// 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) {
  // 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);
  pinMode(GPIO_TL, INPUT);

  // 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(5);
  zacatek_hodina = EEPROM.read(0);
  zacatek_minuta = EEPROM.read(1);
  konec_hodina = EEPROM.read(2);
  konec_minuta = EEPROM.read(3);
  letni_cas = EEPROM.read(4);

  // Nastartuj WiFiManager, ktery se postara o pripojeni k Wi-Fi
  WiFiManager wm;
  wm.setAPCallback(zacatekKonfigurace);
  wm.setSaveConfigCallback(konecKonfigurace);
  // IP parametry konfiguracni Wi-Fi site
  wm.setAPStaticIPConfig(IPAddress(192, 168, 100, 1), IPAddress(192, 168, 100, 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 (!wm.autoConnect("WiFiSonoff","Sonoff18")) {
    // 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 nastaveni letniho casu
    // http://ipadresa/api?letnicas=1 (nebo 0)
    else if (server.hasArg("letnicas")) {
      if (server.arg("letnicas") != NULL) {
        uint8_t stav = server.arg("letnicas").toInt();
        if (stav == 1) {
          Serial.println("Zapinam letni cas");
           EEPROM.write(4, 1);
           EEPROM.commit();
           // aby se zona projevila bez nutnosti restartovat obvod
            setSyncProvider(ziskejNtpCas);
          //  server.send(200, "application/json", "{\"odpoved\":\"1\"}");
       // přesmeruj na hlavni stránku aby se rovnouo aktualizoval datum na stránce

        server.sendHeader("Location", String("/"), true);
        server.send ( 302, "text/plain", "");
        }
        else {
          Serial.println("Vypinam letni cas");
          EEPROM.write(4, 0);
          EEPROM.commit();
           // aby se zona obnovila bez nutnosti restartovat obvod
            setSyncProvider(ziskejNtpCas);
         // server.send(200, "application/json", "{\"odpoved\":\"0\"}");
        // přesmeruj na hlavni stránku aby se rovnouo aktualizoval datum na stránce

        server.sendHeader("Location", String("/"), true);
        server.send ( 302, "text/plain", "");

        }
      }
      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\", \"letni_cas\":\"#letni_cas\"}";
      char zacatek[6];
      char konec[6];
      char cas[9];
   //   sensors.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("#letni_cas", String(letni_cas));
      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;
  }



if (digitalRead(GPIO_RELE) == LOW) {
// zde je opačná logika pokud je relé rozpojeno je nutné poslat HIGH aby dioda zhasla


  digitalWrite(GPIO_LED, HIGH);

}else{

  digitalWrite(GPIO_LED, LOW);
}





  // přečteme signál z tlačítka
  signalstate = digitalRead(GPIO_TL);

if (signalstate != signalstatelast) {

if (signalstate == LOW){


        if (digitalRead(GPIO_RELE) == LOW) {

           digitalWrite(GPIO_RELE, HIGH);
           Serial.println("zapinam rele tlacitkem ");
          }else{

          digitalWrite(GPIO_RELE, LOW);
            Serial.println("vypinam rele tlacitkem");

          }


      delay(50);

   }
}
  signalstatelast = signalstate;



}

// Funcke pro ziskani aktualniho casu z NTP serveru skrze UDP protokol
time_t ziskejNtpCas()
{
   letni_cas = EEPROM.read(4);
   zonanew = zona + letni_cas;
  Serial.println("aktualizuji cas");

  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 + zonanew * 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();
}

Soubor s HTML kódem

// 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>WiFiRele</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);
      $("#letni_cas").html(data.letni_cas);
      $("#zacatek").val(data.zacatek);
      $("#konec").val(data.konec);
      // Podle stavu rele vykresli adekvatni tlacitko
      if(data.stav == 1){
        $("#rozsvitit").hide();
        $("#stav").html("&nbsp;sepnuto");
        $("#stav").css("color", "green");
      }
      else{
        $("#zhasnout").hide();
        $("#stav").html("&nbsp;je vypnuto");
        $("#stav").css("color", "red");
      }
            // Podle stavu letniho casu vykresli adekvatni tlacitko
      if(data.letni_cas == 1){
        $("#zapni_letni").hide();
       }
      else{
        $("#vypni_letni").hide();
       }
    }
    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;zapnuto");
        $("#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 vypnuto");
        $("#stav").css("color", "red");
      }
      else{
        console.error("Chyba: " + data.odpoved);
      }
    });
  });

});

</script>
</head>
<body>
<h1>Rel&#x00E9;<span id="stav"></span></h1>
<p>
  <a id="rozsvitit" href="#">Zapnout</a>
  <a id="zhasnout" href="#">Vypnout</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>
  <a id="zapni_letni" href="/api?letnicas=1">Nastav letn&#x00ED; &#x010D;as</a>
  <a id="vypni_letni" href="/api?letnicas=0">Nastav zimn&#x00ED; &#x010D;as</a><br>
, voln&#x00E1; pam&#x011B;&#x0165;: <span id="ram"></span> kB
</p>
</body>
</html>
)html";

Mohlo by Vás zajímat

Článek z porátlu Živě.cz ze kterého jsem bral inspiraci i převážnou část kódu

Itead.cz stránky ze kterých jsem modul obědnal

Další články o programování z Arduina prostředí na těchto stránkách


Štítky článku elektronika | myslenky | opravy | programovani | windows-10 | zajimavosti |
Autor Ijacek.007 30.05.2018 zobrazeno 311x
Předchozí článek Wi-Fi teploměr s esp8266 se třemi čidly dht22 a regulaci svitu diody
ESP32 s oled displejem jako počítadlo odvinutých metrů Další článek


gravatar

Vložit komentář

Nick *:
WWW:
Email * (nezobrazuje se ):
Gravatar:
Pamatuj si mě:
Komentář článku *:
Opiš následující text: *

* - vyžadované údaje. RSS kanál s komentáři