Popełniłem sterownik terrarium/orchidarium. Wg. mnie całkiem funkcjonalny i tani. Na chwilę obecną obsługuje dwa czujniki: termometr w obudowie wodoodpornej i termometr/higrometr. Użytkownik ma możliwość wyboru na podstawie którego termometru termostat ma działać. Termostat jest dwukanałowy: steruje ogrzewaniem i oświetleniem. Ale bardzo łatwo go rozbudować o dodatkowe kanały.
Największą zaletą jest to, że sterownik jest wyposażony w interfejs WiFi, który służy mu do pobierania czasu z Internetu, jak również do ustawiania parametrów termostatu. Wyposażony jest również w wyświetlacza LCD 2x16 znaków ale to w zasadzie opcja. Wyświetlacza może wcale nie być albo w jego miejsce można wstawić jakiś ładniejszy graficzny - byleby obsługiwał magistralę I2C.
Załączanie urządzeń odbywa się za pomocą dwukanałowego modułu przekaźnikowego. Maksymalne obciążenie każdego przekaźnika wynosi 250V 10A. Aktualnie jako grzałkę zamierzam wykorzystać podgrzewacz do lusterka samochodowego o mocy 12W. Myślę, że przysypany żwirem będzie w sam raz pod doniczkę albo dwie.
Nie będę się więcej rozwodził, bo to w końcu forum o storczykach, a nie o jakiś kabelkach, ale gdyby kogoś temat interesował to napiszę coś więcej.
Poniżej ceny komponentów:
moduł mikroprocesorowy ESP8266 - 2.4$
lcd 2x16 znaków: 1.9$
dwukanałowy moduł przekaźnikowy - 0.90$
termometr w obudowie wodoodpornej DS18B20 - 1$
czujnik termometr/higrometr - 2.5$
podgrzewacz lusterek 12V 12W - 7 zł.
zasilacz 12V 2.3A - 5 zł.
kilka rezystorów - grosz albo kilka.
Circa about 40 zł.
Ceny podawałem z Aliexpres, gdzie kupowałem. Oczywiście elementy u nas również są dostępne tylko trochę drożej.
Termostat zaprogramowałem w Arduino. Poniżej zamieszczam kod sterownika. W 80% kod składa się z przykładów dostępnym w programie. Nie programuję zawodowo, więc na pewno było to napisać lepiej ale działa
- Kod: Zaznacz cały
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WiFiUdp.h>
#include <TimeLib.h>
#include <Timezone.h>
#include <Ticker.h>
#include <EEPROM.h>
#include <Wire.h>
#include <SimpleDHT.h>
#include <LiquidCrystal_PCF8574.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 13 // ( 12E - D7)
#define DHT22_BUS 16
#define GRZANIE 12 // ( 12E - D6)
#define SWIATLO 14 // ( 12E - D5)
#define GRZANIE_ON digitalWrite(GRZANIE, LOW)
#define GRZANIE_OFF digitalWrite(GRZANIE, HIGH)
#define SWIATLO_ON digitalWrite(SWIATLO, LOW)
#define SWIATLO_OFF digitalWrite(SWIATLO, HIGH)
#define LED 2 //12e - D4,
#define LED_ON digitalWrite(LED, LOW)
#define LED_OFF digitalWrite(LED, HIGH)
LiquidCrystal_PCF8574 lcd(0x27); // set the LCD address to 0x27 for a 16 chars and 2 line display
TimeElements t;
Ticker zegar;
int czasSynchronizowany;
bool czasWyswietlony;
String help = "<p>Akceptowane komendy</p>td - temperatura dzien [0-40]<br>tn - temperatura noc [0-40]<br>hd - godzina DZIEN [0-23]<br>hn - godzina NOC [0-23]<br>hi - histereza [1-10]<br>tt - wybor termmometru [0-1]";
ESP8266WiFiMulti wifiMulti; // Create an instance of the ESP8266WiFiMulti class, called 'wifiMulti'
WiFiUDP UDP; // Create an instance of the WiFiUDP class to send and receive
IPAddress timeServerIP; // time.nist.gov NTP server address
const char* NTPServerName = "0.pl.pool.ntp.org";
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte NTPBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets
SimpleDHT22 dht22;
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
// adresy komorek EEPROM
#define TD_ADDR 0 // temperatura dzien
#define TN_ADDR 1 // temperatura noc
#define H_ADDR 2 // histereza temperaturya termostatu
#define HD_ADDR 3 // godzina DZIEN
#define HN_ADDR 4 // godzina NOC
#define T_ADDR 5 // wybor czujnika termostatu: 0= DHT22, 1=DS10b20
// Create an instance of the server
// specify the port to listen on as an argument
WiFiServer serverWWW(80);
void setup() {
int error;
float temperature = 0;
float humidity = 0;
EEPROM.begin(512);
Serial.begin(115200);
Wire.begin();
Wire.beginTransmission(0x27);
error = Wire.endTransmission();
error ? Serial.println(": LCD not found.") : Serial.println(": LCD found.");
lcd.begin(16, 2); // initialize the lcd
sensors.begin();
sensors.setResolution(12);
if ( (error = dht22.read2(DHT22_BUS, &temperature, &humidity, NULL) ) != SimpleDHTErrSuccess)
Serial.print("Read DHT22 failed, error="); Serial.println(error);delay(2000);
startWiFi(); // Try to connect to some given access points. Then wait for a connection
startUDP();
ustawCzas();
zegar.attach_ms(1000, OneSekInterrupt);
serverWWW.begin();
Serial.println("Server WWW started");
pinMode(GRZANIE, OUTPUT);
pinMode(SWIATLO, OUTPUT);
GRZANIE_OFF;
SWIATLO_OFF;
}
void loop() {
float tempC;
float temperature;
float humidity;
float temperatura[2];
if( ( (t.Second % 5) == 0) && (czasWyswietlony==false) ) {
if (czasSynchronizowany == 0)
ustawCzas();
czasWyswietlony=true;
sensors.requestTemperatures();
tempC = sensors.getTempCByIndex(0);
dht22.read2(DHT22_BUS, &temperature, &humidity, NULL);
temperatura[0]=temperature;
temperatura[1]=tempC;
lcd.setBacklight(255);
lcd.home(); lcd.clear();
lcd.print("T");
lcd.print(temperatura[1]);
if( EEPROM.read(T_ADDR) == 0 )
lcd.print("#");
lcd.setCursor(8, 0);
lcd.print(temperatura[0]);
if( EEPROM.read(T_ADDR) == 1 )
lcd.print("#");
lcd.setCursor(0, 1);
lcd.print("H");
lcd.print(humidity);
lcd.print("%");
lcd.setCursor(8, 1);
if(t.Hour < 10)
lcd.print('0');
lcd.print(t.Hour);
lcd.print(":");
if(t.Minute < 10)
lcd.print('0');
lcd.print(t.Minute);
lcd.print(":");
if(t.Second < 10)
lcd.print('0');
lcd.print(t.Second);
Serial.println("");
Serial.print(t.Hour); Serial.print(":"); Serial.print(t.Minute); Serial.print(":"); Serial.println(t.Second);
Serial.print("Temperatura DHT22: ");
if( EEPROM.read(T_ADDR) == 0 )
Serial.print("#");
Serial.print(temperatura[0]);
Serial.print(" *C ");
Serial.print(humidity); Serial.println(" %RH");
Serial.print("Temperatura DS18b20: ");
if( EEPROM.read(T_ADDR) == 1 )
Serial.print("#");
Serial.print(temperatura[1]);
Serial.println(" *C, ");
Serial.print("\nStan termostatu: GRZANIE: "); digitalRead(GRZANIE) ? Serial.print("OFF") : Serial.print("ON"); Serial.print( " SWIATLO: "); digitalRead(SWIATLO) ? Serial.print("OFF") : Serial.print("ON");
}
if( (t.Second % 5) != 0)
czasWyswietlony=false;
if( (t.Hour >= EEPROM.read(HD_ADDR) ) && (t.Hour < EEPROM.read(HN_ADDR) ) ) { // dzien
SWIATLO_ON;
if( temperatura[EEPROM.read(T_ADDR)] < ( EEPROM.read(TD_ADDR) - EEPROM.read(H_ADDR)) ) {
GRZANIE_ON;
}
if( temperatura[EEPROM.read(T_ADDR)] > ( EEPROM.read(TD_ADDR) + EEPROM.read(H_ADDR)) ) {
GRZANIE_OFF;
}
} else { // noc
SWIATLO_OFF;
if( temperatura[EEPROM.read(T_ADDR)] < ( EEPROM.read(TN_ADDR) - EEPROM.read(H_ADDR)) ) {
GRZANIE_ON;
}
if( temperatura[EEPROM.read(T_ADDR)] > ( EEPROM.read(TN_ADDR) + EEPROM.read(H_ADDR)) ) {
GRZANIE_OFF;
}
}
// Check if a client has connected
WiFiClient client = serverWWW.available();
if (!client) {
return;
}
// Wait until the client sends some data
Serial.println("new client");
while (!client.available()) {
delay(1);
}
// Read the first line of the request
String req = client.readStringUntil('\r');
Serial.println(req);
client.flush();
String temp;
int pos;
int tempInt;
bool pomoc;
if ( ( pos = req.indexOf("td") ) != -1){
temp=req.substring(pos+2);
tempInt = temp.toInt();
if ( (tempInt>=0) && (tempInt<=40) )
EEPROM.write(TD_ADDR, tempInt);
EEPROM.commit();
}
if ( ( pos = req.indexOf("tn") ) != -1){
temp=req.substring(pos+2);
tempInt = temp.toInt();
if ( (tempInt>=0) && (tempInt<=40) )
EEPROM.write(TN_ADDR, tempInt);
EEPROM.commit();
}
if ( ( pos = req.indexOf("hd") ) != -1){
temp=req.substring(pos+2);
tempInt = temp.toInt();
if ( (tempInt>=0) && (tempInt<24) )
EEPROM.write(HD_ADDR, tempInt);
EEPROM.commit();
}
if ( ( pos = req.indexOf("hn") ) != -1){
temp=req.substring(pos+2);
tempInt = temp.toInt();
if ( (tempInt>=0) && (tempInt<24) )
EEPROM.write(HN_ADDR, tempInt);
EEPROM.commit();
}
if ( ( pos = req.indexOf("hi") ) != -1){
temp=req.substring(pos+2);
tempInt = temp.toInt();
if ( (tempInt>0) && (tempInt<=10) )
EEPROM.write(H_ADDR, tempInt);
EEPROM.commit();
}
if ( ( pos = req.indexOf("tt") ) != -1){
temp=req.substring(pos+2);
tempInt = temp.toInt();
if (tempInt==0) // DHT
EEPROM.write(T_ADDR, 0);
else
EEPROM.write(T_ADDR, 1);
EEPROM.commit();
}
if ( ( pos = req.indexOf("help") ) != -1){
pomoc=true;
}
Serial.print("Temparatura termostatu DZIEN = "); Serial.println(EEPROM.read(TD_ADDR));
Serial.print("Temparatura termostatu NOC = "); Serial.println(EEPROM.read(TN_ADDR));
Serial.print("Godziny DZIEN: "); Serial.print(EEPROM.read(HD_ADDR)); Serial.print("-"); Serial.println(EEPROM.read(HN_ADDR));
Serial.print("Histereza termostatu = "); Serial.println(EEPROM.read(H_ADDR));
Serial.print("Termostat sterowany przez czujnik "); (EEPROM.read(T_ADDR) == 1)?Serial.println("DS18B20"):Serial.println("DHT");
Serial.print("\nStan termostatu: GRZANIE: "); digitalRead(GRZANIE) ? Serial.print("OFF") : Serial.print("ON"); Serial.print( " SWIATLO: "); digitalRead(SWIATLO) ? Serial.print("OFF") : Serial.print("ON");
client.flush();
// Prepare the response
String s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\n";
s += "<p>";
s += "<br>Temperatura DHT=";
if( EEPROM.read(T_ADDR) == 0 )
s += "<b>";
s += temperatura[0];
s += " oC";
if( EEPROM.read(T_ADDR) == 0 )
s += "</b>";
s += "<br>Wilgotnosc DHT=";
s += humidity;
s += " %RH";
s += "<br>Temperatura DS18b20=";
if( EEPROM.read(T_ADDR) == 1 )
s += "<b>";
s += temperatura[1];
s += " oC";
if( EEPROM.read(T_ADDR) == 1 )
s += "</b>";
s += "</p>";
s += "<p>";
s += "Temperatura zadana:";
s += "<br>DZIEN= ";
s += EEPROM.read(TD_ADDR);
s += "oC<br>NOC= ";
s += EEPROM.read(TN_ADDR);
s += "oC";
s += "<br>Godziny DZIEN: ";
s += EEPROM.read(HD_ADDR);
s += "-";
s += EEPROM.read(HN_ADDR);
s += "<br>Histereza termostatu= ";
s += EEPROM.read(H_ADDR);
s += "</p>";
s += "<p>";
s += "Stan termostatu: GRZANIE: ";
s += digitalRead(GRZANIE) ? "OFF" : "ON";
s += " SWIATLO: ";
s += digitalRead(SWIATLO) ? "OFF" : "ON";
s += "</p>";
s += "</html>\n";
// Send the response to the client
client.print(s);
if (pomoc == true)
client.print(help);
delay(1);
Serial.println("Client disonnected");
}
void OneSekInterrupt(void)
{
if (++t.Second>=60){
t.Second=0;
if (++t.Minute>=60){
t.Minute=0;
if (++t.Hour>=24) {
t.Hour=0;
czasSynchronizowany=0;
}
}
}
}
void ustawCzas()
{
//Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; //Central European Summer Time
TimeChangeRule CET = {"CET", Last, Sun, Oct, 3, 60}; //Central European Standard Time
Timezone CE(CEST, CET);
if(!WiFi.hostByName(NTPServerName, timeServerIP)) { // Get the IP address of the NTP server
Serial.println("DNS lookup failed. Rebooting.");
Serial.flush();
ESP.reset();
}
Serial.print("Time server IP:\t");
Serial.println(timeServerIP);
Serial.println("\r\nSending NTP request ...");
sendNTPpacket(timeServerIP);
int cb;
if( (cb=UDP.parsePacket())>0) {
Serial.print("\nReceived "); Serial.print(cb); Serial.println(" bytes");
UDP.read(NTPBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
//the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, esxtract the two words:
unsigned long highWord = word(NTPBuffer[40], NTPBuffer[41]);
unsigned long lowWord = word(NTPBuffer[42], NTPBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
Serial.print("Seconds since Jan 1 1900 = " );
Serial.println(secsSince1900);
// now convert NTP time into everyday time:
// Serial.print("Unix time = ");
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
unsigned long epoch = secsSince1900 - seventyYears;
// print Unix time:
// Serial.println(epoch);
TimeChangeRule *tcr;
time_t utc;
utc = epoch;
printTime(utc, "UTC", "Universal Coordinated Time");
printTime(CE.toLocal(utc, &tcr), tcr -> abbrev, "Warsaw");
Serial.print("H: "); Serial.print(hour(CE.toLocal(utc, &tcr)));
Serial.print(" M: "); Serial.print(minute(utc));
Serial.print(" S: "); Serial.println(second(utc));
zegar.detach();
t.Hour = hour(CE.toLocal(utc, &tcr));
t.Minute = minute(utc);
t.Second = second(utc);
zegar.attach_ms(1000, OneSekInterrupt);
czasSynchronizowany = hour(CE.toLocal(utc, &tcr))+1;
}
}
void startWiFi() { // Try to connect to some given access points. Then wait for a connection
wifiMulti.addAP("***", "***"); // add Wi-Fi networks you want to connect to
wifiMulti.addAP("***", "***");
wifiMulti.addAP("android");
Serial.println("Connecting");
while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect
delay(250);
Serial.print('.');
}
Serial.println("\r\n");
Serial.print("Connected to ");
Serial.print(WiFi.SSID()); // Tell us what network we're connected to
Serial.print(" (");
Serial.print(WiFi.RSSI());
Serial.println(")");
Serial.print("IP address:\t");
Serial.print(WiFi.localIP()); // Send the IP address of the ESP8266 to the computer
Serial.println("\r\n");
lcd.setBacklight(255);
lcd.home(); lcd.clear();
lcd.print(WiFi.SSID());
lcd.print(WiFi.RSSI());
lcd.setCursor(0, 1);
lcd.print(WiFi.localIP());
delay(3000);
}
void startUDP() {
Serial.println("Starting UDP");
UDP.begin(123); // Start listening for UDP messages on port 123
Serial.print("Local port:\t");
Serial.println(UDP.localPort());
Serial.println();
}
void sendNTPpacket(IPAddress& address) {
memset(NTPBuffer, 0, NTP_PACKET_SIZE); // set all bytes in the buffer to 0
// Initialize values needed to form NTP request
NTPBuffer[0] = 0b11100011; // LI, Version, Mode
// send a packet requesting a timestamp:
UDP.beginPacket(address, 123); // NTP requests are to port 123
UDP.write(NTPBuffer, NTP_PACKET_SIZE);
UDP.endPacket();
}
//Function to print time with time zone
void printTime(time_t t, char *tz, char *loc)
{
sPrintI00(hour(t));
sPrintDigits(minute(t));
sPrintDigits(second(t));
Serial.print(' ');
Serial.print(dayShortStr(weekday(t)));
Serial.print(' ');
sPrintI00(day(t));
Serial.print(' ');
Serial.print(monthShortStr(month(t)));
Serial.print(' ');
Serial.print(year(t));
Serial.print(' ');
Serial.print(tz);
Serial.print(' ');
Serial.print(loc);
Serial.println();
}
//Print an integer in "00" format (with leading zero).
//Input value assumed to be between 0 and 99.
void sPrintI00(int val)
{
if (val < 10) Serial.print('0');
Serial.print(val, DEC);
return;
}
//Print an integer in ":00" format (with leading zero).
//Input value assumed to be between 0 and 99.
void sPrintDigits(int val)
{
Serial.print(':');
if (val < 10) Serial.print('0');
Serial.print(val, DEC);
}