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:49]
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 13: Řádek 16:
  
 FRDM-K64F FRDM-K64F
- 
- 
  
  
Řádek 36: Řádek 37:
   *Operating characteristics:​   *Operating characteristics:​
      ​*Voltage range: 1.71 to 3.6 V, Flash write voltage range: 1.71 to 3.6 V      ​*Voltage range: 1.71 to 3.6 V, Flash write voltage range: 1.71 to 3.6 V
 +
 ---- ----
  
 ====== Vývoj HW ====== ====== Vývoj HW ======
  
-Požadavkem na celou konstrukci automatizovaného skleníku byla především jednoduchost implementace/​instalace,​ ale také cena celého systému. Vzhledem k velkému možství některých součástek v inventáři zadavatele, byly proto použity především ony. +Požadavkem na celou konstrukci automatizovaného skleníku byla především jednoduchost implementace/​instalace,​ ale také cena celého systému. Vzhledem k velkému možství některých součástek v inventáři zadavatele, byly proto použity především ony. 
 + 
 +Seznam použitých komponetů:​ 
 +  * Mikrokontrolér FRDM-K64F 
 +  * Servo Hitec HS-485HB 
 +  * NTC Termistor Eclipsera 1488979094 
 +  * Arduino modul pro měření intenzity světla BH1750 
 +  * Půdní Vlhkoměr Modul pro Arduino 
 + 
 +Odkazy na stránky výrobců/​prodejců:​ 
 + 
 +  *http://​hitecrcd.com/​products/​servos/​sport-servos/​analog-sport-servos/​hs-485hb-deluxe-hd-ball-bearing-servo/​product 
 +  *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 
 +  *https://​arduino-shop.cz/​arduino/​1574-ntc-termistor-10k-1-3950-1m-vodotesna-sonda-1488979094.html 
 +  *https://​arduino-shop.cz/​arduino/​902-arduino-mereni-intenzity-svetla-1420672425.html 
 +  *https://​arduino-shop.cz/​arduino/​1399-pudni-vlhkomer-modul-pro-arduino-1474354607.html 
 ---- ----
  
Řádek 48: Řádek 66:
  
 Použité knihovny v soubotu main.c Použité knihovny v soubotu main.c
- 
  
   * #include "​mbed.h"​   * #include "​mbed.h"​
Řádek 55: Řádek 72:
   * #include "​BH1750.h"​   * #include "​BH1750.h"​
   * #include "​Peripherals.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);​
 +    }
 +}
 +
 +void venting_thread()
 +{
 +    while(true)
 +    {
 +        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í.
 +
 +  * Funkce rozhodovacího stromu pro Ethernetové rozhraní skleníku (přímé ovládání)
 +<code c>
 +int evaluate(char *str)
 +{
 +    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(commandwait,​ sizeof(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(helpmsg,​ sizeof(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 ===
 +
 +https://​os.mbed.com/​users/​civava/​code/​sklenik-2017/​
  
2017/greenhouse-ctrl.1515970141.txt.gz · Poslední úprava: 2018/01/14 23:49 autor: Josef Křivský