1 — Умный дом на Ардуино. Глава I — Теплый пол. Часть 1 — Простое управление. 5

Задача:Регулирование температуры теплого пола в ванной и на кухне

В наличие имеем электрический мощностью 270Вт. В гофру, залитую стяжкой вместе с теплым полом, устанавливаем водонепроницаемые датчики DS18B20.
Как и условились ранее — соединяем все по схеме, учитывая при этом то, что удаленность контроллера от датчика не должна превышать 10-15 метров, т.к. это повлечет за собой потерю данных при передачи.
shema.png

Прежде чем писать необходимо получить адреса наших датчиков. Схема подключения датчиков.
Воспользуемся примером из библиотеки (Описание библиотеки рассмотрим в другой статье)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include
#include
 
// Data wire is plugged into port 2 on the 
#define ONE_WIRE_BUS 2
#define TEMPERATURE_PRECISION 9
 
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
 
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
 
int numberOfDevices; // Number of temperature devices found
 
DeviceAddress tempDeviceAddress; // We'll use this variable to store a found device address
 
void setup(void)
{
// start serial port
Serial.begin(9600);
Serial.println("Dallas Temperature IC Control Library Demo");
 
// Start up the library
sensors.begin();
 
// Grab a count of devices on the wire
numberOfDevices = sensors.getDeviceCount();
 
// locate devices on the bus
Serial.print("Locating devices...");
 
Serial.print("Found ");
Serial.print(numberOfDevices, DEC);
Serial.println(" devices.");
 
// report parasite power requirements
Serial.print("Parasite power is: ");
if (sensors.isParasitePowerMode()) Serial.println("ON");
else Serial.println("OFF");
 
// Loop through each device, print out address
for(int i=0;i<numberOfDevices; i++)
{
// Search the wire for address
if(sensors.getAddress(tempDeviceAddress, i))
{
Serial.print("Found device ");
Serial.print(i, DEC);
Serial.print(" with address: ");
printAddress(tempDeviceAddress);
Serial.println();
 
Serial.print("Setting resolution to ");
Serial.println(TEMPERATURE_PRECISION, DEC);
 
// set the resolution to TEMPERATURE_PRECISION bit (Each Dallas/Maxim device is capable of several different resolutions)
sensors.setResolution(tempDeviceAddress, TEMPERATURE_PRECISION);
 
Serial.print("Resolution actually set to: ");
Serial.print(sensors.getResolution(tempDeviceAddress), DEC);
Serial.println();
}else{
Serial.print("Found ghost device at ");
Serial.print(i, DEC);
Serial.print(" but could not detect address. Check power and cabling");
}
}
 
}
 
// function to print the temperature for a device
void printTemperature(DeviceAddress deviceAddress)
{
// method 1 - slower
//Serial.print("Temp C: ");
//Serial.print(sensors.getTempC(deviceAddress));
//Serial.print(" Temp F: ");
//Serial.print(sensors.getTempF(deviceAddress)); // Makes a second call to getTempC and then converts to Fahrenheit
 
// method 2 - faster
float tempC = sensors.getTempC(deviceAddress);
Serial.print("Temp C: ");
Serial.print(tempC);
Serial.print(" Temp F: ");
Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
}
 
void loop(void)
{
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print("Requesting temperatures...");
sensors.requestTemperatures(); // Send the command to get temperatures
Serial.println("DONE");
 
// Loop through each device, print out temperature data
for(int i=0;i<numberOfDevices; i++)
{
// Search the wire for address
if(sensors.getAddress(tempDeviceAddress, i))
{
// Output the device ID
Serial.print("Temperature for device: ");
Serial.println(i,DEC);
 
// It responds almost immediately. Let's print out the data
printTemperature(tempDeviceAddress); // Use a simple function to print out the data
}
//else ghost device! Check your power requirements and cabling
 
}
}
 
// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
for (uint8_t i = 0; i < 8; i++)
{
if (deviceAddress[i] < 16) Serial.print("0");
Serial.print(deviceAddress[i], HEX);
}
}
#include
#include

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
#define TEMPERATURE_PRECISION 9

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

int numberOfDevices; // Number of temperature devices found

DeviceAddress tempDeviceAddress; // We'll use this variable to store a found device address

void setup(void)
{
// start serial port
Serial.begin(9600);
Serial.println("Dallas Temperature IC Control Library Demo");

// Start up the library
sensors.begin();

// Grab a count of devices on the wire
numberOfDevices = sensors.getDeviceCount();

// locate devices on the bus
Serial.print("Locating devices...");

Serial.print("Found ");
Serial.print(numberOfDevices, DEC);
Serial.println(" devices.");

// report parasite power requirements
Serial.print("Parasite power is: ");
if (sensors.isParasitePowerMode()) Serial.println("ON");
else Serial.println("OFF");

// Loop through each device, print out address
for(int i=0;i<numberOfDevices; i++)
{
// Search the wire for address
if(sensors.getAddress(tempDeviceAddress, i))
{
Serial.print("Found device ");
Serial.print(i, DEC);
Serial.print(" with address: ");
printAddress(tempDeviceAddress);
Serial.println();

Serial.print("Setting resolution to ");
Serial.println(TEMPERATURE_PRECISION, DEC);

// set the resolution to TEMPERATURE_PRECISION bit (Each Dallas/Maxim device is capable of several different resolutions)
sensors.setResolution(tempDeviceAddress, TEMPERATURE_PRECISION);

Serial.print("Resolution actually set to: ");
Serial.print(sensors.getResolution(tempDeviceAddress), DEC);
Serial.println();
}else{
Serial.print("Found ghost device at ");
Serial.print(i, DEC);
Serial.print(" but could not detect address. Check power and cabling");
}
}

}

// function to print the temperature for a device
void printTemperature(DeviceAddress deviceAddress)
{
// method 1 - slower
//Serial.print("Temp C: ");
//Serial.print(sensors.getTempC(deviceAddress));
//Serial.print(" Temp F: ");
//Serial.print(sensors.getTempF(deviceAddress)); // Makes a second call to getTempC and then converts to Fahrenheit

// method 2 - faster
float tempC = sensors.getTempC(deviceAddress);
Serial.print("Temp C: ");
Serial.print(tempC);
Serial.print(" Temp F: ");
Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
}

void loop(void)
{
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print("Requesting temperatures...");
sensors.requestTemperatures(); // Send the command to get temperatures
Serial.println("DONE");

// Loop through each device, print out temperature data
for(int i=0;i<numberOfDevices; i++)
{
// Search the wire for address
if(sensors.getAddress(tempDeviceAddress, i))
{
// Output the device ID
Serial.print("Temperature for device: ");
Serial.println(i,DEC);

// It responds almost immediately. Let's print out the data
printTemperature(tempDeviceAddress); // Use a simple function to print out the data
}
//else ghost device! Check your power requirements and cabling

}
}

// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
for (uint8_t i = 0; i < 8; i++)
{
if (deviceAddress[i] < 16) Serial.print("0");
Serial.print(deviceAddress[i], HEX);
}
}

В результате мы получим адреса всех датчиков на нашей линии.

Теперь, когда у нас есть адреса всех датчиков , напишем скетч для получения значений температуры пола кухни и ванной.

Несколько переменных для обработки ошибок и передачи данных на Мастер — контроллер

1
2
3
4
//Скетч для 1го контроллера
unsigned long time; //Ожидание сообщения - для передачи данных на Мастер контроллер
unsigned long time_over; //Для переполнения счетчика
byte d2=20; //задержка после включения и до отключения Serial
//Скетч для 1го контроллера
unsigned long time; //Ожидание сообщения - для передачи данных на Мастер контроллер
unsigned long time_over; //Для переполнения счетчика
byte d2=20; //задержка после включения и до отключения Serial

Зададим параметры датчиков DS18B20 для опроса по MAC адресу

1
2
3
4
5
6
7
8
9
//переменные для DS18B20
#include
#include //библиотека DS18B20
#define ONE_WIRE_BUS 9//пин датчиков DS18B20
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress t1 = { 0x28, 0xFD, 0x3B, 0x83, 0x05, 0x00, 0x00, 0xB6 }; //Теплый пол - Датчик в ванной
DeviceAddress t2 = { 0x28, 0xE7, 0x73, 0x82, 0x05, 0x00, 0x00, 0x7A }; //Теплый пол - Датчик на кухне
/*---------------------------------------------------------------------*/
//переменные для DS18B20
#include
#include //библиотека DS18B20
#define ONE_WIRE_BUS 9//пин датчиков DS18B20
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress t1 = { 0x28, 0xFD, 0x3B, 0x83, 0x05, 0x00, 0x00, 0xB6 }; //Теплый пол - Датчик в ванной
DeviceAddress t2 = { 0x28, 0xE7, 0x73, 0x82, 0x05, 0x00, 0x00, 0x7A }; //Теплый пол - Датчик на кухне
/*---------------------------------------------------------------------*/

И еще несколько переменных для управления теплым полом

1
2
3
4
5
6
7
//теплый пол
float temp_kitchen;//температура пола кухни
float temp_bath;//температура пола ванной
byte pin_kitchen=8;//пин управления реле теплого пола кухни
byte pin_bath=7; //пин управления реле теплого пола ванной
unsigned long t_temp_old;//время последнего опроса датчиков
/*---------------------------------------------------------------------*/
//теплый пол
float temp_kitchen;//температура пола кухни
float temp_bath;//температура пола ванной
byte pin_kitchen=8;//пин управления реле теплого пола кухни
byte pin_bath=7; //пин управления реле теплого пола ванной
unsigned long t_temp_old;//время последнего опроса датчиков
/*---------------------------------------------------------------------*/

Настройки при запуске

1
2
3
4
5
6
7
8
9
10
11
12
13
void setup() {
//теплый пол
sensors.begin();
sensors.setResolution(t1, 12);//устанавливаем дискретность температур (9-0.5;10-0.25,11-0.125,12-0)
sensors.setResolution(t2, 12);//устанавливаем дискретность температур (9-0.5;10-0.25,11-0.125,12-0)
pinMode(pin_bath, OUTPUT); //пин управления реле Теплый пол - Датчик в ванной
pinMode(pin_kitchen, OUTPUT); //пин управления реле Теплый пол - Датчик на кухне
digitalWrite(pin_bath, HIGH); //выключаем теплый пол Датчик в ванной(реле управляется минусом)
digitalWrite(pin_kitchen, HIGH); //выключаем теплый пол Датчик на кухне(реле управляется минусом)
/*---------------------------------------------------------------------*/
 
attachInterrupt(0, serialwait, RISING);//прерывание для получение сообщения с главного контроллера для передачи данных
}
void setup() {
//теплый пол
sensors.begin();
sensors.setResolution(t1, 12);//устанавливаем дискретность температур (9-0.5;10-0.25,11-0.125,12-0)
sensors.setResolution(t2, 12);//устанавливаем дискретность температур (9-0.5;10-0.25,11-0.125,12-0)
pinMode(pin_bath, OUTPUT); //пин управления реле Теплый пол - Датчик в ванной
pinMode(pin_kitchen, OUTPUT); //пин управления реле Теплый пол - Датчик на кухне
digitalWrite(pin_bath, HIGH); //выключаем теплый пол Датчик в ванной(реле управляется минусом)
digitalWrite(pin_kitchen, HIGH); //выключаем теплый пол Датчик на кухне(реле управляется минусом)
/*---------------------------------------------------------------------*/

attachInterrupt(0, serialwait, RISING);//прерывание для получение сообщения с главного контроллера для передачи данных
}

Напишем функцию управления реле и зададим в ней параметры температуры, которую хотим поддерживать 26-27.

1
2
3
4
5
6
7
void set_tepliy_pol(float temp, byte pin)
{
if (temp<26) { digitalWrite(pin, LOW);//включить теплый пол }else if (temp>27)
{
digitalWrite(pin, HIGH);//выключить тёплый пол
}
}
void set_tepliy_pol(float temp, byte pin)
{
if (temp<26) { digitalWrite(pin, LOW);//включить теплый пол }else if (temp>27)
{
digitalWrite(pin, HIGH);//выключить тёплый пол
}
}

Реле будет включаться, когда температура падает ниже 26 и выключаться при температуре выше 27

Теперь напишем функцию для опроса датчиков с определенной периодичностью, причем периодичность лучше задавать в минутах, т.к. у пола большая инертность.
Сюда мы будем добавлять в будущем опрос других датчиков DS18B20.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
boolean get_temp_ds(int t)//получаем температуру с периодичностью t(миллисекунды)
{
unsigned long m;
m=get_millis();
if (m-t_temp_old>t)//если период наступил
{
sensors.requestTemperatures();
float temp_kitchen_new,temp_bath_new;
temp_kitchen_new=sensors.getTempC(t2);
temp_bath_new=sensors.getTempC(t1);
if ((temp_kitchen_new>10) & (temp_kitchen_new<50)) temp_kitchen=temp_kitchen_new; if ((temp_bath_new>10) & (temp_bath_new<50)) temp_bath=temp_bath_new;
t_temp_old=m;
return true;
} else return false; //если период не наступил возвращаем false
}
boolean get_temp_ds(int t)//получаем температуру с периодичностью t(миллисекунды)
{
unsigned long m;
m=get_millis();
if (m-t_temp_old>t)//если период наступил
{
sensors.requestTemperatures();
float temp_kitchen_new,temp_bath_new;
temp_kitchen_new=sensors.getTempC(t2);
temp_bath_new=sensors.getTempC(t1);
if ((temp_kitchen_new>10) & (temp_kitchen_new<50)) temp_kitchen=temp_kitchen_new; if ((temp_bath_new>10) & (temp_bath_new<50)) temp_bath=temp_bath_new;
t_temp_old=m;
return true;
} else return false; //если период не наступил возвращаем false
}

Т.к. мы работаем с счетчиками миллисекунд, то напишем функцию для обработки их переполнения.

1
2
3
4
5
6
7
8
9
10
11
unsigned long get_millis()//избавляемся от переполнения
{
unsigned long m;
m=millis();
if(m<time_over)//если произошло переполнение то обнуляем все переменные времени
{
time_over=0;
t_temp_old=0;
}
return m;
}
unsigned long get_millis()//избавляемся от переполнения
{
unsigned long m;
m=millis();
if(m<time_over)//если произошло переполнение то обнуляем все переменные времени
{
time_over=0;
t_temp_old=0;
}
return m;
}

Ну и наконец основной цикл программы

1
2
3
4
5
6
7
8
9
10
11
void loop()
{
if (get_temp_ds(10000)==true)
{
set_tepliy_pol(temp_kitchen,pin_kitchen);//управляем теплым полом кухни
set_tepliy_pol(temp_bath,pin_bath);//управляем теплым полом ванной
}
delay(100);
 
// val_b |=(1<<7);
}
void loop()
{
if (get_temp_ds(10000)==true)
{
set_tepliy_pol(temp_kitchen,pin_kitchen);//управляем теплым полом кухни
set_tepliy_pol(temp_bath,pin_bath);//управляем теплым полом ванной
}
delay(100);

// val_b |=(1<<7);
}

На этом можно было бы и закончить, но мы условились, что будем передавать данные на Мастер контроллер для дальнейшего использования, поэтому ниже две функции для входящего запроса с Мастер-контроллера и отправки данных о температуре на Него

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void serialwait()//по прерыванию получаем сообщение от главного контроллера
{
detachInterrupt(0);
interrupts();
Serial.begin(19200);
time = millis();
while (!Serial.available())
{
if (millis()-time>50) goto exit1;
}
int val;//сообщение с главного контроллера
val=Serial.read();
Serial.end();
 
if (val==0xf1)
{
send_serial(val);
}
exit1:
attachInterrupt(0, serialwait, RISING);
 
}
void serialwait()//по прерыванию получаем сообщение от главного контроллера
{
detachInterrupt(0);
interrupts();
Serial.begin(19200);
time = millis();
while (!Serial.available())
{
if (millis()-time>50) goto exit1;
}
int val;//сообщение с главного контроллера
val=Serial.read();
Serial.end();

if (val==0xf1)
{
send_serial(val);
}
exit1:
attachInterrupt(0, serialwait, RISING);

}
1
2
3
4
5
6
7
8
9
10
11
12
void send_serial(int A)//отправляем данные на Мастер контроллер
{
digitalWrite(13, HIGH);
Serial.begin(19200);
delay(d2);
Serial.write(0xf1);//1
send_temp_float_serial(temp_kitchen);
send_temp_float_serial(temp_bath);
delay(d2);
Serial.end();
digitalWrite(13, LOW);
}
void send_serial(int A)//отправляем данные на Мастер контроллер
{
digitalWrite(13, HIGH);
Serial.begin(19200);
delay(d2);
Serial.write(0xf1);//1
send_temp_float_serial(temp_kitchen);
send_temp_float_serial(temp_bath);
delay(d2);
Serial.end();
digitalWrite(13, LOW);
}

Далее скетч Мастер-контроллера для приема данных и передаче их в Serial.консоль

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
String
temp_kitchen_pol, //температура пола кухни
temp_bath_pol;//температура пола ванной
String Var;
int pin_inter=9;//not 13,12,11,10 //посылаем сигнал прерывание на Slave контроллер
 
void setup() {
Serial.begin(19200);
pinMode(pin_inter, OUTPUT);
 
}
 
void loop() {
synch();
delay(5000);
}
 
void synch(){
 
send_serial(0xf1);
seria_input();
Serial.print(";");
Serial.print(temp_kitchen_pol);
Serial.print(";");
Serial.println(temp_bath_pol);
delay(d_synch);
}
 
void seria_input(){//прием сообщения от Слейв контроллера
int val;
if (Serial.available())
{
val=Serial.read();
if (val==0xf1)//1
{
temp_kitchen_pol=get_temp_float_serial();// получаем температуру
temp_bath_pol=get_temp_float_serial();// получаем температуру
}
 
}
 
}
 
void send_serial(int A)//отправляем запрос Слей контроллеру
{
digitalWrite(pin_inter, HIGH);
delay(20);
Serial.write(A);
delay(100);//ожидаем ответ от контроллера
digitalWrite(pin_inter, LOW);
}
String
temp_kitchen_pol, //температура пола кухни
temp_bath_pol;//температура пола ванной
String Var;
int pin_inter=9;//not 13,12,11,10 //посылаем сигнал прерывание на Slave контроллер

void setup() {
Serial.begin(19200);
pinMode(pin_inter, OUTPUT);

}

void loop() {
synch();
delay(5000);
}

void synch(){

send_serial(0xf1);
seria_input();
Serial.print(";");
Serial.print(temp_kitchen_pol);
Serial.print(";");
Serial.println(temp_bath_pol);
delay(d_synch);
}

void seria_input(){//прием сообщения от Слейв контроллера
int val;
if (Serial.available())
{
val=Serial.read();
if (val==0xf1)//1
{
temp_kitchen_pol=get_temp_float_serial();// получаем температуру
temp_bath_pol=get_temp_float_serial();// получаем температуру
}

}

}

void send_serial(int A)//отправляем запрос Слей контроллеру
{
digitalWrite(pin_inter, HIGH);
delay(20);
Serial.write(A);
delay(100);//ожидаем ответ от контроллера
digitalWrite(pin_inter, LOW);
}

К обоим скетчам добавляем файл с функциями для передачи и приема комнатной температуры в формате float

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void send_temp_float_serial(float temp)//посылаем температуру в формате флоат от 255 до -255 с точностью 0.01
{
if (temp>=0) //передаем знак значения + или -
Serial.write(0x00);
else
Serial.write(0x01);
 
Serial.write((int)temp);//передаем целую часть
Serial.write((int)((temp - (int)temp)*100)); // передаем дробную часть
 
}
 
String get_temp_float_serial()//принимаем температуру в формате флоат
{
String float_s;
int val=0;
val=Serial.read();
if (val==0x01) float_s=float_s+'-';
val=Serial.read();
float_s=float_s+val+'.';
val=Serial.read();
if (val>9) float_s=float_s+val;
else float_s=float_s+'0'+val;
return float_s;
}
void send_temp_float_serial(float temp)//посылаем температуру в формате флоат от 255 до -255 с точностью 0.01
{
if (temp>=0) //передаем знак значения + или -
Serial.write(0x00);
else
Serial.write(0x01);

Serial.write((int)temp);//передаем целую часть
Serial.write((int)((temp - (int)temp)*100)); // передаем дробную часть

}

String get_temp_float_serial()//принимаем температуру в формате флоат
{
String float_s;
int val=0;
val=Serial.read();
if (val==0x01) float_s=float_s+'-';
val=Serial.read();
float_s=float_s+val+'.';
val=Serial.read();
if (val>9) float_s=float_s+val;
else float_s=float_s+'0'+val;
return float_s;
}

Выставляем в скетче регулируемую температуру 31-32. Загружаем оба скетча, включаем консоль и ждем регулирование, пока температура не поднимется до нашего максимума.

Скриншот 2014-11-14 23.29.58.png Кухня 31.png

Как видно из графика для пола кухни, при температуре в комнате 23 градуса, изменение температуры пола тем меньше чем ближе мы приближаемся к нашему максимуму к 31 градусу. Это происходит т.к. мощности теплого пола не хватает поднять температуру выше. Т.е. устанавливается баланс температур.
Проведем еще несколько экспериментов…
Кухня 29-31.png Кухня 29-30.png Ванная 26-27.png

На основе полученных графиков выбираем оптимальную для поддержания температуру, учитывая то, что чем меньше температура регулирования, тем быстрее идет набор заданной температуры и медленнее ее уменьшение, что в свою очередь влечет экономию электроэнергию.

5 комментс для “1 — Умный дом на Ардуино. Глава I — Теплый пол. Часть 1 — Простое управление.

  1. Ответить Vect Ноя 16, 2014 11:11

    Спасибо за статью, ждем продолжения

  2. Ответить Андрей Фев 17, 2015 07:29

    Для чего здесь контроллер ставить , если все можно сделать на реле терморегуляторе. Стать не о чем. Полезность бы была если включать в определенные часы по дням недели.А так для управления теплого пола по температуре ставить контроллер нецелесообразно.

  3. Ответить Алберт Окт 15, 2016 19:41

    Можно поинтересоваться, в какой программе вы строили графики?

Добавить комментарий