OK

Při poskytování služeb nám pomáhají soubory cookie. Používáním našich služeb vyjadřujete souhlas s naším používáním souborů cookie. Více informací

Úvodní stránka » Arduino » Nextion display - VII.díl – Klávesnice a hodiny

Nextion display - VII.díl – Klávesnice a hodiny

Když vezmu projekty co jsem Vám ukazoval a co jsem viděl, většina hodnot nastavení je zapsaná přímo v programu a tedy nedají se měnit. Po každém restartu je vše pryč. Slušně řečeno, to je velmi nekomfortní. V tomto díle si vytvoříme numerickou klávesnici a ukážeme si jak nastavit hodinový modul. V dalších dílech budeme tyto dovednosti rozvíjet a data ukládat do paměti EEPROM.

Ale od začátku. Připravte si displej, hodinový modul a Arduino, opět použiji UNO.

Vytvoření klávesnice

Vytvořit funkční klávesnice, lze několika způsoby. Všechna řešení budou mít ale jedno společné. Vše budeme zpracovávat v displeji a v Arduinu až kompletní data. Jinak to není složité, ale je to pracné.

Klávesy

Každá klávesa je jenom tlačítko. Funkci tlačítka lze přiřadit v podstatě libovolnému poli, které vytvoříte v Nextion editoru.

Řešení první: využijte klasické tlačítko v Nextion Editoru, kterému dáte název a funkci podle využití. Jedna – 1, atd. Ke stažení zde.

Řešení druhé: opět použijeme klasické tlačítko, ale to co bude zobrazovat nahradíme samostatnými obrázky. Ke stažení zde.

Řešení třetí: jako pozadí využijeme obrázek o velikosti a zobrazení všech funkčních kláves. Bude se tedy jednat o jeden obrázek, který překryjeme tlačítkem – Hotspot, kterému přiřadíme příslušnou funkci. Jedná se o určení oblasti dotyku s příslušnou funkcí.

Tady se musím všem omluvit. Používáme displej o velikosti 240x320 px. Tato malovaná klávesnice se mi sem nevejde a ta co mám se mi nechce překreslovat, tak jsem použil velikost displeje 240*400 px. Ke stažení zde.

Které řešení je lepší?

Žádné, vždy záleží na konkrétní preferenci. První řešení je rychlé, zabere v displeji nejméně místa a nevyžaduje žádný grafický editor. Na druhou stranu jste graficky omezeni možnostmi Nextion Editoru. Druhé řešení je dobré, když máte tlačítka k dispozici jednotlivě a máte dost místa. Zabírá ho nejvíce. Každé tlačítko je vlastně samostatný obrázek. Poslední řešení je kompromis mezi prvním a druhým řešením, při zachování možnosti libovolné grafiky a hlavně libovolně můžete měnit pozadí jednotlivých kláves. Hlavní nevýhodou je náročnost přípravy obrázku pozadí.

U kláves narazíte na jednu věc, která mi na Nextion Editoru vyloženě vadí. Tou je, když si připravíte klávesnici na jednu velikost displeje, na jinou ji musíte připravit znovu. Dobře tedy zvažte jakou velikost displeje zvolíte, protože předělávat QWERTY klávesnici je opravdu pakárna.

Píšeme čísla

Základní otázka totiž zní, jestli na numerické klávesnice budeme psát čísla jako čísla, nebo jako text. Pro neznalé je to podivný dotaz. Připomeňme si že, číslo byte 1 <> char '1', ale byte 1 = char 49 (všimněte si použitých uvozovek). Tedy když napíšu textovou jedničku, uložím ji do EEPROM a pak za pomoci Serial přečtu a pošlu na monitor jako číslo, uvidím číslo 49.

Jo, chlebíček programátora je těžký. Proto je zapotřebí neustále myslet jestli je to text, nebo číslo a ještě k tomu jaké, u textu jak dlouhý.

Pokud budu na klávesnici psát PIN, přístupový kód, telefonní číslo, osobně to vždy píšu jako text a jako s textem s tím pracuji v Arduinu. Pokud chci přiřadit pořadové číslo a kdekoliv v klávesnici mám i text, také s tím vždy pracuji jako s textem a teprve v Arduinu převádím na číslo. Práce s textem je složitější, ale ve svém důsledku Vám to zjednoduší psaní kódu, protože si můžete vytvořit pouze jednu funkci, která zajistí vše a ušetří tak místo a pak např. GSM modul s telefonním číslem stejně pracuje jako s textem.

My si v příkladech ukážeme pouze práci s textem. Pro mě to má jednu velkou výhodu, neustále vím s čím pracuji a jak se k tomu mám chovat.

Délka textu

Při psaní PIN, nebo telefonního čísla je zapotřebí nastavit počet znaků. To dělám přímo v displeji, dá se dokonce i nastavit minimální a maximální délka textu, kdy se chyba dá ošetřit dialogovým oknem, viz. předchozí díly. Do arduina pak posíláme správně dlouhý text a nemusíme ho tam kontrolovat.

Skrytí textu

Také jedna z technik, se kterou se denně setkávám při použití karty a mobilního telefonu, který mám jako správný paranoik zašifrovaný. Řešení je jednoduché, do viditelného pole na displeji zobrazuji pouze hvězdičky a ten text správný text ukládám do proměnné.

To je k numerické klávesnici pro tento díl vše. Jednotlivé příklady máte ke stažení v odkazech. Jak porovnávat texty s daty uloženými v Arduinu se podíváme v jednom z následujících dílů. Nejdříve se totiž ukážeme jak pracovat s bloky dat v EEPROM.

Jak dostat data do Arduina?

To v ukázkách neřeším a podrobně si to projdeme v následujících dílech, kde budeme ukládat logy. Pro ty co spěchají. Přiřaďte tlačítku OK (křížku) funkci sendme, v arduinu jako reakci na tlačítko pošlete dotaz na data z příslušného pole funkcí get_text u textu, get_value v případě čísla.

Automatické vytvoření klávesnice

Asi není úplně fér, když jsem Vás nechal přečíst všechny předchozí řádky, abych Vám řekl, že Nextion editor ve verzi 0.43 (aktuální verze v době tvorby tohoto článku) umí vytvořit klávesnici automaticky. Není hezká, ale je to bez práce. Vytvoření klávesnice je jednoduché. Vytvořte textové, nebo číselné pole a parametr vscope nastavte jako global. Pak v parametru key vyberte typ klávesnice, kterou chcete použít. Bleskem se vytvoří klávesnice a pak stačí stisknout debug a můžete testovat.

Úplně ideální to není. Když máte pole ve formátu čísla, stejně si na klávesnici můžete zadat co chcete, jenom se Vám to nepropíše. Co je ale na této funkci zajímavé, jsou použité příkazy, které nejsou uvedeny v žádné dokumentaci a přitom fungují. Určitě je nastuduji a postupně přidám do svých projektů.

Hodiny

V minulém díle jsme si ukázali ručičkové hodiny, řešených jako výpočty úhlů, což byla náhrada funkce Gauge. Zde se zaměříme na digitální hodiny, jejich nastavení a budeme si zobrazovat i datum.

To co Vám ukáži používám ve všech svých projektech s tímto displejem. Každá stránka displeje je tvořena pozadím s černým pruhem v její horní části, kde zobrazuji čas a případně i další důležité stavy, upozornění na chybová hlášení, sílu signálu, hlavní teplotu, či stav záložní baterie. Tak už alespoň víte, proč se v některých předchozích HMI souborech objevuje ta černá linka nahoře.

Stačí nahrát přiložení sketch, zapojit hodinový modul, který jsme si ukázali v tomto článku , připojit nahrát a připojit displej a vše funguje jak má.

HMI soubor ke stažení zde.

Co displej dělá. Po načtení stránky se spustí část aktualizace záhlaví, které zobrazí datum a čas v záhlaví stránky. Na hlavní stránce, po stisknutí ikony zubatého kolečka, se zobrazí stránka pro nastavení hodin. Arduino v tomto případě odešle do displeje ještě data pro nastavení času. Samostatně se nastavuje datum, den týdne a čas. Pro datum a čas jsem použil automaticky vytvořenou numerickou klávesnici. Proto je při zadávání nutné dodržet správný formát zadávání. Např. pro čas 21.3.25, datum 5.12.2017. Pokud zadáte neplatná data, aktualizace nastavení modulu neproběhne. Den v týdnu je výběrový seznam a pro naše potřeby se ukládá číslo do proměnné t_den. Pak po stisknutí ikony ulož, se data uloží a v záhlaví se hned objeví nová hodnota. Tím poznáte, že vše proběhlo v pořádku.

Sketch pro Arduino:

//------------------------------------------------------------------
// inicializace zakladnich knihoven
#include <Wire.h> //komunikacni knihovna
#define nextion Serial // port pro komunikaci s displejem Nextion
//------------------------------------------------------------------
// nastaveni nazvu pinu
// #define RX        0 // seriová komunikace display
// #define TX        1 // seriová komunikace display
// #define SDA      A4 // komunikace cas
// #define SCL      A5 // komunikace cas
//------------------------------------------------------------------
#define DS3231_I2C_ADDRESS 0x68 // nastaveni adresy modulu casu DS3231
//------------------------------------------------------------------
// promenne cas
byte second;
byte minute;
byte hour;
byte dayOfWeek;
byte dayOfMonth;
byte month;
byte year;
//------------------------------------------------------------------
// ostatni promenne
unsigned long casAktualizace; // posledni aktualizace casu zahlavi
//------------------------------------------------------------------
void setup() {
  //------------------------------------------------------------------
  nextion_init(9600); // inicializace displeje a nastaveni rychlosti (mozno ponechat prazdne)
  //------------------------------------------------------------------
  Wire.begin(); // inicializace sbernice I2C
}
//------------------------------------------------------------------
void loop() {
  //------------------------------------------------------------------
  // funkce nextion displej
  check_display();
  //------------------------------------------------------------------
  // kazdou vterinu aktualizuje datum a cas
  if ((unsigned long)(millis() - casAktualizace) >= 1000) {
    casAktualizace = millis(); // nastav novy cas cteni
    cteniCasuDS3231(); // precist cas
    aktualizaceZahlavi();
  }
}
//------------------------------------------------------------------
// akce po stisku tlacitka
void touch_Return(String page_ID, String component_ID, String touch_event) {
  //------------------------------------------------------------------
  if (page_ID == "1") {
    if (component_ID == "13") { // uloz datum a cas
      String txt_String;
      char text[12];
      Wire.beginTransmission(DS3231_I2C_ADDRESS);
      Wire.write(0); // nastav registr DS3231 na 00h
      txt_String = get_text("t2");
      txt_String.toCharArray(text, txt_String.length() + 2);
      hour = atoi(strtok(text, ".:"));
      minute = atoi(strtok(NULL, ".:"));
      second = atoi(strtok(NULL, ".:"));
      Wire.write(decToBcd(second)); // nastav sekundy
      Wire.write(decToBcd(minute)); // nastav minuty
      Wire.write(decToBcd(hour)); // nastav hodiny
      Wire.write(decToBcd(get_value("t_den"))); // nastav den tydne (1=nedele, 7=sobota)
      txt_String = get_text("t0");
      txt_String.toCharArray(text, txt_String.length() + 2);
      Wire.write(decToBcd(atoi(strtok(text, ".")))); // nastav datum (1 az 31)
      Wire.write(decToBcd(atoi(strtok(NULL, ".")))); // nastav mesic
      Wire.write(decToBcd(atoi(strtok(NULL, ".")) - 2000)); // nastav rok (0 az 99)
      Wire.endTransmission();
    } // end component
  } // end page
  // aktualizace zahlavi
  aktualizaceZahlavi();
} // end void
//------------------------------------------------------------------
// akce po nacteni stranky
void page_Return(String page_ID) {
  //------------------------------------------------------------------
  // akce jednotlivych stranek
  if (page_ID == "1") { // kdyz se nacte stanka nastaveni casu
    cteniCasuDS3231();
    send_text("t0", sestavDatum()); // odesli aktualni datum
    send_value("t_den", dayOfWeek); // odesli den týdne
    send_text("t1", sestavDenTydne()); // odesli den týdne
    send_text("t2", sestavCas()); // odesli aktualni cas
  } // end page
  //--------------------------------------------------
  // aktualizace zahlavi stranky provadi se vzdy po nacteni stranky
  aktualizaceZahlavi();
} // end void
//------------------------------------------------------------------
//aktualizace zahlavi stranky
void aktualizaceZahlavi() {
  // send_text("time", sestavCas().substring(0, sestavCas().length() - 3)); // aktualizuj cas bez vterin
  send_text("time", sestavCas()); // aktualizuj cas vcetne vterin
  send_text("date", sestavDatum()); // aktualizuj datum
}
//------------------------------------------------------------------
// precte cas a nastavi promenne pro dalsi zpracovani
void cteniCasuDS3231() {
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0); // nastav registr DS3231 na 00h
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
  // request sedm bytu z DS3231 - zacina z registru 00h
  second = bcdToDec(Wire.read() & 0x7f);
  minute = bcdToDec(Wire.read());
  hour = bcdToDec(Wire.read() & 0x3f);
  dayOfWeek = bcdToDec(Wire.read());
  dayOfMonth = bcdToDec(Wire.read());
  month = bcdToDec(Wire.read());
  year = bcdToDec(Wire.read());
}
//------------------------------------------------------------------
// prevod hodnot
byte decToBcd(byte val) { // konverze cisla DEC na BIN
  return ( (val / 10 * 16) + (val % 10) );
}
byte bcdToDec(byte val) { // konverze cisla BIN na DEC
  return ( (val / 16 * 10) + (val % 16) );
}
//------------------------------------------------------------------
// sestavi cas, datum a den v tydnu pro dalsi zpracovani
String sestavCas() {
  // sestaveni casu
  String cas;
  cas = (String)hour;
  cas += ":";
  if (minute < 10) cas += "0";
  cas += (String)minute;
  cas += ":";
  if (second < 10) cas += "0";
  cas += (String)second;
  return cas;
}
String sestavDatum() {
  // sestaveni data
  String datum;
  datum = (String)dayOfMonth;
  datum += ".";
  datum += (String)month;
  datum += ".20";
  datum += (String)year;
  return datum;
}
String sestavDenTydne() {
  String den;
  switch (dayOfWeek) {
    case 1: den = "Nedele"; break;
    case 2: den = "Pondeli"; break;
    case 3: den = "Utery"; break;
    case 4: den = "Streda"; break;
    case 5: den = "Ctvrtek"; break;
    case 6: den = "Patek"; break;
    case 7: den = "Sobota"; break;
  }
  return den;
}
//------------------------------------------------------------------
String Nextion_receive(boolean read_data) { //returns generic

  boolean answer = false; // znacka
  char bite; // promenna pro ulozeni znaku
  String cmd; // promenna pro ulozeni textu
  byte countEnd = 0; // pocitadlo
  unsigned long previous; // cas spusteni
  int timeout = 1000; // doba po kterou se ceka na prichozi data
  previous = millis();

  do { // cekani na spravnou odpoved
    if (nextion.available() > 0) { // kdyz jsou k dispozici data, precti data
      bite = nextion.read();
      cmd += bite;
      if ((byte)bite == 0xff) countEnd++;
      if (countEnd == 3) answer = true;
    }
  }
  while (!answer && !((unsigned long)(millis() - previous) >= timeout)); // ceka na spravnou hodnotu, nebo uplynuti casu

  if (read_data) { // read general data
    if (cmd[0] == 0x65) { // Touch event return data
      // 0X65 + Page ID + Component ID + TouchEvent + End
      touch_Return(String(cmd[1], DEC), String(cmd[2], DEC), String(cmd[3], DEC));
    }
    else if (cmd[0] == 0x66) { // Current page ID number returns
      // 0X66 + Page ID + End
      page_Return(String(cmd[1], DEC));
    }
    else if (cmd[0] == 0x67) { // Touch coordinate data returns
      // 0X67++ Coordinate X High-order+Coordinate X Low-order+Coordinate Y High-order+Coordinate Y Low-order+TouchEvent State+End
    }
    else if (cmd[0] == 0x68) { // Touch Event in sleep mode
      // 0X68++Coordinate X High-order+Coordinate X Low-order+Coordinate Y High-order+Coordinate Y Low-order+TouchEvent State+End
    }
  }
  else { //read get data
    if (cmd[0] == 0x70) { // String variable data returns
      // X70+Variable Content in ASCII code+End
      return cmd;
    }
    else if (cmd[0] == 0x71) { // Numeric variable data returns
      // 0X71+variable binary data(4 bytes little endian mode, low in front)+End
      return cmd;
    }
  }
}
//------------------------------------------------------------------
void check_display() { // kontrola prijatych dat
  if (nextion.available() > 0) // kontroluje obsah pameti, pokud nen nic odeslano, dalsi cast programu se neprovede
  {
    Nextion_receive(true); // precist hodnoty z serial portu
  }
}
//------------------------------------------------------------------
void nextion_init(int speed_init) { // nastaveni pri spusteni displeje
  nextion.begin(speed_init);
}
//------------------------------------------------------------------
void send_text(String componentID, String text) { // odesli text
  String txt = componentID + ".txt=\"" + text + "\"";
  send_Command(txt.c_str());
}
//------------------------------------------------------------------
void send_value(String componentID, int value) {
  String compValue = componentID + ".val=" + String(value); // odesli hodnotu
  send_Command(compValue.c_str());
}
//------------------------------------------------------------------
String get_text(String componentID) { // nacti text
  String txt = "get " + componentID + ".txt";
  send_Command(txt.c_str());
  txt = Nextion_receive(false);
  txt = txt.substring(1, txt.length() - 3);
  return txt;
}
//------------------------------------------------------------------
unsigned int get_value(String componentID) { // nacti hodnotu
  String compValue = "get " + componentID + ".val";
  send_Command(compValue.c_str());
  unsigned int value = 0;
  uint8_t temp[8] = {0};
  Serial.readBytes((char *)temp, sizeof(temp));
  if ((temp[0] == (0x71)) && (temp[5] == 0xFF) && (temp[6] == 0xFF) && (temp[7] == 0xFF)) {
    value = (temp[4] << 24) | (temp[3] << 16) | (temp[2] << 8) | (temp[1]);
    return value;
  }
  else return -1;
}
//------------------------------------------------------------------
void send_Command(const char* cmd) { // odeslani dat do displeje
  nextion.print(cmd);
  nextion.write(0xFF);
  nextion.write(0xFF);
  nextion.write(0xFF);
  nextion.flush();
}

V programu pro arduino, nenajdete nic nového, než jsme si ukázali v předchozích dílech, vyjma dvou celkem zajímavých funkcí. Obě se týkají práci s textem.

Funkce strtok

Tato funkce extrahuje část textu, oddělených libovolnými oddělovači. Píši to v množném čísle, protože jako oddělovač můžete nastavit jeden znak, nebo i libovolnou sekvenci znaků, přičemž každý musí samostatně.

Funkce vypadá následovně a nedá se měnit:

char text = .....
char text1 = strtok(text, ":");
char text2 = strtok(NULL, ":");
char text3= strtok(NULL, ":");

Do proměnné text1, uložíme část proměnné text do oddělovače ":", do text2 uložíme část proměnné text následující po předchozím oddělovači do dalšího oddělovače atd. Pokud máme známou délku textu, pracujeme s tím takto jak ukazuji. Pokud neznáme délku textu, můžeme použít funkci while a zpracovávat řetězce, dokud je k dispozici hodnota NULL. U while, ale nemůžeme měnit oddělovače.

Tato funkce zpracovává pouze řetězce typu char. V programu, jsme si tedy nejdříve museli převést String na char. To je také možná zajímavá funkce, ale tu jako druhou nemyslím.

Funkce substring

Také extrahuje část textu, ale přesně stanové délky. Tuto funkci najdete v zakomentované části funkce aktualizujCas(). Funguje tak, že označíte řetězec, přidáte substring, označíte začátek a konec extrahovaného řetězce. Já jsem použil funkci pro výpočet délky extrahovaného řetězce a odečetl poslední 3 znaky (tečku a vteřiny). Pokud si vzpomínáte na část práce s textem, tak jsem tam psal z znaku, který označuje konec řetězce. Tady se na něj nebere zřetel.

Jinak program samotný je okomentovaný a vyjma výše uvedeného Vás tam nečeká nic co by jsme si už neukázali v předchozích dílech. Jenom je to jinak uspořádané.

Po povinném úvodu v hlavní smyčce kontrolujeme příchozí data a každou vteřinu odesíláme aktualizaci záhlaví stránky. Pouze nastavení času, není jako samostatná funkce, ale je součástí funkce displeje. Protože tam převádíme text na čísla, přišlo mi to ta jednodušší.

Tedy musím smutně konstatovat, že jsme se nic nového nenaučili. Nebo nesouhlasíte?

Závěr

Ukázali jsme si funkce, které opět jasně ukazují, jak může Nextion displej zjednodušit psaní vlastního programu. Už se budu jenom opakovat. Snad jenom zvažte, jestli je nutné aktualizovat čas každou vteřinu. Sériová komunikace, nepatří mezi nejrychlejší a zabírá dost času displeje. Stejně tak se dá přidat funkce, která aktualizuje datum, jenom při změně stránky a změně data. To jsem pro jednoduchost nepřidával.

Příště se vrátíme k numerické klávesnici a ukážeme si jak pracovat s hesly v případě, že máme více uživatelů. Klíčová bude práce s EEPROM. Mimo velmi zajímavého kódu na kontrolu hesla, nestanovené délky se můžete těšit na zpracování bloků dat v EEPROM, které budeme potřebovat pro ukládání a čtení logů.

JB

jaroslav.bohac@arduinotech.cz

Přidat komentář

Zvýrazněné položky jsou povinné.

Přehled komentářů

  1. Úžasný (zdendis, 26.3.2017 14:13:46) Odpovědět

    Zase jako vždy paráda děkuji

  2. Nefunguje odkaz u třetího příkladu (PetrP, 27.3.2017 11:21:06) Odpovědět

    Dobrý den,
    máte chybuv odkazu u třetího příkladu.
    Má být:
    https://www.arduinotech.cz/soubor/klavesnice-3-hmi/
    A máte:
    https://www.arduinotech.cz/inpage/nextion-display-vii-dil-klavesnice-a-hodiny/Klavesnice_3.HMI

  3. Latence (Zdendis, 1.4.2017 18:17:28) Odpovědět | Zobrazit odpovědi

    Nestává se Vám že při naběhnutí nebo při změně stránky je dlouhá latence než čas naskočí?
    A už vůbec při změně aktualizace pozadi
    if ((unsigned long)(millis() - casAktualizace) >= 30000)

    nebo to dělá jen mě :)?

    1. Re: Latence (Jaroslav Boháč, 2.4.2017 16:39:10) Odpovědět

      Máte tam nastavenu prodlevu 30s. V původním programu je 1s: if ((unsigned long)(millis() - casAktualizace) >= 1000) a pak aktualizace času vždy po změně stránky - musíte ale Arduinu poslat informaci z displeje že se změnila stránka - "sendme".
      Změňte si tu hodnotu.

      1. Re: Re: Latence (Zdendis, 2.4.2017 17:35:25) Odpovědět

        Právě že mám už poučen z minulých dotazů k displeji sendme pro jistotu na každé stránce při testech .. pokud to nikomu jinému nedělá při změně zvýšení času aktualizace pozadí tak na to přijdu díky ;)

    2. Re: Latence (Zdendis, 2.4.2017 19:10:07) Odpovědět

      Na seriovém monitoru mi to posílá správně ale na displeji se to nezobrazí zajimavý :D

      1. Re: Re: Latence (Jaroslav Boháč, 2.4.2017 20:13:02) Odpovědět

        Pošlete mi program a HMI soubor emailem, podívám se na to.

  4. Alarm (zdendis, 10.4.2017 13:43:26) Odpovědět | Zobrazit odpovědi

    Dobrý den,
    několikrát tu píšete že jste dělal na Alarmu s Arduinem je někde dostupný kód ?

    Děkuji

    1. Re: Alarm (Jaroslav Boháč, 15.4.2017 21:43:56) Odpovědět

      Jako celek ho zde nenajdete. Ale pokud složíte jednotlivé střípky i z následujících dílů, dostanete se k němu. Jinak o čem bych pak psal :-).

      1. Re: Re: Alarm (Zdendis, 16.4.2017 11:09:32) Odpovědět

        To je fakt budu trpělivě čekat na další díly :)

  5. Novej dil (Zdendis, 22.4.2017 13:41:59) Odpovědět | Zobrazit odpovědi

    Můžem se těšit na nový díl :) ?

    1. Re: Novej dil (Zdendis, 16.5.2017 22:04:14) Odpovědět

      Bude pokracovani? Nebo z nedostatku casu jiz toto vlakno nebude pokracovat

      1. Re: Re: Novej dil (Zdendis, 31.5.2017 21:39:52) Odpovědět

        :( snad bude pokračování


TOP produkty

Arduino DUE

Arduino DUE
696 Kč s DPH

Arduinotech GSM shield

Arduinotech GSM shield
877 Kč s DPH

Kontakt

Ing. Petr Foltýn
Kunčice pod Ondřejníkem 814, 73913
TOPlist