Последнее время в нашем доме часто стала возникать проблема с горячей водой. В принципе, это началось, когда мы отсудили котельную в общедомовую собственность и начали сами ей распоряжаться, в том числе наняли другую обслуживающую организацию. Потом обслуживающую организацию снова поменяли, стало дешевле, но качество не выросло. И больше всего напрягало, что с момента остановки котельной до момента, когда горячая вода снова нагревалась до нужной температуры, проходило очень много времени — порой до четырех часов. Складывалось ощущение, что диспетчеризация не работала, либо на ее сигналы просто забивал диспетчер, а высылали техника к нам только после телефонного звонка. Приходилось щупать змеевик в ванной и гадать — 5 минут назад было теплее и он остывает или все-таки котельную уже запустили и он нагревается? Бегать в котельную вечером, как вы понимаете, тоже очень лениво.
Наткнулся тут на обзоры всяких датчиков от
Sanja, этот добрый человек прислал ссылки на запчасти, что мне надо закупить (
плата с модулем WiFi NodeMCU ESP8266 и
датчики DS18b20) и даже
ролик на ютубе, показывающий, что надо делать и как. В принципе, этих ссылок достаточно для создания аналогичного устройства, но были и сложности, о которых я и хочу рассказать, дабы еще более упросить повторение процесса для таких же новичков в этом деле, как я)
Это мой первый опыт использования подобной техники и программирования контроллеров, поэтому используемые методы пайки или монтирования могут повергнуть в шок бывалых мастеров… Впечатлительных прошу отдалиться от мониторов). Также я не стал заморачиваться с печатью корпуса для устройства, а использовал пластиковую бутылку (почти), как научила меня передача «Очумелые ручки» в свое время :)
Итак, нам понадобится:
- Плата с модулем WiFi NodeMCU ESP8266 ~220 руб
- Датчики Dallas DS18b20 по ~80 руб за штуку
- Резистор 4к7 маломощный ~5 руб
- Разъемы «мама» для подключения к ножкам Arduino и подобным ~120руб за 40 штук
- WiFi роутер (если в предполагаемом для установки помещении его еще нет) ~400 руб б/у
- Блок питания USB (1А, говорят, достаточно, но я брал на 2А) и кабель microUSB для программирования (при подключении к компу) и питания ~200 руб
Сначала параллельно соединяем все датчики, которые нам нужны, в одно целое
Получилось примерно так:
Потом, конечно, это все тщательно замотано изолентой/термоусадкой.
Подключаем к пинам по схеме, плату подсоединяем USB-кабелем к компьютеру, запускаем свежеустановленный Arduino 1.8.2 и настраиваем его, как описано
тут. Порт меняем на COM4 (если плата уже подключена).
Распаковываем
архив в папку, где у вашей Arduino лежат скетчи. В комплекте уже есть библиотеки, которые было так тяжело найти подходящие, пол дня убил на это.
Загружаем сам скетч sketch_esp8266dallas в среду разработки.
Меняем там на строках 14 и 15 название WiFi сети и пароль к ней, а также на 45 строке адрес сервера, на котором будут сохраняться передаваемые параметры. Серверная часть (PHP) описана ниже.
К слову, мы могли бы уже здесь отправлять уведомления в Telegram, если температуры ниже заданного уровня, немного изменив код отправки GET-запроса, но Telegram у нас в России под запретом, поэтому существуют некоторые проблемы с использованием его API. Альтернативный вариант — использовать API от sms.ru, но это уже совсем другая история. Главное, что сюда можно вписать абсолютно любое обращение к какому угодно сервису, будь то «народный мониторинг» или любой другой.
Пробуем скомпилировать скетч через меню «Скетч — Проверить/Компилировать», если все хорошо и ошибок нет, то можно загружать в плату «Скетч — Загрузка».
Открываем меню «Инструменты — Монитор порта» и видим примерно такую картину:
А если у вас картина другая и плата WiFi-соединение поднимает, но запросы нифига не идут, то скорее всего роутер находится слишком далеко и сигнал слабый. У меня роутер стоял в соседней комнате (!) и я пару часов убил на выяснение, почему же не работает сервис Blink или любой другой. Когда ты этим занимаешься первый раз, сложно понять, что плате просто не хватает мощности сигнала, ведь к сети то она подцепиться смогла, причем с первого раза. И именно по этой причине пришлось купить дополнительный роутер, потому что в подвале, где я хотел расположить датчик, ситуация с WiFi была бы еще хуже. Я даже посмотрел кучку обзоров на разные вариации этой платы и анализ мощности приема WiFi-сигнала, а также возможность использования дополнительных антенн, но найденные примеры не демонстрировали существенного эффекта.
Тут мне пришла в голову мысль, что вместо WiFi-платы можно было бы использовать сразу модуль с RJ-45 на борту, но было уже поздно :)
Сигналы датчиков обрабатываются успешно, запросы отправляются, теперь приступим к серверной части.
У меня, как у любого порядочного веб-программиста, имеется с пяток хостинговых площадок в распоряжении, поэтому вопроса о том, где разместить серверную часть, не возникло — уже работал в ТСЖ сайт на вордпрессе. Но есть и варианты бесплатного хостинга с PHP и MySQL, я ими не пользовался и ссылок ставить не буду, но найти их легко в Яндексе.
Далее нам нужно через PHPMyAdmin создать табличку в любой имеющейся или новой базе с названием temperatures и полями: datatime (тип DATETIME), t1 (DECIMAL), t2 (DECIMAL), t3 (DECIMAL), t4 (DECIMAL).
Распаковываем
архив серверной части через ftp у себя на сервере и правим в файлах index.php и json.php параметры соединения с базой данных и в index.php внешний адрес скрипта для получения данных. Также у меня весь скрипт лежит в папке /temp/ на сайте, а если вы собираетесь распаковать все в корень сайта, то надо в index.php заменить все вхождения "/temp/" на "/".
В коде нет ничего невероятного, если кому-то лень скачивать архив, то вот содержимое основных php-скриптов:
index.php
<?php
// Параметры соединения с базой данных
$db_server = 'localhost';
$db_name = '*****';
$db_user = '*****';
$db_passwd = '******';
// Подключение к базе данных
$db_connection = @mysql_connect($db_server, $db_user, $db_passwd);
if (!$db_connection || !@mysql_select_db($db_name, $db_connection))
{
echo "Error during SQL connection";
}
mysql_select_db($db_name, $db_connection);
// Сохранение в базу переданных с контроллера данных
if(isset($_GET['t1']) && isset($_GET['t2']) && isset($_GET['t3']) && isset($_GET['t4'])) {
$t1 = floatval($_GET['t1']);
$t2 = floatval($_GET['t2']);
$t3 = floatval($_GET['t3']);
$t4 = floatval($_GET['t4']);
if($t1>0 && $t2>0 && $t3>0&& $t4>0) {
$result = mysql_query("
INSERT INTO `temperatures` (datatime,t1,t2,t3,t4)
VALUES (now(), $t1, $t2, $t3, $t4)");
echo mysql_error();
// Если температуры ниже минимума, отправим сообщение в Telegram
if($t4<40 || $t2<35 || $t1<50 || $t3<50) {
include 'tele.php';
message_to_telegram("ГВС_П: $t4, ГВС_О: $t2, Котельная_П: $t1, Котельная_О: $t3");
}
}
die();
}
?>
<html>
<head>
<title>Температурные данные дома №*** по ул.*********</title>
<link href="/temp/style.css" rel="stylesheet">
<link href="/temp/jquery.datetimepicker.min.css" rel="stylesheet">
<script type="text/javascript" src="/temp/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="/temp/moment.js"></script>
<script type="text/javascript" src="/temp/jquery.datetimepicker.full.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class='current row'>
<div class='col time'></div>
<div class='t1 col'>ГВС, подача:
<b></b></div>
<div class='t2 col'>ГВС, обратка:
<b></b></div>
<div class='t3 col'>Котельная, подача:
<b></b></div>
<div class='t4 col'>Котельная, обратка:
<b></b></div>
</div>
<div class="row selectperiod">
<div class="col"><input type="text" id="datastart" value="" class="form-control"/></div>
<div class="col"><input type="text" id="dataend" value="" class="form-control"/></div>
<div class="col">Точность: <select id="intervalMinutes">
<option value="1">1 мин</option>
<option value="5">5 мин</option>
<option value="15">15 мин</option>
<option value="30" selected>30 мин</option>
<option value="6">1 час</option>
</select>
</div>
</div>
<div id="chartContainer" style="position: relative; height:40vh; width:90vw;margin: 0 auto;">
<canvas id="myChart"></canvas>
</div>
<script>
// Создание и отрисовка графика
function getChart(dataFromJSON) {
var dates = [], t1arr = [], t2arr = [], t3arr = [], t4arr = [];
dataFromJSON.forEach(function(item, i, arr) {
dates.push(item.datatime);
t1arr.push(item.t1);
t2arr.push(item.t2);
t3arr.push(item.t3);
t4arr.push(item.t4);
});
var ctx = document.getElementById("myChart").getContext('2d');
window.myChart = new Chart(ctx, {
type: 'line',
data: {
labels: dates,
datasets: [{
label: 'ГВС, подача',
data: t1arr,
borderColor: '#ffc700',
backgroundColor: 'rgba(255,255,255,0)'
},{
label: 'ГВС, обратка',
data: t2arr,
borderColor: '#3bd100',
backgroundColor: 'rgba(255,255,255,0)'
},{
label: 'Контур котельной, подача',
data: t3arr,
borderColor: '#ff0000',
backgroundColor: 'rgba(255,255,255,0)'
},{
label: 'Контур котельной, обратка',
data: t4arr,
borderColor: '#ee00ff',
backgroundColor: 'rgba(255,255,255,0)'
}]
},
options: {
responsive: true,
//maintainAspectRatio: false,
legend: {
display: false
},
scales: {
xAxes: [{
type: 'time',
time: {
unit: 'minute'
}
}]
}
}
});
}
// Обновление графика данными
function updateChart(dataFromJSON) {
var dates = [], t1arr = [], t2arr = [], t3arr = [], t4arr = [];
dataFromJSON.forEach(function(item, i, arr) {
dates.push(item.datatime);
t1arr.push(item.t1);
t2arr.push(item.t2);
t3arr.push(item.t3);
t4arr.push(item.t4);
});
window.myChart.data.labels = dates;
window.myChart.data.datasets[0].data = t1arr;
window.myChart.data.datasets[1].data = t2arr;
window.myChart.data.datasets[2].data = t3arr;
window.myChart.data.datasets[3].data = t4arr;
window.myChart.update();
}
// Получение данных для графика
function doUpdateChart() {
$.getJSON( "путь к файлу/json.php?datastart="+$("#datastart").val()+"&dataend="+$("#dataend").val()+"&intervalMinutes="+$("#intervalMinutes").val(),
function( data ) {
updateChart(data);
});
}
// Получение текущих данных
function doUpdateLastValues() {
$.getJSON( "путь к файлу/json.php?last=true",
function( data ) {
$(".time").html((data[0].datatime+'').replace(' ', '
'));
$(".t1 b").text(data[0].t1+"°C");
$(".t2 b").text(data[0].t2+"°C");
$(".t3 b").text(data[0].t3+"°C");
$(".t4 b").text(data[0].t4+"°C");
});
}
$(function() {
// Выбор дат
$.datetimepicker.setLocale('ru');
$('#datastart,#dataend').datetimepicker({
format:'d.m.Y H:i',
lang:'ru',
minDate:'20.11.2018',
maxDate:Date.now(),
formatDate:'d.m.Y',
dayOfWeekStart: 1,
i18n:{
ru:{
months:[
'Январь','Февраль','Март','Апрель',
'Май','Июнь','Июль','Август',
'Сентябрь','Октябрь','Ноябрь','Декабрь',
],
dayOfWeek:[
"Вс", "Пн", "Вт", "Ср",
"Чт", "Пт", "Сб",
]
}
},
});
// Загрузка текущих данных
doUpdateLastValues();
// Заполнение графика
$.getJSON( "путь к файлу/json.php?datastart="+$("#datastart").val()+"&dataend="+$("#dataend").val()+"&intervalMinutes="+$("#intervalMinutes").val(),
function( data ) {
getChart(data);
});
window.lastDataStart = $("#datastart").val();
window.lastDataEnd = $("#dataend").val();
window.intervalMinutes = $("#intervalMinutes").val();
var winHeight = $(window).height();
setInterval(function() {
var changed = false;
if($("#datastart").val() != window.lastDataStart) {
changed = true;
window.lastDataStart = $("#datastart").val();
}
if($("#dataend").val() != window.lastDataEnd) {
changed = true;
window.lastDataEnd = $("#dataend").val();
}
if($("#intervalMinutes").val() != window.intervalMinutes) {
changed = true;
window.intervalMinutes = $("#intervalMinutes").val();
}
if(changed)
doUpdateChart();
}, 1000);
// Обновлять текущие данные раз в 30 сек
setInterval(function() {
doUpdateLastValues();
doUpdateChart();
}, 30000);
});
</script>
</body>
</html>
json.php
<?php
ini_set('error_reporting', E_ALL);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
// Параметры соединения с базой данных
$db_server = 'localhost';
$db_name = '*****';
$db_user = '*****';
$db_passwd = '*****';
// Вычисление разницы дат
function dateDifference($date_1 , $date_2 , $differenceFormat = '%a' )
{
$datetime1 = date_create($date_1);
$datetime2 = date_create($date_2);
$interval = date_diff($datetime1, $datetime2);
return $interval->format($differenceFormat);
}
// Подключение к базе данных
$db_connection = @mysql_connect($db_server, $db_user, $db_passwd);
if (!$db_connection || !@mysql_select_db($db_name, $db_connection))
{
echo "Error during SQL connection";
}
mysql_query("set names utf8");
// Если запрошены текущие данные
if(isset($_GET['last'])) {
$result = mysql_query("
SELECT DATE_FORMAT(DATE_ADD(datatime, INTERVAL 1 HOUR),'%d.%m.%Y %H:%i:%s') datatime,t1 t3,t2 t2,t3 t4,t4 t1 FROM temperatures
ORDER BY datatime DESC
LIMIT 1");
$data = [];
while($row = mysql_fetch_assoc($result)) {
$data[] = $row;
}
// Выводим только их и заканчиваем на этом
die(json_encode($data ));
}
$intervalMinutes = 30;
$datastart = date_add(date_create(date('Y-m-d H:i:s')), date_interval_create_from_date_string('-1 days'));
$dataend = date_add(date_create(date('Y-m-d H:i:s')), date_interval_create_from_date_string('1 hour'));
if(isset($_GET['intervalMinutes']))
$intervalMinutes = intval($_GET['intervalMinutes']);
// Если слишком большой временной промежуток, то не будем наружать сервер большим объемом данных, округлим побольше
$difHours = date_diff($datastart, $dataend);
if($difHours->format('%days')>3)
$intervalMinutes = 60;
if($difHours->format('%days')>10)
$intervalMinutes = 180;
if(isset($_GET['datastart']) && $_GET['datastart']!="")
$datastart = date_create($_GET['datastart']);
if(isset($_GET['dataend']) && $_GET['dataend']!="")
$dataend = date_create($_GET['dataend']);
$result = mysql_query("
SELECT roundeddatetime datatime, AVG(t1) t3,AVG(t2) t2,AVG(t3) t4,AVG(t4) t1
FROM
(
SELECT DATE_FORMAT(DATE_ADD(DATE_ADD(datatime, INTERVAL (".$intervalMinutes."-MINUTE(datatime)%".$intervalMinutes.") MINUTE), INTERVAL 1 HOUR),'%Y-%m-%d %H:%i:00') roundeddatetime,t1,t2,t3,t4 FROM temperatures
WHERE datatime>DATE_ADD('".$datastart->format('Y-m-d H:i:s')."', INTERVAL -1 HOUR) AND datatime<DATE_ADD('".$dataend->format('Y-m-d H:i:s')."', INTERVAL -1 HOUR)
) t1
GROUP BY roundeddatetime
LIMIT 3440");
$data = [];
while($row = mysql_fetch_assoc($result)) {
$data[] = $row;
}
echo json_encode($data );
?>
tele.php
<?
// сюда нужно вписать токен вашего бота
define('TELEGRAM_TOKEN', '*****:********');
// сюда нужно вписать ваш внутренний айдишник чата
define('TELEGRAM_CHATID', '414951********599');
//echo message_to_telegram('Данные!');
function message_to_telegram($text) {
$ch = curl_init();
$url = "https://api.telegram.org/bot".TELEGRAM_TOKEN."/sendMessage?chat_id=".TELEGRAM_CHATID."&text=".$text; // где XXXXX - ваши значения
$prxy = '162.33.68.41:56505'; // адрес:порт прокси http://spys.one/proxys/US/
$prxy_auth = 'auth_user:auth_pass'; // логин:пароль для аутентификации
curl_setopt_array ($ch, array(CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true));
/********************* Код для подключения к прокси *********************/
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); // тип прокси
curl_setopt($ch, CURLOPT_PROXY, $prxy); // ip, port прокси
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxy_auth); // авторизация на прокси
curl_setopt($ch, CURLOPT_HEADER, false); // отключение передачи заголовков в запросе
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // возврат результата в качестве строки
curl_setopt($ch, CURLOPT_POST, 1); // использование простого HTTP POST
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // отмена проверки сертификата удаленным сервером
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
// Параметры ниже подходят, если у вас PHP свежей версии
//curl_setopt($ch, CURLOPT_RESOLVE, array("api.telegram.org:443:146.185.158.29"));
//curl_setopt($ch, CURLOPT_DNS_SERVERS, '8.8.8.8,8.8.4.4'); // тип прокси
/***********************************************************************/
$response = curl_exec($ch);
if ($response === FALSE) {
$response = "cURL Error: " . curl_error($ch);
}
$info = curl_getinfo($ch);
//$response .= '
Took ' . $info['total_time'] . ' seconds for url ' . $info['url']."
";
curl_close($ch);
return $response;
}
?>
style.css
.current {
margin-top: 10px;
padding: 0 10px;
font-size: 14px;
}
.current div {
height: 100px;
}
.current b {
display:inline-block;
border: 3px solid #ffc700;
padding: 5px;
font-size: 30px;
}
.t2 b {
border: 3px solid #3bd100;
}
.t3 b {
border: 3px solid #ff0000;
}
.t4 b {
border: 3px solid #ee00ff;
}
.current .time {
padding-top: 20px;
font-weight: bold;
text-align: center;
font-size: 18px;
}
.selectperiod {
padding: 5px 30px;
}
.selectperiod div{
text-align:center;
}
В архиве лежат еще файлики библиотеки jquery, moment.js и jquery.datetimepicker.
Для красивого отображения графика используется
ChartJS.
Итак, теперь у нас сохраняются температуры в базу и даже выводятся в виде красивого графика. Дело за малым — настроить оповещения в Telegram, когда температуры достигают слишком малых значений. По
этой инструкции легко настроить бот, который будет присылать вам сообщения, если послать запрос на api.telegram.org. Мой код такой обработки температур и отправки в телеграм в файле index.php:
// Если температуры ниже минимума, отправим сообщение в Telegram
if($t4<40 || $t2<35 || $t1<50 || $t3<50) {
include 'tele.php';
message_to_telegram("ГВС_П: $t4, ГВС_О: $t2, Котельная_П: $t1, Котельная_О: $t3");
}
Но вот незадача, телеграм то в России заблокирован! И у большинства провайдеров вы даже IP-адрес сервера не получите по этому имени домена.
Для работы с этим сервером придется использовать SOCKS-прокси (выше есть код файла tele.php), плюс в некоторых случаях потребуется прописать
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
чтобы запросы начали ходить бесперебойно.
Можно раскомментировать строчку 9 в файле tele.php и проверить отправку, обновив страницу.
Подготовка завершена, осталось разместить все это на местах:
Придерживаясь колхоз-стайла, датчики к трубе можно примотать великим Скотчем, но для лучшей теплопередачи предварительно смазать место контакта термопастой:
А сверху нужно еще теплоизолировать датчики от внешней среды. Самый практичный вариант — пенофолом (фольгированый вспененный полиэтилен).
Для потомков и случайно оказавшихся в бойлерной слесарей желательно обозначить на корпусе, что это за фиговина с проводами:
Прокинули витую пару с интернетом до бойлерной, подключили роутер, вставили в розетку наше устройство и наслаждаемся картинкой:
Открывать ссылку можно с любого устройства, даже со слабым интернетом. Вообще говоря, веб-сервер можно поднять и на самой ESP8266 и рисовать аналогичный график, заморочившись с обновлением IP-адреса через DynDNS, но «я художник, я так вижу» :) Да и стабильность у такого варианта все-таки куда выше, а еще доработки в серверную часть можно вносить прямо с рабочего места в другом городе, а не программировать каждый раз плату.
На этом можно остановиться, ибо все работает, но есть и пока не решенные проблемы.
Первая — это датчики почему-то иногда сбоят. С периодичностью раз в 4 часа плата почему-то путается в показаниях и присылает откровенную фигню. Один раз даже показало температуру в -70 градусов на одном из датчиков, после чего я внес в серверный код условие добавления в базу значений, только если они все положительные.
Вот пример полученных корявых данных
Второе — если температура все-таки опустится ниже установленного в коде минимума, Telegram-бот вас задолбает сообщениями. Нужно сохранять время предыдущей отправки (например, в файле на хостинге или в базе) и анализировать, прошел ли час (к примеру).
Третье — найденный в интернете прокси-сервер может просто-напросто прекратить свое существование, его нужно будет сменить. А сообщения то вам не будут присылаться в телеграм… Поэтому надо добавить либо оповещение на email, либо сделать парсинг свежих прокси каждый раз. А может кто-то знает лучшее решение?
Надеюсь, хоть кому-нибудь данный пост был полезен, а может кого-то подтолкнет все-таки заказать эту плату и сделать свой вариант погодной станции (следуя поговорке «Что бы вы ни собрались сделать на базе ESP8266, все равно получится погодная станция»).
UPD 1: В комментариях
muskati предложил использовать twitter.com/tgvpn:
['proxy' => 'socks5://login:[email protected]:1080']
UPD 2: Полезный совет от
merlin7 про использование медианного фильтра.
UPD 3:
SiZE предложил вместо танцев с прокси телеграма использовать
bullhorn_bot, намного проще.
UPD 4: На днях сделал на другой плате аналогичное только для 1 датчика, серверную часть скачать можно тут: yadi.sk/d/Ujb7Ucp2QjT94w. (На этот раз разместил в папке /temp2/, поэтому если у вас путь на сайте отличается, поменяйте этот путь по всем файлам внутри.
купить платный прокси за 3 бакса
VPN-сервер разворачиваем скриптом с github.com/jawj/IKEv2-setup (даже домен не понадобится).
на нём же поднимаем прокси для Телеграма (так он на мобильном меньше жрёт батарейку, чем через VPN) -https://habr.com/post/412759/#comment_18717423
Через мою хетцнеровскую виртуалку сидят 60 человек сослуживцев, загрузка минимальная, скорости выжимаются с лихвой за сто мегабит
Если кому-то очень надо и лень ставить, то в личке могу поделиться своим, мне не жалко.
Сейчас €2.79…
VPS же за 1 евро в европе продают, слабенькие очень, но там можно и серверную часть поднять, и собственно заблокирован телеграм для него не будет.
12,79 евро (подняли цены блин!!!) в мес, дают linux VPS vmware 1gb 20gb 1TB трафика1 остался только для продления для клиентов с уже купленным тарифным планом по 1 евро.
отличное начало!
А проблемы мы решим!)
Конкретно нужена только температура в серверной.и если она больше порога то сообщение.
Интересен опыт реальный.
Рисуется график температуры (по простому, через MRTG), при превышении заданного порога отправляется уведомление на почту (самодельным скриптом на питоне).
Если бы первая, самая древняя распберри тормозила меньше, можно было бы сказать что на настройку ушло 15 минут. С учетом ее тормозов, времени ушло чуть больше, только пакеты минут 20 обновлялись )
www.aliexpress.com/item/Sonoff-TH10-TH16-Smart-Wi-Fi/32854996164.html
— не WiFi
— минимальная зависимость от локальной инфраструктуры (железке назначен внешний IP и зависит она только от оборудования провайдера)
— возможность удаленного контроля и настройки
— возможность добавления функций в случае необходимости
Там можно прикрутить разные сервисы, например Народный мониторинг.
По идее бесплатного объема элементов приложения должно хватить, или можно поднять свой сервер на дешевом хостинге.
В приложении графики выглядят красиво, есть экспорт в .csv.
Оповещения приходят в само приложение, думаю API от телеграмм тоже можно прикрутить.
Используйте хотя бы обычный силиконовый герметик, и желательно не через краску
Ставлю уже много лет светодиоды не только на термоклей, но и на герметик, особенно если звезды использую (на автомобильный герметик, что на 200-300 градусов, он удобнее, и гдето читал теплопроводнее обычного строительного)
Когда-то эту идею почерпнул из интернета, попробовал, сравнил температуру радиатора, оказалась разница в пределах статичтических погрешностей.
PS: если вы разбирали блоки питания, в них сплошь и рядом термождатчики на герметике приклеены к радиаторам.
Когда прижимаешь рукой термопару к радиатору отопления то влияет на точность измерения ВСЁ!
И температура руки и время и сквозняк и температура воздуха и даже слой краски.
В результате пришлось прижимать термопару в меховой перчатке через слой поролона и на термопасту.
И держать так несколько минут пока температура не стабилизируется.Или приматывать изолентой через толстый слой поролона на термопасту.Для разового измерения.
В каждой из 9 точек радиатора.
Весь этот ужас кончился только с покупкой ИК термометра.
ЗЫ: уже заработало :) А www добавляла опера если только явно не написать перед именем сервера http:// :)
www.thewindowsclub.com/browser-automatically-adds-www-to-url
Сам столкнулся. Заказчик прозевал оплату хостинга (там все), хостинг хоть и имел бэкап, но все скрывает после подобного. Т.е. надо просить ТП. При восстановлении ребята как-то не правильно прописали зоны в DNS. И вышло так что www.site.ru был доступен, а site.ru редиректил на заглушку, как будто там ни чего нет. Я сразу этого не заметил, т.к. при наборе site.ru хром автоматом добавлял www. и перекидывал меня на сайт. У заказчика же была лиса и он сразу столкнулся с подобной проблемой. Я проверил на всех устройствах что у меня есть и естественно везде у меня хром и на всех устрйоствах я смог зайти на сайт.
Там энтузиасты начали графиком член рисовать, вот ведь муськоэффект… А я даже сознательно в посте ссылки не указал на график, все равно нашлись бета-тестеры))
PZEM избыточен и дорог на три фазы.
Но цена приятная.
Вариант годный, уж всяко лучше, чем самостоятельно искать какие-то трансформаторы и после них городить выпрямители и делители.
PCF8591 или ADS1115 несколько попроще будет :-)
Почему такая просадка по температуре в 10:30 вечера и в 8:30 утра? Хотя, температура теплонасителя не упала.
Мощности теплообменников не хватает? Или они забились и требуют промывки?
Обыкновенные пики потребления.
Вот почему вечером и утром резко уменьшается температура теплоносителя — это уже интересно. Такое впечатление, что ложные срабатывания погодных датчиков или других, которые контролируют работу теплогенерации.
У нас такая ситуация была (с падением температуры в пики), когда теплообменники забились совсем. Мы их промыли химией, и все встало на свои места. И потом пришли к выводу, что теплообменники нужно промывать минимум раз в год. Иначе происходит такая ситуация как на графиках ТС.
Я не о том.
Если подобное снижение температуры ГВС, при пиковых нагрузках, не сказывается отрицательно на потребителях — значит оборудование подобрано правильно, с верным запасом мощности.
Вот скриншот:
InfluxDB + Grafana нравится:
Полностью хранится изначальная точность измерений по сравнению с RRD Решениями. Поддержка Alerts, Plugins, и т.д.
А какая модель счетчика? Как конденсатор подключали? Я просто только начинаю, и еще не все понимаю с полу слова. Т.е. там параллельно или как?
Где-то 33 USD стоят.
В счетчике стоит герконовый датчик (далеко не самое лучшее решение...) — конденсатор поставил параллелельно геркону.
Я бы краску ещё стёр на трубе под датчиком температуры для лучшего контакта.
У меня в проекте мониторинга воздушной среды один из датчиков (кислород) аналоговый. Он дает такие же выбросы. Усреднение результатов приводит к погрешности. Следующий код решил проблему.
// накапливаем данные для медианного фильтра аналогового входа
// период опроса 120мсек
if (millis()-timer3 > 120)
{
//выбираем 65 значений
if (median_count<65)
{
oxy_raw[median_count] = analogRead(pinAdc);
median_count++;
}
timer3=millis();
}
Функция фильтра
float readO2_median ()
{
// сортируем массив по возрастанию значений в ячейках
float temp = 0; // временная переменная
for (int i = 0; i < 65; i++){
for (int j = 0; j < 65 — 1; j++){
if (oxy_raw [j] > oxy_raw [j + 1]){
temp = oxy_raw [j];
raw[j] = oxy_raw [j + 1];
oxy_raw [j + 1] = temp;
}
}
}
// возвращаем значение средней ячейки массива
float MeasuredVout = oxy_raw [32] * (VRefer / 1023.0);
//Сбрасываем счетчик накопления
median_count=0;
return MeasuredVout;
}
При такой фильтрации случайные всплески датчика не будут влиять на результат.
Размещение этих датчиков как раз решает проблему контроля — оно позволило привлечь больше внимания к проблеме со стороны обслуживающей организации, сегодня директор этой организации лично пол дня лазил по подвалу и некоторые выводы уже сделаны, сформулированы методы решения.
Добавил авторизацию, пока кто-нибудь член не нарисовал графиком. Честно говоря, не ожидал, что кто-то будет этим заниматься)
Что я могу сказать.
1. Датчики надо теплоизолировать от окружающей среды, иначе получается «погода на Марсе, осложненная земным климатом». Сами же видите: датчик касается трубы мизерной площадью, а все остальное висит в окружающем холодном воздухе. И скотч — это плохая теплоизоляция (и крепеж плохой). Свои я крепил проволокой, и стоило замотать их в два слоя теплоизоляцией на ширине в полметра — как добавилось почти 10 градусов.
2. Датчики надо калибровать. Потому что метод измерения у вас — косвенный, и температура на поверхности трубы вовсе не равна температуре воды внутри. Я использую такую эмпирическую формулу: до +20 температуры равны, выше — прямая «истинной» температуры идет вверх круче температуры трубы, а угловой к-т получил калибровкой в рабочей точке: взял второй датчик, одел варежки чтобы не обвариться, приоткрыл кран слива трубы — и сунул датчик в поток. И подождал пять минут, а система все это время писала показания с обоих датчиков.
3. Показания датчиков надо фильтровать от ошибок. 1-wire — система, весьма чувствительная к наводкам, а у вас в ИТП наверняка есть насосы и прочие мощные потребители электричества. Так что сбои — будут. Можно вылавливать явные ошибки (те же -70 например), а можно сделать скажем медианный фильтр: берете 5 подряд результатов измерения, сортируете по возрастанию, и в качестве результата используете третье значение.
4. Показания датчиков надо фильтровать от шумов. Датчик неидеален, и конструкция измерительной системы неидеальна, а в итоге будет разброс показаний, не несущий никакой смысловой нагрузки. Используйте например экспоненциальное скользящее среднее — предельно просто реализуется, а результат радует глаз.
P.S. Как сделать экспоненциальное скользящее среднее. Определяемся с длиной… пусть скажем n=10
const float aEMA=2.0/(1.0+n);
float xEMA;
А дальше пусть x это очередные показания датчика
Тогда в самом начале, с первым показанием делаем так
xEMA=x;
А дальше в рабочем цикле, получив очередное значение x
xEMA= aEMA*x+(1.0-aEMA)*xEMA;
И используем xEMA как результат
На фото видны датчики теплосчетчика, стоящие неподалеку, они как раз по-правильному монтированы прямо в среду, внутрь трубы. И показывают тот же результат с разницей в 1 градус (при температуре 50 градусов) Возможно, для больших значений отклонение будет чуть больше. Теплоизолирую снаружи обязательно! Но 10 вряд ли добавится, конечно…
Про медианный фильтр уже кто-то выше советовал, попробую реализовать, спасибо)
С выводом графика прекрасно справляется ChartJS, если указывать период данных не слишком частый, конечно)
Сперва фильтруем данные от ошибок — медианным фильтром, или «окном» (отбрасываем все значения выше и ниже ожидаемых), или еще как.
А после, к свободным от ошибок, но зашумленным данным, применяем другой фильтр — можно обычное скользящее среднее, но мне больше нравится экспоненциальное.
А данные я отправляю в Zabbix (видимо — у кого какая профессиональная деформация, тот на то и горазд :) )
Паразитное питание, это когда датчик запитывается от сигнального провода.
Сверху неправильно подключено, внизу — правильно.
Ну в Вашем случае
Слева как сделали Вы, а справа как надо.
Вот и не сбивайте людей с толку неправильной терминологией :)
Для общего развития я бы тоже с удовольствием почитал как оно на самом деле и почему так. (вот без сарказма!)
По сути там не такие частоты, чтобы мешали какие-то неравномерности на кабеле (тем более, что они в любом случае есть), так что я не думаю, что настоящая причина ошибок — разводка кабеля. Была бы у меня подобная ошибка — я бы разбирался с осциллографом в зубах :)
нормальные датчики с проводами красный, оранжевый, черный, а мертвые красный, желтый, черный… почитал в инете — не мне одному так «везло»((((((((( купил отдельно чипы с хорошими отзывами — исполнение делать сам буду, потому как такой кусок трубки не всегда удобно можно пристроить (разбирал их — внутри просто на герметик вставлен чип без намека на термопасту)…
И крепить удобно, и теплопроводность хорошая.
Вы на общественных началах программером/админом «работаете» или всё-таки за денежку ?, думаю немногие стали бы из альтруистических побуждении (пусть даже для себя) так стараться, расходуя личное время и нервы.
Программером я работаю за денежку, а вот председательство в ТСЖ — скорее хобби, потому что даже 15 тысяч рублей в месяц, которые у меня были окладом первый год после создания ТСЖ, деньгами назвать сложно (с точки зрения веб-программиста, конечно). А с августа этого года уже управлением домом занимается нанятая посторонняя управляющая за оклад 20000 руб, на мой взгляд, самая профессиональная в нашем небольшом городке. А я продолжаю следить за всем за 2900 руб в месяц :)
Альтруизм — это моя слабость, да)
правда размещение котельной на крыше, напряжно как-то, имхо, ГИ хотя бы сделана — а то в случае чего — ой :(
ну как бы котельная располагается на плитах перекрытия крыши, т.е если что та самая плита может внутрь полететь с вытекающими, ну или если на краю — то всё равно будет как-то опираться на стену (плиту в случае плиты) или кирпич (если дом кирпичный) т.е вполне плиту дома на которую опирается крыша может сдвинут и край дома обвалится.…
автоматика — дело такое — может и не сработать вовремя, да и не от всего спасёт увы, должны быть предусмотреть и мех. задвижки и задвижки до задвижек и еще задвижки до задвижек задвижек.
Либо накапливайте данные, а потом пакетом передавайте раз в несколько минут. Либо используйте не WiFi, а другой канал — провод, или скажем LoRa.
habr.com/company/aladdinrd/blog/353738/
— как-то вы сильно упростили «традицию» :)
В базе данных время правильное
На клиенте время правильное
В выдаче json время +1 час вперед — соответственно на странице тоже.
Это лечится?
Спасибо за статью. Подчерпнул так подчерпнул.
А мы в глуши, в Саратове…
Кто портит картину?
в книгув код — вижу фигу. Разобрался :)конструкция
DATE_ADD(datatime, INTERVAL 1 HOUR)
можно написать
DATE_ADD(datatime, INTERVAL 0 HOUR)
или вообще убрать эту функцию
Можно командой в 1-wire настроить точность 9,10,11 или 12бит для каждого датчика.
От этого сильно зависит время преобразования температуры.
Разрешение — Максимальное время конвертирования
9-bit — 93.75 ms (0,5 градуса)
10-bit — 187.5 ms (0,25)
11-bit — 375 ms (0,125)
12-bit — 750 ms (0,0625)
Ну, по крайней мере 4 датчика, лежащие прям рядом с друг другом, всегда показывают относительно разные температуры в пределах погрешности, причем их линии пересекаются, а не движутся все синхронно) А про скорость считывания — не возникало такой проблемы, не подозревал даже о ней.
Просто вот эти 4 строчки:
float temp1 = sensors.getTempCByIndex(0);
float temp2 = sensors.getTempCByIndex(1);
float temp3 = sensors.getTempCByIndex(2);
float temp4 = sensors.getTempCByIndex(3);
Выполняются за 3 секунды, а могли бы за 0,375сек :) Вдруг пригодится в дальнейшем…
Период опроса в скетче 60сек. К ним каждый раз прибавляется еще 3 сек. минимум.
То есть реальный период 63 сек.
По-умолчанию «с завода» оно 12бит
При инициализации датчиков есть строка bitResolution=9
А в sensors.begin есть такая строчка:
bitResolution = max(bitResolution, getResolution(deviceAddress));
То есть выберет то, что установлено в датчике.
А калибровкой — систематическую.
P.S. А еще у этих датчиков есть саморазогрев: при интенсивной работе температура кристалла может «уходить» выше температуры корпуса на 2-3 градуса. Для вас это несмертельно, а вот при измерении температур около ноля С — бывает обидно; лечится либо увеличенными интервалами измерений (скажем, не чаще раза в пару минут), либо… использованием другого типа датчиков. PT-100 например, с соответствующим преобразователем.
на
Но в этом случае не обойтись без фильтрации данных, а то часто будут сбои приходить, и фиг просмотришь потом.
А если именно отправку на телеграм поменять, то в файле index.php поменять строки
на
Сервис полезный, периодически пользуюсь им для целей ТСЖ.
Про «облака» — icloud например, это хранилище, типа флешки. Как отправить температурные данные на флешку? Ну, можно создавать каждый раз новый файл… Можно дописывать в какой-то определенный файл и сохранять… В зависимости от того, как вы придумаете хранить информацию в этом «облаке», можно и реализовать как угодно. Чаще всего подобные операции можно сделать с помощью HTTP-запроса, пример которого я и описал выше
1. Установил всё в клеммную коробку или маленький пластиковый щиток, например в щит для автоматов на 2 DIN места. Повесил бы это щит\коробку в положенном по правилам месте, то есть не на трубу, не рядом с кранами, не на пол бы бросил…
2. Кабели. Не плохо бы повесить бирки, кабели проложить в гофре или лотке, кабельканале…
3. Датчики. Ну так прилепить скотчем — совсем стыд. Матнули бы теплоизоляцией, чтоб этого колхоза не видеть, да и показания точнее станут.
А так как есть — вызывает желание оторвать нафиг и выкинуть.
Кстати, термоизоляцией уже обмотал.
По железной части, если витую тянуть в котельную, то ИМХО еэспишка и роутер- излишне. Роутер A5-V11 за $7 с прошивкой CyberWRT, в USB роутера переходник usb-uart, к нему шлейф датчиков, в RJ45 роутера- витую пару. В прошивке ставится модуль «термостат».
А трупы завонялись так что пришлось жалобу писать.
А для мониторинга использую заббикс. Вопроса с повторными оповещениями не возникает. Но для данной задачи это конечно как из пушки по воробушкам
Про СМС писал тут — mySKU.me/blog/diy/68375.html#comment2906254
www.techmarcet.ru/zadvizhki/zadvizhki-s-ehlektroprivodom/?_openstat=ZGlyZWN0LnlhbmRleC5ydTszNDUzMTIxOTs1Njc0OTgxNzMzO3d3dy55YW5kZXgucnU6cHJlbWl1bQ&yclid=7029105930651326929
У нас такие стоят на погодной автоматике. Но вот сможет ли ими управлять ESP8266 — вопрос)
Через пол года скотч отклеится.
Сразу видно программиста!
Да и вообще вся история показательна.
На самом деле да, этот проект был собран «для себя» — посмотреть, как оно вообще будет работать и будет ли.Ну а временные решения у нас, как водится, самые постоянные. Про хомуты кстати не вспомнил почему-то. В идеале да, гильзы должны быть на трубах, с маслом, а туда уже датчик втыкать… Но эти гильзы приделать — столько мороки… И зачем, если и так все показывает отменно) Теплоизоляцию сделал, конечно, чуть позже, я и в посте это писал.
Закончите мысль?
Можно найти схему теплового узла? Её автоматику?
Хотя, сразу оговорюсь: в «классических» тепловых пунктах температуру ГВС невозможно поддержать точно, да и цели такой никто никогда не ставит. Экономической справедливости ради имеет смысл поставить теплосчётчик, учитывающий потребление именно контура ГВС, и тогда в подаче +45С не будет ничего «обидного», она холоднее, но дешевле.
Ок, как тогда Вы поймете, что проблема решена?
Вы плохо представляете себе работу управляющих компаний в ЖКХ с точки зрения психологии (и/или философии). Если люди не звонят, чтобы сообщить о проблеме, это не значит, что ее нет. Если компания, обслуживающая котельную, говорит, что проблема решена, это не обязательно означает, что она действительно решена. Если вы считаете, что легко сменить обслуживающую котельную компанию на более профессиональную без увеличения стоимости ее содержания для жильцов, то это тоже не так.
И отговорка «у вас горячая вода зато дешевле будет» плохо работает, когда люди просто не могут ванну горячую набрать, чтобы ребенка искупать.
Понятное дело, никто под «правильное решение» реконструкцию котельной не устроит, и тепловой пункт возводить не станет. Но если есть что-то, что можно «покрутить», то кручение этого самого прекрасно можно автоматизировать. Только делать это нужно нормальным контроллером, а не ардуиной-стм-есп.
Всё таки, было бы очень хорошо увидеть схему вашего теплоснабжения, тогда разговор стал бы более предметным.
И чисто по-человечески, очень рекомендую посмотреть на термостатические смесители, нынче их на Али от 30 долларов продают, а 15 лет назад они были по 200, и всё равно того стоили. Не панацея, но добавляют много комфорт и даже безопасности.
Проблемы есть две:
1. Нерегулярная подача теплоносителя на теплообменник от котельной.
2. Неэффективная работа теплообменника по нагреву воды до нужной температуры.
Первая проблема практически решена — вчера собственноручно нашли причину непонятной разницы температуры на котлах и приходящей к теплообменнику, она заключалась в том, что из 11 котлов работали только 5, а вода шла через все 11. Таким образом, разогретая до 85 градусов смешивалась с 65 градусами обратки, проходящей через выключенные котлы, и в итоге получалось 71 на выходе. А инженеры организации, обслуживающей котельную, этого не замечали. Закрыли краны на выключенных котлах и поставили на рабочих котлах температуру 73 градуса вместо 85 — верхняя линия на графике стабилизировалась. Думаю, также сократился и расход газа, но это подтвердится только 25-го декабря.
Вторую проблему с забитым теплообменником вчера увидели на тепловизоре. Прокладки для них уже заказали, как придут — поменяем и проведем промывку, будут работать. И греть водичку с гораздо большим КПД, чем сейчас.
А при чем тут смесители?
К вопросу они не относятся.
А вот то, почему между котельной и теплообменником нет никакого регулирования, это вопрос.
Нет, заглушить не работающие котлы это хорошо, почистить теплообменник (хотя это надо каждый год делать) — тоже. Только Вы ни разу регулирование не упомянули, а это очень странно. Ну, сами посудите, если расхода ГВС нет, то теплообменник (ТО) должен лишь компенсировать остывание ГВС при циркуляции (разницу между подачей и обраткой), а если расход есть, то ему надо холодную воду (в расчёте берётся +12С) нагреть до т-ры подачи ГВС, а это совсем другая мощность. Ну вот не могу я поверить, что где-то ГВС включено через теплообменник, но не содержит регулирующего клапана (со стороны теплоносителя котельной). Он даже термо-механическим может быть, но он должен быть всегда.
Я пока промолчу о том, что в норме контур ГВС имеет два теплообменника, где первый стоит в разрыв обратки теплосети, не имея никакой регулировки, а второй уже работает «нормально».
Бес схемы невозможно интерпретировать Ваши графики. Видно, что теплообменнику не хватает мощности. Ночью, без расхода, ГВС нагревается сильно, а в часы расхода не догревается. Возможно, чистка теплообменника поможет, но обычно его выбирают с запасом, тогда и разумное загрязнение ему не критично, и т-ру ГВС можно сдлеать стабильной за счёт регулирования подачи теплосносителя. Главное, чтобы был запас по мощности, без него регулировать нечего.
И для справки: проблемы теплообменника определяют по перепаду давления на манометрах, тепловизор для этого не нужен.
в json.php?last=true вернет данные за 30 ноября максимум, т.к. сначала изменяется порядок в формате даты, а потом сортировка.
Главным определяющим сортировку становится день-месяц-год а не год-месяц-день.
Только 31 декабря мы увидим данные, т.к. 31>30 :)
Поправил колхозно просто добавив еще одно поле
Может есть более изящный способ?
Интересно все таки как «правильно»…
Нужны еще какие то модули?
P.S. Немного соображаю в котельных, но в Ардуино не погружался.
C:\arduino-1.8.8\libraries\Blynk\src/BlynkSimpleEsp8266.h:18:21: fatal error: version.h: No such file or directory
Что оно не может найти файл BlynkSimpleEsp8266.h
Создаю базу phpMyadmin с именем (temperatures) как в инструкции.
Прописываю имя и тип datatime (тип DATETIME), t1 (DECIMAL)
Выдает ошибку:
Я просто далек от этого. Подскажите пожалуйста как исправить. :)
И последний вопрос.
На хостинге возникла ошибка в index.php (на 17 строке) я не пойму как ее исправить
И я не понял что писать на 7 строке в место звездочек "$db_name ="
Но не все так просто как хотелось.
При мониторинге порта появляется такая строчка
это проблема в сайте или проблема в моём браузере? Если на сайте, то как ее можно решить.
Где копать?