Individální projekty MPOA

Mikroprocesory s architekturou ARM

Uživatelské nástroje

Nástroje pro tento web


2017:greenhouse-ctrl

Rozdíly

Zde můžete vidět rozdíly mezi vybranou verzí a aktuální verzí dané stránky.

Odkaz na výstup diff

Obě strany předchozí revize Předchozí verze
Následující verze
Předchozí verze
2017:greenhouse-ctrl [2018/01/14 23:21]
Josef Křivský
2017:greenhouse-ctrl [2018/01/15 07:28] (aktuální)
Josef Křivský
Řádek 8: Řádek 8:
  
 Cílem tohoto projektu je co nejvíce automatizovat obsluhu a udržování běžného zahradního skleníku. Obsahem návrhu je kompletní software řízení a obsluhy, ale také co nejjednodušší hardware pro příslušné periferie. Základní myšlenkou je možnost kontroly a nastavení jednotlivých vlastností skleníku z pohodlí domova, za pomoci ethernetového rozhraní a běžného PC. Pomocí definovaných příkazů je tak možné jak kontrolovat vlhkost půdy, teplotu a osvětlení ve skleníku, tak nastavit jejich limitní hodnoty pro spuštění zavlažování,​ odvětrávání/​vytápění skleníku a přisvětlení zářivkami. Cílem tohoto projektu je co nejvíce automatizovat obsluhu a udržování běžného zahradního skleníku. Obsahem návrhu je kompletní software řízení a obsluhy, ale také co nejjednodušší hardware pro příslušné periferie. Základní myšlenkou je možnost kontroly a nastavení jednotlivých vlastností skleníku z pohodlí domova, za pomoci ethernetového rozhraní a běžného PC. Pomocí definovaných příkazů je tak možné jak kontrolovat vlhkost půdy, teplotu a osvětlení ve skleníku, tak nastavit jejich limitní hodnoty pro spuštění zavlažování,​ odvětrávání/​vytápění skleníku a přisvětlení zářivkami.
 +
 +{{ :​2017:​xkrivs00:​img_20180115_063847_7.jpg?​400 |}}
 +
 ---- ----
  
Řádek 15: Řádek 18:
  
  
 +Key Features
 +  *Microcontroller:​
 +     ​*Kinetis MK64FN1M0VLL12 in 100LQFP microcontroller featuring ARM® Cortex™-M4 32-bit core with DSP instructions and Floating Point Unit (FPU) working @ 120 MHz max CPU frequency
 +  *Memory:
 +     *1024 KB program flash memory, 256 KB RAM, and FlexBus external bus interface
 +  *System peripherals:​
 +     ​*Multiple low-power modes, low-leakage wake-up unit, 16-channel DMA controller
 +  *Clocks:
 +     *3x Internal Reference Clocks: 32KHz, 4MHz and 48MHz, 2x Crystal inputs: 3-32MHz (XTAL0) and 32kHz (XTAL32/​RTC),​ PLL and FL
 +  *Analog modules:
 +     *2x 16-bit SAR ADCs up 800ksps (12-bit mode), 2x 12-bit DACs, 3x Analog comparators,​ Voltage reference 1.13V
 +  *Communication interfaces:
 +     *1x 10/100 Mbit/s Ethernet MAC controller with MII/RMII interface IEEE1588 capable, 1x USB 2.0 Full-/​Low-Speed Device/​Host/​OTG controller with embedded 3.3V/120mA Vreg, and USB device Crystal-less operation, 1x Controller Area Network (CAN) module, 3x SPI modules, 3x I2C modules. Support for up to 1 Mbit/s, 6x UART modules, 1x Secure Digital Host Controller (SDHC), 1x I2S module
 +  *Timers:
 +     *2x 8-channel Flex-Timers (PWM/Motor control), 2x 2-channel FlexTimers (PWM/Quad decoder), 32-bit PITs and 16 bit low-power timers, Real-Time Clock (RTC), Programmable delay block
 +  *Security and integrity modules:
 +     ​*Hardware CRC and random-number generator modules, Hardware encryption supporting DES, 3DES, AES, MD5, SHA-1 and SHA-256 algorithms
 +  *Operating characteristics:​
 +     ​*Voltage range: 1.71 to 3.6 V, Flash write voltage range: 1.71 to 3.6 V
  
 +----
  
-Key Features +====== Vývoj HW ====== 
-*Microcontroller:​ + 
-    ​Kinetis MK64FN1M0VLL12 in 100LQFP microcontroller featuring ARM® Cortex™-M4 32-bit core with DSP instructions  +Požadavkem na celou konstrukci automatizovaného skleníku byla především jednoduchost implementace/​instalaceale také cena celého systému. Vzhledem k velkému možství některých součástek v inventáři zadavatelebyly proto použity především ony. 
-    and Floating Point Unit (FPU) working @ 120 MHz max CPU frequency + 
-*Memory: +Seznam použitých komponetů
-    1024 KB program flash memory256 KB RAMand FlexBus external bus interface +  * Mikrokontrolér FRDM-K64F 
-*System peripherals+  Servo Hitec HS-485HB 
-    ​Multiple low-power modes, low-leakage wake-up unit, 16-channel DMA controller +  * NTC Termistor Eclipsera 1488979094 
-*Clocks: +  Arduino modul pro měření intenzity světla BH1750 
-    3x Internal Reference Clocks: 32KHz, 4MHz and 48MHz, 2x Crystal inputs: 3-32MHz (XTAL0) and 32kHz (XTAL32/​RTC), ​ +  * Půdní Vlhkoměr Modul pro Arduino 
-    PLL and FL + 
-*Analog modules+Odkazy na stránky výrobců/​prodejců
-    2x 16-bit SAR ADCs up 800ksps (12-bit mode), 2x 12-bit DACs, 3x Analog comparators,​ Voltage reference 1.13V + 
-*Communication interfaces: +  *http://hitecrcd.com/​products/​servos/​sport-servos/analog-sport-servos/​hs-485hb-deluxe-hd-ball-bearing-servo/​product 
-    1x 10/100 Mbit/s Ethernet MAC controller with MII/RMII interface IEEE1588 capable, 1x USB 2.0 Full-/Low-Speed  +  ​*https:​//www.nxp.com/products/​processors-and-microcontrollers/arm-based-processors-and-mcus/kinetis-cortex-m-mcus/k-seriesperformancem4/​k2x-usb/​freedom-development-platform-for-kinetis-k64-k63-and-k24-mcus:​FRDM-K64F 
-    ​Device/Host/OTG controller with embedded 3.3V/120mA Vreg, and USB device Crystal-less operation, 1x Controller  +  *https://arduino-shop.cz/​arduino/​1574-ntc-termistor-10k-1-3950-1m-vodotesna-sonda-1488979094.html 
-    Area Network (CAN) module, 3x SPI modules, 3x I2C modules. Support for up to 1 Mbit/s, 6x UART modules, 1x  +  *https://​arduino-shop.cz/​arduino/​902-arduino-mereni-intenzity-svetla-1420672425.html 
-    Secure Digital Host Controller (SDHC), 1x I2S module +  *https://​arduino-shop.cz/​arduino/​1399-pudni-vlhkomer-modul-pro-arduino-1474354607.html
-*Timers: +
-    2x 8-channel Flex-Timers (PWM/Motor control), 2x 2-channel FlexTimers (PWM/Quad decoder), 32-bit PITs and 16  +
-    bit low-power timers, Real-Time Clock (RTC), Programmable delay block +
-*Security and integrity modules: +
-    Hardware CRC and random-number generator modules, Hardware encryption supporting DES, 3DES, AES, MD5, SHA-1 and  +
-    SHA-256 algorithms +
-*Operating characteristics: +
-    Voltage range: 1.71 to 3.6 V, Flash write voltage range1.71 to 3.6 V+
  
 ---- ----
Řádek 48: Řádek 63:
 ====== Software ====== ​ ====== Software ====== ​
  
-V hlavním souboru main.c probíha ​inicializace ​ADC převodníkuUSB device audio class audio kodeku pro přehrávání audio výstupu.+V hlavním souboru main.cpp probíhá ​inicializace ​celého programu pomocí jednotlivých knihoven. Pro celý program byly použity veřejně dostupné knihovny mbed(Rev. 109)mbed-rtos(Rev. 95), EthernetInterface(Rev. 49) také knihovna BH1750 dostupná na stránce https://​os.mbed.com/​users/​vrabec/​code/​BH1750/​ , kterou však bylo z důvodu jednoduchosti použití třeba značně modifikovat. Pro ostatní periferie byla vytvořena nová c++ knihovna Peripherals,​ která zahrnuje patřičnou inicializaci periferií (analogové vstupy, digitální a PWM výstupy, ...) a následně práci s nimi.
  
 Použité knihovny v soubotu main.c Použité knihovny v soubotu main.c
  
 +  * #include "​mbed.h"​
 +  * #include "​rtos.h"​
 +  * #include "​EthernetInterface.h"​
 +  * #include "​BH1750.h"​
 +  * #include "​Peripherals.h"​
 +
 +=== Knihovna Peripherals ===
 +Zapouzdřuje do tříd veškeré vlastnosti pro snímání vlhkosti a teploty, a ovládání akčních členů (servomotorem řízené otevírání ventilace a reléově ovládané zavlažování/​ventilace/​topení). Skládá se z
 +
 +Peripherals.h:​
 +<code c>
 +class Humid
 +{
 +public:
 +    Humid(PinName AInp = A0, PinName AOut = D0);
 +    uint8_t readHumidity(void);​
 +    void setLimit(uint8_t lim);
 +    void setCurrentLimit(void);​
 +    uint8_t getLimit(void);​
 +
 +private:
 +    AnalogIn ​  ain;
 +    DigitalOut dop;
 +    uint8_t Limit;
 +};
 +
 +class Temp
 +{
 +public:
 +    Temp(PinName AInp = A1);
 +    float readTemperature(void);​
 +    void setHighLimit(float lim);
 +    void setLowLimit(float lim);
 +    void setVentLimit(float lim);
 +    void setCurrentHighLimit(void);​
 +    void setCurrentLowLimit(void);​
 +    void setCurrentVentLimit(void);​
 +    float getHighLimit(void);​
 +    float getLowLimit(void);​
 +    float getVentLimit(void);​
 +
 +private:
 +    AnalogIn ​  ain;
 +    float HLimit, LLimit, VLimit;
 +};
 +
 +class Vent
 +{
 +public:
 +    Vent(PinName PWM = D9);
 +    void open(uint8_t percent = 100);
 +    void close(void);​
 +    ​
 +private:
 +    PwmOut pwm;
 +    uint8_t percentage;
 +};
 +
 +class Fan
 +{
 +public:
 +    Fan(PinName DOut = D4);
 +    void Start(void);​
 +    void Stop(void);
 +private:
 +    DigitalOut fan;
 +};
 +
 +class Heat
 +{
 +public:
 +    Heat(PinName DOut = D5);
 +    void Start(void);​
 +    void Stop(void);
 +private:
 +    DigitalOut heater;
 +};
 +
 +class Water
 +{
 +public:
 +    Water(PinName DOut = D6);
 +    void Start(void);​
 +    void Stop(void);
 +private:
 +    DigitalOut sprinkler;
 +};
 +
 +class Light
 +{
 +public:
 +    Light(PinName DOut = D7);
 +    void Start(void);​
 +    void Stop(void);
 +private:
 +    DigitalOut lighting;
 +};
 +</​code>​
 +
 +Peripherals.cpp:​
 +
 +<code c>
 +Humid::​Humid(PinName AInp, PinName AOut):​ain(AInp),​dop(AOut)
 +{
 +    Limit = 110;
 +    dop = 0;
 +}
 +
 +uint8_t Humid::​readHumidity(void)
 +{
 +    dop = 1;
 +    wait_ms(10);​
 +    double humidity = 1-ain.read();​
 +    humidity = ((humidity*100)-7)*1.471;​
 +    uint8_t hum = humidity;
 +    dop = 0;
 +    return hum;
 +}
 +
 +void Humid::​setLimit(uint8_t lim)
 +{
 +    Limit = lim;
 +}
 +
 +void Humid::​setCurrentLimit(void)
 +{
 +    uint8_t humidity = readHumidity();​
 +    setLimit(humidity);​
 +}
 +
 +uint8_t Humid::​getLimit(void)
 +{
 +    return Limit;
 +}
 +
 +Temp::​Temp(PinName AInp):​ain(AInp)
 +{  ​
 +    LLimit = -30;
 +    HLimit = 125;
 +    VLimit = 125;
 +}
 +
 +float Temp::​readTemperature(void)
 +{
 +    double voltage, resistance, temperature;​
 +    float temp;
 +    int resistor = 9910;
 +    int thermistor = 10000;
 +    int refTemp = 25;
 +    int beta = 3380;
 +    ​
 +    voltage = (ain.read())*3.3;​
 +    ​
 +    resistance = (voltage*resistor)/​(3.3-voltage);​
 +    ​
 +    temperature = (log(resistance/​thermistor))/​beta;​
 +    temperature += 1.0 / (refTemp + 273.15);
 +    temperature = 1.0 / temperature;​
 +    temperature -= 273.15;
 +    ​
 +    temp = temperature;​
 +    ​
 +    return temp;
 +}
 +
 +void Temp::​setHighLimit(float lim)
 +{
 +    HLimit = lim;
 +}
 +
 +void Temp::​setLowLimit(float lim)
 +{
 +    LLimit = lim;
 +}
 +
 +void Temp::​setVentLimit(float lim)
 +{
 +    LLimit = lim;
 +}
 +
 +void Temp::​setCurrentHighLimit(void)
 +{
 +    float temperature = readTemperature();​
 +    setHighLimit(temperature);​
 +}
 +void Temp::​setCurrentLowLimit(void)
 +{
 +    float temperature = readTemperature();​
 +    setLowLimit(temperature);​
 +}
 +
 +void Temp::​setCurrentVentLimit(void)
 +{
 +    float temperature = readTemperature();​
 +    setVentLimit(temperature);​
 +}
 +
 +float Temp::​getHighLimit(void)
 +{
 +    return HLimit;
 +}
 +
 +float Temp::​getLowLimit(void)
 +{
 +    return LLimit;
 +}
 +
 +float Temp::​getVentLimit(void)
 +{
 +    return VLimit;
 +}
 +
 +
 +Vent::​Vent(PinName PWM):​pwm(PWM)
 +{
 +    pwm.period(0.020);​
 +    pwm.pulsewidth(0.0009);​
 +    percentage = 0;
 +}
 +
 +void Vent::​open(uint8_t percent)
 +{
 +
 +    uint16_t width = ((6 * percentage)+900);​
 +    ​
 +    while(width != (6*percent)+900)
 +    {
 +        if(percentage>​percent)
 +        {
 +            width-=1;
 +            pwm.pulsewidth_us(width);​
 +            wait_ms(10);​
 +        }
 +        else
 +        {
 +            width+=1;
 +            pwm.pulsewidth_us(width);​
 +            wait_ms(10);​
 +        }
 +    }
 +    percentage = percent;
 +}
 +
 +void Vent::​close(void)
 +{
 +    open(0);
 +}
 +
 +
 +Fan::​Fan(PinName DOut):​fan(DOut)
 +{
 +    fan = 0;
 +}
 +
 +void Fan::​Start(void)
 +{
 +    fan = 1;
 +}
 +    ​
 +void Fan::​Stop(void)
 +{
 +    fan = 0;
 +}
 +.
 +.
 +.
 +</​code>​
 +Za zmínku stojí především třídy Humid a Temp, které nejen zapouzdřují různé proměnné a vstupy, ale také nad nimi provádějí řadu výpočtů. U třídy Humid se jedná pouze o úpravu snímaného napětí ze zesilovače a měření v pulzním režimu (z důvodu elektrolýzy na elektrodách snímače vlhkosti). Třída Temp však již provádí poměrně složité výpočty při převodu snímaného napětí z děliče (tvořen rezistorem a NTC termistorem) na hodnotu okolní teploty. Veškeré další třídy jsou v této knihovně již pouze kopie třídy Fan, které slouží k ovládání reléových výstupů, a které mají jen rozdílné názvy a výstupní piny na desce FRDM-K64F.
 +
 +
 +=== hlavní soubor main.h ===
 +
 +Soubor main.cpp lze rozdělit do několika částí:
 +  * Inicializace (zde probíhá pouze inicializace jednotlivých tříd pro příslušné periferie)
 +<code c>
 +#define ECHO_SERVER_PORT ​  23
 +
 +BH1750 light(I2C_SDA,​ I2C_SCL); // Senzor osvětlení
 +Humid humidity; ​                // Senzor vlhkosti
 +Temp temperature; ​              // Snímač teploty
 +Vent ventilation; ​              // Otevírání ventilace
 +Fan conditioning; ​              // Ovládání aktivního větrání
 +Heat heating; ​                  // Ovládání topení
 +Water irrigation; ​              // Spouštění zavlažování
 +Light illumination; ​            // Ovládání světel
 +
 +enum STAT                       // Proměnná pro uložení stavu celého systému
 +{
 +    READY = 1, SETTINGS, MANUAL, ERROR
 +}sklenikstav;​
 +</​code>​
 +Stav systému sklenikstav je díky třídám pouze pomocnou proměnnou pro rozhodovací stromy uživatelského rozhraní.
 +
 +  * Ovládací vlákna RTOS (zde již probíhá periodická kontrola stavu skleníku, a příslušné reakce periferií)
 +<code c>
 +void watering_thread(void)
 +{
 +    while(true)
 +    {
 +        uint8_t hum = humidity.readHumidity();​
 +        float avghum = 0;
 +        if(hum < humidity.getLimit())
 +        {
 +            for(int i=0; i<6; i++)
 +            {
 +                avghum += humidity.readHumidity();​
 +            }
 +            ​
 +            avghum /= 6;
 +            ​
 +            if(avghum < humidity.getLimit())
 +            {
 +                irrigation.Start();​
 +                Thread::​wait(180000);​
 +                irrigation.Stop();​
 +            }
 +            avghum = 0;
 +        }
 +        Thread::​wait(900000);​
 +    }
 +}
  
-  * #include "​stm32f4xx_hal.h" +void venting_thread() 
-  * #include "​usb_device.h" +
-  * #include "​stm32f4xx_hal_adc.h" +    while(true) 
-  * #include "​cs43l22.h" +    { 
-  * #include "​stm32f4_discovery_audio.h"+        uint8_t temp = temperature.readTemperature();​ 
 +        float avgtemp = 0; 
 +         
 +        if(temp > temperature.getVentLimit()) 
 +        { 
 +            for(int i=0; i<6; i++) 
 +            { 
 +                avgtemp += temperature.readTemperature();​ 
 +            } 
 +             
 +            avgtemp /= 6; 
 +             
 +            if(avgtemp > temperature.getVentLimit()) 
 +            { 
 +                ventilation.open(); 
 +                conditioning.Start();​ 
 +                heating.Stop();​ 
 +            } 
 +            avgtemp = 0; 
 +        } 
 +         
 +        else if(temp > temperature.getHighLimit()) 
 +        { 
 +            for(int i=0; i<6; i++) 
 +            { 
 +                avgtemp += temperature.readTemperature();​ 
 +            } 
 +             
 +            avgtemp /= 6; 
 +             
 +            if(avgtemp > temperature.getHighLimit()) 
 +            { 
 +                ventilation.open();​ 
 +                conditioning.Stop();​ 
 +                heating.Stop();​ 
 +            } 
 +            avgtemp = 0; 
 +        } 
 +         
 +        else if(temp < temperature.getLowLimit()) 
 +        { 
 +            for(int i=0; i<6; i++) 
 +            { 
 +                avgtemp += temperature.readTemperature();​ 
 +            } 
 +             
 +            avgtemp /= 6; 
 +             
 +            if(avgtemp < temperature.getLowLimit()) 
 +            { 
 +                ventilation.close();​ 
 +                conditioning.Stop();​ 
 +                heating.Start();​ 
 +            } 
 +            avgtemp = 0; 
 +        } 
 +         
 +        else 
 +        { 
 +            ventilation.close();​ 
 +            conditioning.Stop();​ 
 +            heating.Stop();​ 
 +        } 
 +         
 +        Thread::​wait(100000);​ 
 +    } 
 +}
  
 +void lighting_thread()
 +{
 +    uint16_t lx[6];
 +    for(int i=0; i<6; i++)
 +    {
 +        lx[i] = light.singleMeas();​
 +    }
 +    ​
 +    while(true)
 +    {
 +        uint16_t avglx = 0;
 +        ​
 +        for(int i=0; i<5; i++)
 +        {
 +            lx[i] = lx[i+1];
 +        }
 +        lx[5] = light.singleMeas();​
 +        ​
 +        for(int i=0; i<6; i++)
 +        {
 +            avglx += lx[i];
 +        }
 +        avglx /= 6;
 +        ​
 +        if(avglx<​light.getLimit())
 +        {
 +            illumination.Start();​
 +        }
 +        ​
 +        else
 +        {
 +            illumination.Stop();​
 +        }
 +        ​
 +        Thread::​wait(300000);​
 +    }
 +}
 +</​code>​
 +Tato vlákna mají zajišťovat plnou automatičnost skleníku po jeho prvotním nastavení. Bohužel se díky problémům s různými verzemi systému MBED a jeho knihoven nepodařilo (díky nedostatku času) najednou zprovoznit vlákna a ovládání skleníku přes ethernetové rozhraní. Ačkoliv RTOS sám o sobě funguje, nebylo by možné nastavovat skleník on-line, čímž bychom přišli o jakoukoliv možnost okamžitého zásahu, a zároveň by nebyla splněna nejdůležitější část zadání.
  
-    assert_param(IS_ADC_CHANNEL(sConfig->ADC_CHANNEL_1)); +  * Funkce rozhodovacího stromu pro Ethernetové rozhraní skleníku ​(přímé ovládání) 
-    ​hadc->​Instance->​SMPR2 &= ~ADC_SMPR2(ADC_SMPR2_SMP0sConfig->​Channel); +<code c> 
-    ​hadc->​Instance->​SMPR2 |= ADC_SMPR2(sConfig->​SamplingTimesConfig->​Channel); +int evaluate(char *str) 
-    void ADC_Init(hadc); +
-    ​cs43l22_Init(29OUTPUT_DEVICE_SPEAKER60AUDIO_FREQUENCY_48K);+    switch(sklenikstav
 +    ​
 +        case READY: 
 +            if (strcmp(str"​help"​== 0) 
 +            { 
 +                return 1
 +            } 
 +            else if (strcmp(str"​stav"​== 0) 
 +            { 
 +                return 2
 +            } 
 +            else if (strcmp(str,​ "​set"​) == 0) 
 +            { 
 +            . 
 +            . 
 +            . 
 +             
 +             
 +             
 +             
 +int main(void
 +
 +   . 
 +   . 
 +   . 
 +            int x = evaluate(message); 
 +            if(sklenikstav == READY) 
 +            { 
 +                if (x == 0) 
 +                { 
 +                    client.send_all("​Neplatny prikaz!\r\n"​18); 
 +                    client.send_all(commandwaitsizeof(commandwait));​ 
 +                } 
 +                else if (x == 1) 
 +                { 
 +                    char helpmsg[] = "​stav\t\t-Zobrazeni aktualnich informaci\r\nset\t\t-Nastaveni skleniku\r\nman\t\t-Ovladani skleniku\r\nexit/​quit\t-Konec\r\n";​ 
 +                    client.send_all(helpmsgsizeof(helpmsg)); 
 +                    client.send_all(commandwait,​ sizeof(commandwait));​ 
 +                    number = 0; 
 +   . 
 +   . 
 +   . 
 +
 +</​code>​ 
 +Jak lze vidět, jedna z těchto funkcí se nachází ve funkci main, a druhá je pouze jako pomoc při určování obdržených řetězců. V závislosti na obdrženém textu pak tyto funkce přepínají jak stavy systému (pouze pro potřeby komunikace s uživatelem),​ tak výstupy mikrokontroléru. Tyto funkce jsou obdobou absolvovaného cvičení č.5, pouze značně složitější,​ a proto je tu nebudu uvádět celé. Uvedu pouze stavy systému a jejich akceptované příkazy.
  
 +=== Celý kód ===
  
-Nejprve je inicializován AD převodník a přiřazeným kanálem číslo jedna, který odpovídá převodníku ADC1Na tento převodník je přivedeno napětí z potenciometru,​ který ovládá Volume přehrávání audio výstupu. Následně jsou přiřazeny patřičné parametry a vzorkovací frekvence.+https://os.mbed.com/​users/​civava/​code/​sklenik-2017/​
  
-Dále je inicializován audio kodek, kde je přižen výstupní pin na Jack 3.5 mm, zvolen typ výstupního zařízení,​ počáteční hlasitost v procentech a vrozkovací frekvence na 48 kHz. Stejná je vzorkovací frekvence AD převodníku a rychlost přenosu po USB. 
2017/greenhouse-ctrl.1515968511.txt.gz · Poslední úprava: 2018/01/14 23:21 autor: Josef Křivský