====== Zadání ======
V prostředí Mbed vytvořte program pro vývojovou desku NXP FRDM-K64F, který bude realizovat řízení automatizovaného skleníku a jeho komunikaci s PC pomocí ethernetového rozhraní. Program bude zahrnovat ovládání a řízení periferií včetně návrhu komunikačního rozhraní s příkazy.
----
====== Úvod ======
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 |}}
----
====== Vývojový hardware ======
FRDM-K64F
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
----
====== 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.
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
----
====== Software ======
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) a 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
* #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:
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;
};
Peripherals.cpp:
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;
}
.
.
.
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)
#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;
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í)
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
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í)
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;
.
.
.
}
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/