Po pewnym czasie eksploatacji oraz kilku poprawkach baboli, kod uzyskał już działającą postać.
Kod: Zaznacz cały
//#define ILI9341_BLACK 0x0000 /* 0, 0, 0 */
//#define ILI9341_NAVY 0x000F /* 0, 0, 128 */
//#define ILI9341_DARKGREEN 0x03E0 /* 0, 128, 0 */
//#define ILI9341_DARKCYAN 0x03EF /* 0, 128, 128 */
//#define ILI9341_MAROON 0x7800 /* 128, 0, 0 */
//#define ILI9341_PURPLE 0x780F /* 128, 0, 128 */
//#define ILI9341_OLIVE 0x7BE0 /* 128, 128, 0 */
//#define ILI9341_LIGHTGREY 0xC618 /* 192, 192, 192 */
//#define ILI9341_GREY 0x7BEF
//#define ILI9341_DARKGREY 0x7BEF /* 128, 128, 128 */
//#define ILI9341_BLUE 0x001F /* 0, 0, 255 */
//#define ILI9341_GREEN 0x07E0 /* 0, 255, 0 */
//#define ILI9341_CYAN 0x07FF /* 0, 255, 255 */
//#define ILI9341_RED 0xF800 /* 255, 0, 0 */
//#define ILI9341_MAGENTA 0xF81F /* 255, 0, 255 */
//#define ILI9341_YELLOW 0xFFE0 /* 255, 255, 0 */
//#define ILI9341_WHITE 0xFFFF /* 255, 255, 255 */
//#define ILI9341_ORANGE 0xFD20 /* 255, 165, 0 */
//#define ILI9341_GREENYELLOW 0xAFE5 /* 173, 255, 47 */
//#define ILI9341_PINK 0xF81F
#include <Wire.h>
#include <EEPROM.h>
#include <SHT2x.h>
#include <DS3231.h>
#include "SPI.h"
#include "Adafruit_GFX_AS.h"
#include "Adafruit_ILI9341_AS.h"
DS3231 clock;
RTCDateTime dt;
#define sclk 13 // Nie zmieniać
#define miso 12
#define mosi 11 // Nie zmieniać
#define cs 10
#define dc 9
#define rst 8 // można podłączyć do resetu arduino
#define adrdzien 0 //adres eepromu z zawartością godziny rozpoczęcia dnia: 0 > hh, 1 > mm
#define adrnoc 2 //adres nocy 2 > hh, 3 > mm
#define adrtempdzien 4 //adres temperatury w dzień: 4 starszy bajt, 5 młodszy bajt
#define adrtempnoc 6 //adres temperatury w nocy
#define grzalka 7 //wyjście grzałki
#define UP 2 // przycisk UP na Pin 3
#define OK 3 // przycisk OK na pinie 4
#define DOWN 4 // przycisk DOWN na pinie 5
#define PWMLED 5 //wyjście na sterowanie oświetleniem PWM
#define zarowka 6 //wyjście na żarówkę
Adafruit_ILI9341_AS tft = Adafruit_ILI9341_AS(cs, dc, rst); //Deklaracja biblioteki wyświetlacza
unsigned long start = micros(); //licznik czasu pętli
float temperature = 0; //temperatura zmierzona
word temperatura = 0; //przekonwertowana temeratura ze zmiennoprzecinkowej na całkowitą, float x 100
word termostat = 5; //temperatura aktualna termostatu, po zmianie dąży do docelowej co 1
word termostata = 0; //temperatura docelowa dnia lub nocy
byte hhdzien = 8; //godzina rozpoczęcia dnia
byte mmdzien = 0; //minuta rozpoczęcia dnia
byte hhnoc = 20; //godzina rozpoczęcia końca dnia
byte mmnoc = 0; //minuta rozpoczecia nocy
byte poradoby = 0; //pora doby dzień = 1, rano lub wieczór =2, noc = 3
word licznik = 0; //liczenie czasu w pętli pomiarów
byte licznikLED = 0; //liczenie czasu rozjaśniania LEDów
byte liczniktemp = 0; //licznik czasu zmiany temperatury
word tsr = 0; //zmienna zawierająca sumę 10 pomiarów
byte oldss = 0; //poprzednie sekundy
byte LEDDN = 0; //jasność docelowa LED
byte LED = 0; //jasność światła LED
int ep; //uchyb poprzedni
int en; //uchyb następny
int U; //sygnał sterujący
int C; //część całkująca
byte menu = 0;
byte submenu = 0;
void setup() {
licznik = 500; //500 = 5 minut
licznikLED = 25; //25 = 15s
liczniktemp = 100; //200 = 1 minuty
Wire.begin();
clock.begin();
Serial.begin(9600);
pinMode(grzalka, OUTPUT); //grzałka
pinMode(PWMLED, OUTPUT); //wyjście LED
pinMode(zarowka, OUTPUT); //wyjście na sterowanie oświetleniem klasycznym
pinMode(DOWN,INPUT_PULLUP); // konfiguracja pinów dla przycisków
pinMode(UP,INPUT_PULLUP); // konfiguracja pinów dla przycisków
pinMode(OK,INPUT_PULLUP); // konfiguracja pinów dla przycisków
tft.init();
tft.setRotation(3);
tft.fillScreen(ILI9341_BLACK);
tft.setTextColor(ILI9341_YELLOW, ILI9341_BLACK);
tft.setTextSize(1);
tft.drawString("Termostat - akwarium",40,0,4);
tft.setTextColor(ILI9341_GREEN);
tft.drawString("Temp. Wilg.",40,30,4);
tft.drawString("Czas",20,170,4);
tft.drawString("Menu",20,210,4);
tft.setTextSize(2);
tft.setTextColor(ILI9341_BLUE, ILI9341_BLACK);
tft.drawString("|",280,60,4);
tft.drawString("*",125,60,4); //st. C
pomiartemp();
tsr = temperature * 900;
temperature = temperature *10;
termostat = temperature; //przyjęcie zmierzonej temperatury, jako aktualną termostatu by dążyć do docelowej
termostat = termostat *10;
termostat = termostat + 5; //zaokrąglenie ostatniej cyfry do 5
//EEPROM.update(adrdzien,hhdzien);
//EEPROM.update((adrdzien+1),mmdzien);
//EEPROM.update(adrnoc,hhnoc);
//EEPROM.update((adrnoc+1),mmnoc);
// byte pomocnicza = 0;
// pomocnicza = termostat;
//EEPROM.update((adrtempdzien +1),pomocnicza);
//pomocnicza = termostat>>8;
//EEPROM.update(adrtempdzien,pomocnicza);
// pomocnicza = termostat;
//EEPROM.update((adrtempnoc +1),pomocnicza);
//pomocnicza = termostat>>8;
//EEPROM.update(adrtempnoc,pomocnicza);
//clock.setDateTime(__DATE__, __TIME__); //ustaw czas pobrany z serwaera
// Manual (Year, Month, Day, Hour, Minute, Second) //ustawienia zegara
//clock.setDateTime(2014, 4, 25, 0, 0, 0);
//termostata = (odczyteeprom(adrtempdzien)<<8) + odczyteeprom (adrtempdzien +1);
hhdzien = odczyteeprom(adrdzien);
mmdzien = odczyteeprom(adrdzien + 1);
hhnoc = odczyteeprom(adrnoc);
mmnoc = odczyteeprom(adrnoc + 1);
}
void loop(void) {
if(digitalRead(OK)==LOW){delay(40); if(digitalRead(OK)==LOW){delay(200); if (menu == 0) menu = 1;}} // jeśli OK
ustaw();
tft.setTextSize(2);
// tft.setTextColor(ILI9341_BLUE, ILI9341_BLACK);
word dzien;
word noc;
word czas;
byte nowadoba = 0;
dt = clock.getDateTime(); //pobranie czasu z zegara
byte hh = dt.hour;
byte mm = dt.minute;
byte ss = dt.second;
czas = (hh << 8) + mm; //przeliczanie godzin i minut na jedną liczbę
dzien = (hhdzien << 8) + mmdzien;
noc = (hhnoc << 8) + mmnoc;
if (czas >= dzien && czas <= noc) { //rozpoznanie pory doby dla wyświetlenia ikony
nowadoba = 1;
termostata = (odczyteeprom(adrtempdzien)<<8) + odczyteeprom (adrtempdzien +1);
LEDDN = 255;
}
else {
nowadoba = 3;
termostata = (odczyteeprom(adrtempnoc)<<8) + odczyteeprom (adrtempnoc +1);
LEDDN = 2;
}
if (termostat != termostata) nowadoba = 2; //Jeśli temperatura aktualna i docelowa są różne to jest wieczór lub poranek
if (nowadoba != poradoby){ //Jeśli nastąpiła zmiana pory doby to wyświetl nowy symbol
poradnia(nowadoba);
poradoby = nowadoba;
}
if (oldss != ss){ //Nowa sekunda to wyświetl czas
oldss = ss;
zegar(hh,mm,ss);
}
if (liczniktemp == 200){ //cykl zmian temperatury 0,1C na 2 minuty
liczniktemp = 0;
if (termostata > termostat) termostat = termostat + 10;
if (termostata < termostat) termostat = termostat - 10;
}
if (licznik == 500){ //cykl grzania 5 minut
licznik = 0;
pomiartemp();
grzanie(); //Termostat obliczanie PID
}
if (licznikLED == 25){ //cykl zmiany jasności 15s
licznikLED = 0;
if (LED < LEDDN) {LED++; if (LED > 127) LED = LEDDN;} //rozjaśnianie LED
if (LED > LEDDN) {LED--; if (LED > 129) LED = 129;} //ściemnianie LED
if (LED == 255) digitalWrite(zarowka,HIGH); //załączenie żarówki
if (nowadoba == 3)digitalWrite(zarowka,LOW); //wyłaczenie żarówki
analogWrite(PWMLED,LED); //ustawienie jasności LED
pomiartemp();
temp(mm,ss); //wyświetlanie temperatury i wilgotności
}
if (licznik < U) { //Obsługa grzałki
tft.setTextColor(ILI9341_RED, ILI9341_BLACK);
tft.drawString(")",70,110,4); //grzałka włączona
digitalWrite(grzalka,HIGH);
}
else {
tft.setTextColor(ILI9341_BLUE, ILI9341_BLACK);
tft.drawString(")",70,110,4); //grzałka wyłączona
digitalWrite(grzalka,LOW);
}
if ((millis() - start) > 600){ //600 to 0,6s
licznik ++;
licznikLED++;
liczniktemp++;
start = millis();
}
}
void nastawaczasu(byte adres, byte zakres){
byte dane;
byte godz;
byte minuty;
byte nowe = 255;
unsigned long czas;
czas = millis();
godz = odczyteeprom(adres);
minuty = odczyteeprom(adres + 1);
while (true){
byte xpos = 180;
byte ypos = 210;
if (zakres == 23) {dane = godz;}
else {dane = minuty;}
tft.setTextSize(1);
tft.setTextColor(ILI9341_GREEN,ILI9341_BLACK);
if (nowe != dane){
if (godz < 10) xpos+= tft.drawChar('0',xpos,ypos,4);
xpos+= tft.drawNumber(godz,xpos,ypos,4);
xpos+= tft.drawChar(':',xpos,ypos,4);
if (minuty<10) xpos+= tft.drawChar('0',xpos,ypos,4);
xpos+=tft.drawNumber(minuty,xpos,ypos,4);
nowe = dane;
}
if(digitalRead(UP)==LOW){delay(40);if(digitalRead(UP)==LOW) {delay(200); dane++; czas = millis(); if (dane > zakres) {dane = 0;}}} // jeśli UP
if(digitalRead(DOWN)==LOW){delay(40);if(digitalRead(DOWN)==LOW) {delay(200); czas = millis(); if (dane == 0) {dane = zakres + 1;}dane--;}} // jeśli DOWN
if(digitalRead(OK)==LOW){delay(40); if(digitalRead(OK)==LOW){delay(200); zapiszczas(adres,godz,minuty); break;}} // wejście submenu
if (zakres == 23) {godz = dane;}
else {minuty = dane;}
if (millis() - czas > 10000) {tft.drawString(" ",180,210,4); submenu = 0; break;}
dt = clock.getDateTime(); //pobranie czasu z zegara
byte hh = dt.hour;
byte mm = dt.minute;
byte ss = dt.second;
if (oldss != ss){
oldss = ss;
zegar(hh,mm,ss);
}
}
}
void zapiszczas(byte adres, byte godz, byte minuty){
tft.drawString("OK ",180,210,4);
EEPROM.update(adres,godz);
EEPROM.update((adres+1),minuty);
if (adres == adrdzien){hhdzien = godz; mmdzien = minuty;}
else {hhnoc = godz; mmnoc = minuty;}
delay(1000);
tft.drawString(" ",180,210,4);
submenu = 0;
}
void nastawatemp(byte adres){
word dane;
word nowe = 0;
float temp = 0;
unsigned long czas;
czas = millis();
dane = (odczyteeprom(adres)<<8) + odczyteeprom (adres +1); //odczyt temperatury do ustawienia
// dane = 2555;
while (true){
temp = dane;
temp = temp / 100; //przeliczenie temperatury na liczbę zmiennoprzecinkową do wyświetlenia
tft.setTextSize(1);
tft.setTextColor(ILI9341_GREEN,ILI9341_BLACK);
if (nowe != dane) {tft.drawFloat(temp,2,180,210,4); tft.drawString("*",250,210,4); nowe = dane;}
if(digitalRead(UP)==LOW){delay(40);if(digitalRead(UP)==LOW) {delay(200); if (dane < 3495) dane = dane + 10;czas = millis();}} // jeśli UP
if(digitalRead(DOWN)==LOW){delay(40);if(digitalRead(DOWN)==LOW) {delay(200); if (dane > 1815) dane = dane - 10;czas = millis();}} // jeśli DOWN
if(digitalRead(OK)==LOW){delay(40); if(digitalRead(OK)==LOW){delay(200);zapisztemp(adres,dane); break;}} // wejście submenu
if (millis() - czas > 10000) {tft.drawString(" ",180,210,4); submenu = 0; break;}
dt = clock.getDateTime(); //pobranie czasu z zegara
byte hh = dt.hour;
byte mm = dt.minute;
byte ss = dt.second;
if (oldss != ss){
oldss = ss;
zegar(hh,mm,ss);
}
}
}
void zapisztemp(byte adres, word dane){
tft.drawString("OK ",180,210,4);
byte pomocnicza = 0;
pomocnicza = dane;
EEPROM.update((adres +1),pomocnicza);
pomocnicza = dane>>8;
EEPROM.update(adres,pomocnicza);
delay(1000);
tft.drawString(" ",180,210,4);
submenu = 0;
}
void ustaw(){
tft.setTextSize(1);
tft.setTextColor(ILI9341_GREEN,ILI9341_BLACK);
if (menu >0){
if(digitalRead(UP)==LOW){delay(40);if(digitalRead(UP)==LOW) {delay(200); menu++; submenu = 0; if (menu > 7) menu = 1;}} // jeśli UP
if(digitalRead(DOWN)==LOW){delay(40);if(digitalRead(DOWN)==LOW) {delay(200); menu--; submenu = 0; if (menu < 1) menu = 7;}} // jeśli DOWN
if(digitalRead(OK)==LOW){delay(40); if(digitalRead(OK)==LOW){delay(200);if (submenu == 1) {submenu = 2;}}} // wejście submenu
}
switch(menu){
case 0:
break;
case 1:
if (submenu == 0) {tft.drawString("<<< ",100,210,4); submenu = 1;}
if(digitalRead(OK)==LOW){delay(40); if(digitalRead(OK)==LOW){menu = 0; submenu = 0; tft.drawString(" ",100,210,4);delay(200); }} // Powrót z menu
break;
case 2:
if (submenu == 0) {tft.drawString("D-h ",100,210,4); submenu = 1;}
if(submenu == 2){delay(200);nastawaczasu(adrdzien, 23);} // wejście submenu
break;
case 3:
if (submenu == 0) {tft.drawString("D-m ",100,210,4); submenu = 1;}
if(submenu == 2){delay(200);nastawaczasu(adrdzien, 59);} // wejście submenu
break;
case 4:
if (submenu == 0) {tft.drawString("N-h ",100,210,4); submenu = 1;}
if(submenu == 2){delay(200);nastawaczasu(adrnoc, 23);} // wejście submenu
break;
case 5:
if (submenu == 0) {tft.drawString("N-m ",100,210,4); submenu = 1;}
if(submenu == 2){delay(200);nastawaczasu(adrnoc, 59);} // wejście submenu
break;
case 6:
if (submenu == 0) {tft.drawString("TeD ",100,210,4); submenu = 1;}
if(submenu == 2){delay(200);nastawatemp(adrtempdzien);} // wejście submenu
break;
case 7:
if (submenu == 0) {tft.drawString("TeN ",100,210,4); submenu = 1;}
if(submenu == 2){delay(200);nastawatemp(adrtempnoc);} // wejście submenu
break;
}
tft.drawString(" ",170,210,4);
}
void poradnia(byte symbol){
tft.drawString(" ",200,110,4);
switch (symbol){
case 1: //słońce
tft.setTextColor(ILI9341_YELLOW, ILI9341_BLACK);
tft.drawString("!",190,110,4); //pół słońca - ' 39; słońce - ! 33; księżyc - ( 40
break;
case 2: //pół słońca
tft.setTextColor(ILI9341_YELLOW, ILI9341_BLACK);
tft.drawString("'",190,110,4);
break;
case 3: //księżyc
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.drawString("(",190,110,4);
break;
}
}
byte odczyteeprom(byte adres){
return EEPROM.read(adres);
}
void zapiseeprom(byte adres,byte dane){
EEPROM.update(adres,dane);
}
void pomiartemp(){
temperature = SHT2x.GetTemperature();
temperatura = temperature * 100;
}
void temp(byte mm, byte ss){
tsr = tsr + temperatura;
temperatura = tsr /10;
tsr = tsr - temperatura;
temperature = temperatura;
temperature = temperature / 100;
tft.setTextSize(2);
tft.setTextColor(ILI9341_BLUE, ILI9341_BLACK);
tft.drawFloat(temperature,1,20,60,4);
tft.drawFloat(SHT2x.GetHumidity(),0,220,60,4);
}
void grzanie(){ //procedura termostatu
byte dt = 30; //co ile pobiera się próbkę
byte Kp = 20; //wzmocnienie
byte Ti = 30; //stała całkowania
byte Td = 40; //stała różniczkowania
en = (termostat - temperatura);
C += ((ep + en)/2)*dt;
U = Kp*(en + (1/Ti)*C + Td*(en - ep)/dt);
ep = en;
if (U < 0) U = 0;
if (U > 500) U = 500;
Serial.print(" LED ");
Serial.print(LED);
Serial.print(" termostat " );
Serial.print(termostat);
Serial.print(" temperatura " );
Serial.print(temperatura);
Serial.print(" temp = ");
Serial.print(temperature);
Serial.print(" en = ");
Serial.print(en);
Serial.print(" ep = ");
Serial.print(ep);
Serial.print(" C = ");
Serial.print(C);
Serial.print(" U =" );
Serial.println(U);
}
void zegar(byte hh, byte mm, byte ss){
byte xpos = 85;
byte ypos = 155;
tft.setTextSize(2);
tft.setTextColor(ILI9341_BLUE, ILI9341_BLACK);
if (hh<10) xpos+= tft.drawChar('0',xpos,ypos,4);
xpos+= tft.drawNumber(hh,xpos,ypos,4);
xpos+= tft.drawChar(':',xpos,ypos,4);
if (mm<10) xpos+= tft.drawChar('0',xpos,ypos,4);
xpos+=tft.drawNumber(mm,xpos,ypos,4);
xpos+= tft.drawChar(':',xpos,ypos,4);
if (ss<10) xpos+= tft.drawChar('0',xpos,ypos,4);
tft.drawNumber(ss,xpos,ypos,4);
}
Tutaj należy "odkomentować" wszystkie linie, a następnie przy deklaracji zmiennych, wpisać wartości starowe. Po zapisaniu w eepromie, trzeba przywrócić pierwotny stan programu, gdyż wszystkie dane już będą zapamiętane.
//clock.setDateTime(__DATE__, __TIME__); //ustaw czas pobrany z serwaera
Ustawia czas ściągnięty z komputera. Niestety, dostajemy wtedy opóźnienie zegara o czas wgrywania programu.
// Manual (Year, Month, Day, Hour, Minute, Second) //ustawienia zegara
Natomiast ten fragment, umożliwia ręczne ustawienie czasu, te dwa sposoby, należy traktować alternatywnie.
Nie zrobiłem ustawiania czasu, gdyż wyszedłem z założenia, że zwierząt nie obowiązują zmiany zima/lato, a niedokładność zegara wynosząca kilka minut w skali roku, jest w tym zstaosowaniu do pominięcia. Jeśli ktoś chce mieć możliwość ręcznego ustawienia czasu, może sobie dopisać odpowiedni fragment w menu.
Mała poprawka dostyczy też układu regulacji jasności taśmy LED, rezystor R2 ma teraz wartość 2k2, co dało zakłądany zakres regulacji jasności.