Проект 2. Отопление на Arduino
Если допустим вам нужно автоматически отапливать гараж, или дачу, или домик в деревне у бабушки, вам совершенно ни к чему покупать супер-пупер дорогостоящие контроллеры со своими мегафункционалами. Управление отоплением Ардуино справляется на все сто. Только нужно грамотно и правильно написать скетч, да и к тому же вы имеете возможность настраивать систему под себя. А если обращаетесь к программированию на вы, то можно попросить опытного и профессионального программиста написать софт.
Из этой статьи вы узнаете:
Приветствую друзья, с вами автор блога, Гридин Семён. В просторах интернета я наткнулся на замечательный проект по отоплению на Arduino. Сразу хочу поставить все точки над i. Статья не моя, и проект не мой, даже картинки не мои. Это произведение автора под ником Vanalaizer, первоисточник размещён на Geektimes.
К сожалению, связаться мне с ним не удалось. Этот пост я пишу в основном, чтобы зафиксировать на сайте и закрепить для себя и для вас основные аспекты проекта.
Ну а вам решать, какой текст вам удобнее всего читать — мой или уважаемого Vanalaizer'a. А ему кстати говоря отдельное спасибо за проект. Так что не бросайтесь на меня тухлыми помидорами=)). Я не специально.
Схема отопления
Основной принцип данного проекта — это процесс регулирования электрокотла с помощью самой простой и приземлённой платой Arduino UNO.
Вот непосредственно сам шкаф управления, «мозги» на основе Arduino.
Это как раз делалось для дома в деревне. Регулирование температуры ведётся в одной комнате.
Какое же само оборудование? Я не буду расписывать большие тексты. Напишу конкретно. Для системы требуется следующее.
- Электрокотёл на 4-6 кВт
- рециркуляционный насос
Сама конструкция котла состоит и металлического цилиндра с тремя нагревательными ТЭНами по 2 кВт. Отсюда следует вывод, что система должна быть трёхфазная на каждой фазе по ТЭНу. Дополнительно в корпус электрокотла врезаны 2 датчика, резистивный датчик температуры и датчик, замыкающийся при перегреве.
Система управления на Arduino не совсем простая, включать и выключать реле по PID-закону. Лучше всего два ТЭНа включать сразу, а одним поддерживать температуру, это самый надёжный и наиболее точный способ.
Ток потребления нагревателя составляет примерно 8 А. Лучше всего управлять твердотельными реле с запасом по мощности, примерно ампер 15-20 и с охлаждающим радиатором.
Есть готовые модули Solid State Relay, проще говоря — электросхема, собранная на мощном тиристоре, в корпусе, похожем на обычное реле. Из его плюсов — нет механики, ничего не залипнет. Не создаёт мощных ЭМ-помех, что важно для ethernet’a, о котором ниже.
Они уже содержат схему, которая включает и отключает реле при проходе нуля. На реле есть индикатор включения. Ну и ещё они беззвучные, хотя для нашего случае это не так и актуально. Были установлены SSR-25DA, что по-русски означает — твердотельное реле с постоянным управляющим током 3-5В и током нагрузки до 25А.
К ним есть штатные радиаторы, они должны быть установлены — ОБЯЗАТЕЛЬНО!! Не забываем смазать термопастой, для улучшения теплообмена.
Для измерения температуры были использованы цифровые датчики температуры на базе DS18B20.
Список оборудования и ПО
Что же применялось в конечном итоге:
- Плата arduino. Использовалась UNO r3
- Ethernet Shield
- Витая пара
- Датчики температуры
- Блок питания на 110-240 — 12В 2А
- Стабилизатор LM7805
- Реле SSR-40DA 3 шт
- Радиаторы для реле
Но всё это, конечно, хорошо, но не стоять же постоянно с компьютером рядом с котлом, всё же хотелось бы знать о том, что происходит дома, удалённо через инет. Уже был самый простенький VPS сервер от majordomo для чего попало. На нём создана база данных на MySQL для хранения данных о температуре. (на этом мои познания пока ограничены).
Теперь нам надо как-то положить данные из arduino в эту базу. Для этого, естественно, понадобится как минимум связать arduino с интернетом.
Для этого нам и понадобится Ethernet Shield и его библиотека. Установлен простенький роутер. Тянем стандартную витую пару к роутеру и добавляем в программу передачу данных. Передача идёт через вызов странички на PHP с параметрами — данными. Создаём страничку с именем temp.php на нашем инет-сервере.
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 | <?php $dbhost = 'localhost'; $dbuser = '***'; $dbpass = '***'; $dbname = 'nikolaarduino'; $client_ip = $_SERVER['REMOTE_ADDR']; $temp1 = $_GET["t1"]; $temp2 = $_GET["t2"]; $temp3 = $_GET["t3"]; $power = $_GET["p"]; $connect = mysql_connect($dbhost, $dbuser, $dbpass); if(! $connect ) { die('Could not connect: ' . mysql_error()); } mysql_select_db ($dbname, $connect); $sql = "INSERT INTO Temp (datetime, temp1, temp2, temp3, power, ip) VALUES (Now(), '$temp1', '$temp2', '$temp3', '$power', '$client_ip')"; if(!mysql_query($sql)) {echo '<p><b>Data upload error!</b></p>';} else {echo '<p><b>OK</b></p>';} mysql_close($connect); ?> |
После этого мы имеем данные о температурах и мощности работы котла, чтобы каждый раз не лазить в базу, а посмотреть последние данные, автор написал “временный” скрипт на php, gettemp.php
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 | <?php $dbhost = 'localhost'; $dbuser = '***'; $dbpass = '***'; $dbname = 'nikolaarduino'; $client_ip = $_SERVER['REMOTE_ADDR']; $connect = mysql_connect($dbhost, $dbuser, $dbpass); if(! $connect ) { die('Could not connect: ' . mysql_error()); } mysql_select_db ($dbname, $connect); $query = 'SELECT * FROM Temp WHERE id=(SELECT MAX(ID) FROM Temp)'; $result = mysql_query($query) or die('Error get data: ' . mysql_error()); echo "<table style='text-align: left; width: 100%;' border='1' cellpadding='2' cellspacing='2'>\n"; while ($line = mysql_fetch_array($result, MYSQL_NUM)) { echo "<tr align='center'><td colspan='2' rowspan='1'>$line[1]</td></tr>"; echo "<tr align='center'><td>TempIN = $line[2]</td><td> TempOUT = $line[3]</td></tr>\n"; echo "<tr align='center'><td colspan='2' rowspan='1'>TempKotel = $line[4]</td></tr>\n"; echo "<tr align='center'><td colspan='2' rowspan='1'>Power = $line[5]</td></tr>\n"; } echo "</table>\n"; mysql_free_result($result); mysql_close($connect); ?> |
Вот и практическое применение языка PHP в сфере автоматизации. Честно говоря, пока не углублялся в такие дебри, но всё равно интересно!!!
Ну и, сам скетч.
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 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | #include <OneWire.h> #include <DallasTemperature.h> #include <PID_v1.h> #include <DHT.h> #include <Ethernet.h> #include <SPI.h> // OneWire DS18S20, DS18B20, DS1822 Temperature Example // // http://www.pjrc.com/teensy/td_libs_OneWire.html // // The DallasTemperature library can do all this work for you! // http://milesburton.com/Dallas_Temperature_Control_Library OneWire ds(6); // on pin 10 (a 4.7K resistor is necessary) DallasTemperature sensors(&ds); boolean waithTemp = false; int TEMPERATURE_PRECISION = 10; int lamp1 = 7; int lamp2 = 8; int lamp3 = 9; DeviceAddress IntThermometer = { 0x28, 0x8E, 0xF4, 0x28, 0x05, 0x00, 0x00, 0x07 }; DeviceAddress OutThermometer = { 0x28, 0x65, 0x15, 0x32, 0x05, 0x00, 0x00, 0xE2 }; DeviceAddress KatThermometer = { 0x28, 0x61, 0x43, 0x28, 0x05, 0x00, 0x00, 0x14 }; byte addr[8]; unsigned long StartTime = 0; unsigned long WorkWindow = 60000; // 10min unsigned long WorkTime, TenTime; float maxData = 100; float celsius, temp; double Setpoint, Input, Output; int ThermometerCount; DeviceAddress Thermometer[100]; PID myPID(&Input, &Output, &Setpoint, 10, 0.1, 5, DIRECT); //0.000006 0.03 40 double targetTemp = 22.5; byte mac[] = { 0xE0, 0x69, 0x95, 0x72, 0x65, 0xE8 }; byte ip[] = { 192, 168, 1, 100 }; byte server[] = { ?, ?, ?, ? }; EthernetClient client; byte webskipcount = 10; byte webcount = 0; void setup(void) { Serial.begin(9600); //Ethernet.begin(mac, ip); Ethernet.begin(mac); sensors.begin(); pinMode(lamp1, OUTPUT); pinMode(lamp2, OUTPUT); pinMode(lamp3, OUTPUT); Setpoint = targetTemp; myPID.SetOutputLimits(0, maxData); myPID.SetMode(AUTOMATIC); celsius = targetTemp; StartTime = millis(); } long filter(long x, long Nb, long k) { static long y = 0, z = 0; z += (x - y); return y = (Nb * z) >> k; }; void loop(void) { byte i; byte type_s; byte data[12]; long Out; if (millis() < StartTime) { StartTime = millis(); } WorkTime = millis() - StartTime; if (WorkTime > WorkWindow) { WorkTime = WorkTime - WorkWindow; StartTime = millis() + WorkTime; } //Serial.println("cycle"); sensors.requestTemperatures(); celsius = sensors.getTempC(IntThermometer); Input = celsius; if (webcount >= webskipcount) { char buffer[10]; String temperatureS1 = dtostrf(celsius, 2, 2, buffer); String temperatureS2 = dtostrf(sensors.getTempC (OutThermometer), 2, 2, buffer); String temperatureS3 = dtostrf(sensors.getTempC (KatThermometer), 2, 2, buffer); String OutputPowerS = dtostrf(Output, 2, 2, buffer); String msg = "GET /temp.php?t1="+ temperatureS1 + "&t2=" + temperatureS2 + "&t3=" + temperatureS3 + "&p="+OutputPowerS; Serial.println(msg); client.connect(server, 80); client.println(msg); client.stop(); webcount = 0; } else { webcount += 1; } myPID.Compute(); //Serial.print(Input);Serial.print(" ");Serial.print(Output); //Serial.print(" ");Serial.println(Setpoint); if (Output > maxData/3*2) { digitalWrite(lamp1, HIGH); digitalWrite(lamp2, HIGH); Out = Output - maxData/3*2; } else if (Output > maxData/3) { digitalWrite(lamp1, HIGH); digitalWrite(lamp2, LOW); Out = Output - maxData/3; } else { digitalWrite(lamp1, LOW); digitalWrite(lamp2, LOW); Out = Output; } TenTime = map(Out, 0, maxData/3, 0, WorkWindow); Serial.print(celsius); Serial.print(" "); Serial.print(sensors.getTempC(OutThermometer)); Serial.print(" "); Serial.print(sensors.getTempC(KatThermometer)); Serial.print(" "); Serial.print(Output); Serial.print(" "); Serial.print(TenTime); Serial.print(" "); Serial.println(WorkTime); if (WorkTime < TenTime) { digitalWrite(lamp3, HIGH); } if (WorkTime > TenTime) { digitalWrite(lamp3, LOW); } } |
Здесь я останавливаюсь, спасибо автору за его проект. Было бы интересно пообщаться, я всегда на связи. Спасибо за внимание! Всего доброго!!!
С уважением, Гридин Семён
Семен! Добрый день! Хотел бы уточнить у вас есть ли какой то опыт реального использования данной схемы и какие выявились проблемы (если выявились)? Второй вопрос: почему вы использовали один ПИД контроллер на два ТЭНа, может имело смысл два массива коэффициентов использовать один более грубый один точнее? Скорее всего экономия электричества была бы. Третий вопрос: как был вычислено время для пересчета коэффициентов? (система отопления очень инерционная или у вас каркасный дом)
День добрый, это не мой проект, я в начале статьи указал. Мне он встретился в просторах интернета, и я для того чтобы не потерять, опубликовал здесь.
Но на некоторые вопросы могу ответить. Насчёт одного контроллера на два ТЭНа, это скорее всего использовалось в целях экономии. Два массива коэффициентов работать точно не будут, они друг другу будут мешать, лучше всего делить на группы Тэнов, один на двухпозиционник, другой на ПИД. Надо быстро нагреть включаем сразу две твердотелки, догоняем до первой, одну группу выключаем и поддерживаем температуру ПИДом. Время не вычисляется, всё делается только эмпирическим путём. Системы разные, нагрев разный, конструкция разная. Нет готовых алгоритмов.