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/15 00:00]
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 41: Řádek 42:
 ====== 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 49: Řádek 66:
  
 Použité knihovny v soubotu main.c Použité knihovny v soubotu main.c
- 
  
   * #include "​mbed.h"​   * #include "​mbed.h"​
Řádek 62: Řádek 78:
 Peripherals.h:​ Peripherals.h:​
 <code c> <code c>
- 
 class Humid class Humid
 { {
Řádek 78: Řádek 93:
 }; };
  
-/** 
- 
-*/ 
 class Temp class Temp
 { {
Řádek 101: Řádek 113:
 }; };
  
-/** 
- 
-*/ 
 class Vent class Vent
 { {
 public: public:
-    Vent(PinName PWM = D8);+    Vent(PinName PWM = D9);
     void open(uint8_t percent = 100);     void open(uint8_t percent = 100);
     void close(void);​     void close(void);​
Řádek 116: Řádek 125:
 }; };
  
-/** 
- 
-*/ 
 class Fan class Fan
 { {
Řádek 129: Řádek 135:
 }; };
  
-/** 
- 
-*/ 
 class Heat class Heat
 { {
Řádek 142: Řádek 145:
 }; };
  
-/** 
- 
-*/ 
 class Water class Water
 { {
Řádek 155: Řádek 155:
 }; };
  
-/** 
- 
-*/ 
 class Light class Light
 { {
Řádek 167: Řádek 164:
     DigitalOut lighting;     DigitalOut lighting;
 }; };
- 
 </​code>​ </​code>​
  
Řádek 173: Řádek 169:
  
 <code c> <code c>
- 
-#include "​mbed.h"​ 
-#include "​Peripherals.h"​ 
- 
- 
 Humid::​Humid(PinName AInp, PinName AOut):​ain(AInp),​dop(AOut) Humid::​Humid(PinName AInp, PinName AOut):​ain(AInp),​dop(AOut)
 { {
Řádek 299: Řádek 290:
 { {
  
-    ​float width = ((6 * percentage)+900);​+    ​uint16_t ​width = ((6 * percentage)+900);​
     ​     ​
     while(width != (6*percent)+900)     while(width != (6*percent)+900)
Řádek 307: Řádek 298:
             width-=1;             width-=1;
             pwm.pulsewidth_us(width);​             pwm.pulsewidth_us(width);​
-            wait_ms(25);+            wait_ms(10);
         }         }
         else         else
Řádek 313: Řádek 304:
             width+=1;             width+=1;
             pwm.pulsewidth_us(width);​             pwm.pulsewidth_us(width);​
-            wait_ms(25);+            wait_ms(10);
         }         }
     }     }
 +    percentage = percent;
 } }
  
 void Vent::​close(void) void Vent::​close(void)
 { {
-    ​for(float width=((6 * percentage)+900);​ width>​900;​ width-=1)  +    ​open(0);
-    { +
-        pwm.pulsewidth_us(width);​ +
-        wait_ms(25); +
-    }+
 } }
  
Řádek 342: Řádek 330:
     fan = 0;     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.
  
-Heat::Heat(PinName DOut):heater(DOut)+ 
 +=== 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
 { {
-    ​heater ​0; +    ​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í.
  
-void Heat::Start(void)+  * 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)
 { {
-    ​heater ​1+    ​while(true) 
-+    { 
-     +        uint8_t hum humidity.readHumidity()
-void Heat::Stop(void+        float avghum = 0; 
-{ +        if(hum < humidity.getLimit()) 
-    ​heater ​= 0;+        { 
 +            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);​ 
 +    }
 } }
  
-Water::​Water(PinName DOut):​sprinkler(DOut)+void venting_thread()
 { {
-    ​sprinkler ​= 0;+    ​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 Water::​Start(void)+void lighting_thread()
 { {
-    ​sprinkler ​1+    ​uint16_t lx[6]; 
-}+    for(int i=0; i<6; i++) 
 +    { 
 +        lx[i] light.singleMeas()
 +    }
     ​     ​
-void Water::Stop(void+    while(true
-+    
-    ​sprinkler ​= 0;+        ​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í.
  
-Light::​Light(PinName DOut):lighting(DOut)+  * Funkce rozhodovacího stromu pro Ethernetové rozhraní skleníku ​(přímé ovládání) 
 +<code c> 
 +int evaluate(char *str)
 { {
-    ​lighting ​= 0; +    ​switch(sklenikstav) 
-+    { 
- +        case READY: 
-void Light::​Start(void)+            if (strcmp(str,​ "​help"​) == 0
 +            { 
 +                return 1
 +            
 +            else if (strcmp(str,​ "​stav"​) == 0) 
 +            { 
 +                return 2; 
 +            } 
 +            else if (strcmp(str,​ "​set"​) == 0) 
 +            { 
 +            . 
 +            . 
 +            . 
 +             
 +             
 +             
 +             
 +int main(void)
 { {
-    lighting ​= 1;+   . 
 +   . 
 +   . 
 +            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; 
 +   . 
 +   . 
 +   .
 } }
-    ​ 
-void Light::​Stop(void) 
-{ 
-    lighting = 0; 
-} 
- 
 </​code>​ </​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.1515970809.txt.gz · Poslední úprava: 2018/01/15 00:00 autor: Josef Křivský