Время на прочтение

52K

Здравствуйте. Существует такое устройство, как гидролок\нептун\авквасторож — это системы, предназначенные для автоматического перекрытия подачи воды в случае неконтролируемой утечки. Принцип работы довольно прост: в состав входят датчик воды, управляющая автоматика и пара кранов с электроприводами.

Самодельный «умный дом»: от идеи до

В рассказе пройдусь по кирпичикам системы, чем руководствовался в выборе. Вся система строится на заводских датчиках и самодельном контроллере на базе Particle (ex.Spark) Photon (такая esp8266 у которой облачная IDE на wiring из коробки), база девайса stm контроллер + wifi модуль от броадкома. Все это завязано на openhab сервер на Orange Pi One.


Почему не готовая система?

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

Начем с кранов

Выбирал тупо в лоб по крутящему моменту. Некоторое время проживал в подмосковье, где качество воды (как наверно везде в замкадье) оставляет желать лучшего. Так шаровые краны на 1\2 дюйма если год не трогать — повернуть очень тяжело. А на полотенцесушителе 1 дюймовые я даже и не пытаюсь шевелить — только если усилить плечо ключем разводным, а тут и сорвать чего-нибудь можно. Проблема в отложениях кальциево-хзчего, «зарастают» одноим словом.

Соответсвенно выбор пал на проф серию от гидролока — 21Н*м крутящего момента по ощущениям не рекламный треп, кран просто огромный — оцените место его установки перед покупкой.

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

Перед нами верхняя часть платы и шаговый двигатель. Питается все это от 12 вольт. Замыкание контрольного кабеля на землю переводит кран в закрытое положение. На плате видим простенький контроллер PIC 12f629. Дожили, контроллер в приводе крана.

Сзади платы самое интересное.

L293 драйвер шаговика и фотопара (излучатель + фотоприемник). Она смотрит на основную шестерёнку привода, которая раскрашена на части — белая и черная, закрыто\открыто.

Кран вращается все время в одну сторону, логика контроллера простая — крутим вал, пока не переключимся на нужный цвет. Вращение крана в одну сторону, это меньше износ, а бесконтактный способ определения положения — меньше шансов закисания\сбоя переменного резистора или концевика.

Для монтажа можно открутить кран от привода — держится на 2 гайках. Между приводом и краном теплоизоляционная прокладка.

Ремонт у меня был полтора года назад. Кран покупал года три назад — разобрать, посмотреть внутри, купить еще и накрутить во время ремонта. Ага, сейчас… максимум чего успел в этом ацком цирке — заложить в сборку водоразводки грязевик с переспективой заменить его на кран.
И вот только спустя полтора года — докупил второй кран и накрутил их.

В итоге мы наблюдаем странное и редкое явление(читать голосом Дроздова) — вся информация с сайта производителя подтвердилась. Причем описание своеобразное, как будто писали технари, а потом маркетинг полирнул для народа, но все равно мало кто поймет все фишки. Не хватает раздела на сайте — для интеграторов с техподробностями внутри. Даже на счет повышенного крутящего момента на старте не соврали — кран на старте бухтит движком в 1,5А и через 2-3 сек начинает уже гундеть в обычном (ток 0,7 А) режиме. На закрытие уходит секунд 25-30.

Еще из опыта: на счет крутящего момента — он избыточный для Мск, тут вода вполне ОК, за полтора года в 100мкм фильтре пара окалин и никакого зарастания. За большой крутящий момент приходится платить и ценой, и временем открытия, и местом в шкафу. Думаю тут обычных приводов хватит от Гидролока Ультимэйт, Нептуна или Аквасторожа. За два последних не поручусь — не разбирал, лет 5 назад у них были частично пластиковые шестерни, сейчас вроде это исправили.

Еще есть гидролок виннер с прямым подключением датчиков к приводу — это если вам не надо все что я наворотил. Там питание автономное от 4 батареек, а база похоже от ультимэйт привода. Вообще, он потенциально интересен и для контроллера самопального — питание 5 вольт, не надо две шины на 5 и 12 вольт городить и можно выбросить опторазвязку.

Датчики протечки

Купил датчики той же контроры WSU — универсальные. У них два выхода «открытый коллектор», один тянет на землю только при наличии воды, второй — если вода попала, то тянет на землю все время, пока питание не рубанешь. Только первый выход использую, остальная логика в контроллере, но похоже этот выход может пригодиться для каких более кондовых систем диспечеризации.

Провода в комплекте метра три где то. Цвет проводов — Адъ_и_Израиль. Зацените цитату:

красный (коричневый) провод (Vcc) питание от +5 до +30 вольт.
черный(белый) провод (OUT2)
зеленый провод (OUT1)
желтый провод (GND)

Вот что мешало сделать белый\черный землей? На приводе крана тоже кстати провода по цвету с логикой не але. Первый датчик стоит на кухне, под раковиной рядом с посудомойкой.

Строим безумный дом. ESP8266 — безумно

Второй в ванной в специальной водоотводной канаве. Когда делал стяжку — не довел ее до стены. Получился этакий зумпф для сбора воды с ванной и туалета.

Из опыта эксплуатации — ужу было одно ложное срабатывание датчика у посудомойки. Судя по логу на один цикл опроса (500мс) было замыкание, модифицировал код — смена состояния теперь происходит при 10 подряд одинаковых значениях с датчика.

Контакты датчика покрыты позолотой. У товарища подобные датчики уже несколько лет, окисления не замечено.

Датчики давления

Практически — показометры. Точность +- 0,5 атм меня полностью устроила. На основе датчиков приходит оповещении по отключению воды. Покупал на Али тут.

Датчики температуры

А почему бы и не добавить? Из полезного — сможет раз в год оповестить об отключении горячей воды. Используются банальные ds18b20.

Счетчики

Самые обычные Itelma, раз в 10 литров замыкают контакты. На стороне контроллера выход подтянут к + 3,3v, счетчик тянет его на землю.

Контроллер

Внутри

На базе Particle Photon, подробнее тут. Есть у них версия с 2G или 3G модулем (Electron). Первые прошивки был полный шлак, поморгать диодами ОК, но как только начинаешь чето сложное колбасить, играть с i2c и прерываниями может терять wifi. Сейчас жить можно. В принципе можно выкинуь датчики давления из схемы и замутить все на ESP8266 — дерзайте. Первым делом photon надо привязать к аккаунту particle (делается через App на мобиле или через консоль Particle CLI — пользую только второй метод) и прописать wifi-сеть. После привязки тут в разделе устройств появляется контроллер и его статус подключения к облаку.

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

Исходный код прошивки

// This #include statement was automatically added by the Particle IDE. #include «Adafruit_SSD1306/Adafruit_SSD1306.h» // This #include statement was automatically added by the Particle IDE. #include «MQTT/MQTT.h» // This #include statement was automatically added by the Particle IDE. #include «OneWire/OneWire.h» SYSTEM_THREAD(ENABLED); SYSTEM_MODE(MANUAL); STARTUP(WiFi.selectAntenna(ANT_EXTERNAL)); STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY)); struct counter_struct { float value; byte state; int pin; }; struct valve_struct { byte state; int pin; }; struct sensor_struct { int timeout; byte state; int pin; }; unsigned long currentMillis = 0; unsigned long previous_conected = 100000; //финт ушами unsigned long previous_wifi_uptime = 100000; //финт ушами unsigned long previous_counter_read = 0; //финт ушами unsigned long wifi_uptime; unsigned long start_temp_timer = 0; unsigned long read_temp_timer = 0; byte display_timeout = 0; //temp onewire OneWire ds0 = OneWire(D2); OneWire ds1 = OneWire(D3); byte addr0[8]; byte addr1[8]; bool presense0 = false; bool presense1 = false; byte data[12]; #define OLED_RESET A7 Adafruit_SSD1306 display(OLED_RESET); //valve control retained valve_struct valve[2] = { {0, D4}, {0, D5} }; //counter control retained counter_struct counter[2] = { {0, 1, A0}, {0, 1, A1} }; volatile int pressure[2] = {A2, A3}; #define SENSOR_TIMEOUT 10 volatile sensor_struct sensor[2] = { {0, 1, D6}, {0, 1, D7} }; void callback(char* topic, byte* payload, unsigned int length); byte server[] = { 192,168,2,101}; MQTT client(server, 1883, callback); bool publish_message(const char* t, const char* p, bool retain) { return client.publish(t, (uint8_t*)p, sizeof(p), retain); } bool publish_message(const char* t, int p, bool retain) { char buf_d[12]; int n = sprintf(buf_d,»%d»,p); return client.publish(t, (uint8_t*)buf_d, n, retain); } bool publish_message(const char* t, float p, bool retain) { //char buf_f[18]; String s(p, 4); // dtostrf(p, 9, 4, buf_f); //int n = sprintf(buf_f,»%f»,p); return client.publish(t, (uint8_t*)s.c_str(), s.length(), retain); } // recieve message void callback(char* topic, byte* payload, unsigned int length) { char p[length + 1]; memcpy(p, payload, length); p[length] = NULL; String message(p); String t(topic); if (t.equals(«home/water_count/spark/set»)) { if (message.equalsIgnoreCase(«1»)) { Particle.connect(); if (waitFor(Particle.connected, 10000)) {publish_message(«home/water_count/spark», 1, false);} else {Particle.disconnect(); publish_message(«home/water_count/spark», 0, false);} } else { Particle.disconnect(); publish_message(«home/water_count/spark», 0, false); } } else if (t.startsWith(«home/water_count/valve/»)) { int m = message.toInt(); int x = t.substring(23,24).toInt(); if (m > -1 && m < 2 && x > -1 && x <2) { set_valve(x, m); } else { publish_message(«home/water_count/valve/» + t.substring(23,24), valve[x].state , true); } } else if (t.startsWith(«home/water_count/counter/»)) { float m = message.toFloat(); int x = t.substring(25,26).toInt(); if (m > -1 && m <= 999999 && x > -1 && x <2) { counter[x].value = m; } publish_message(«home/water_count/counter/» + t.substring(25,26), counter[x].value , true); } } void setup() { //Serial.begin(9600); WiFi.on(); WiFi.connect(); if (waitFor(WiFi.ready, 5000)) {mqtt_connect();} for (int i=0; i < 2; i++) { pinMode(valve[i].pin, OUTPUT); digitalWrite(valve[i].pin, valve[i].state); pinMode(counter[i].pin, INPUT); pinMode(sensor[i].pin, INPUT); counter[i].state = digitalRead(counter[i].pin); pinMode(pressure[i], AN_INPUT); } pinMode(A4, INPUT_PULLUP); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C (for the 128×64) display.clearDisplay(); // clears the screen and buffer //Particle.connect(); } void loop() { currentMillis = millis(); // проверяем наличие сети и подключения к MQTT брокеру if (currentMillis — previous_conected >= 30000 || previous_conected > currentMillis) { previous_conected = currentMillis; if (!client.isConnected() & wifi_uptime > 60) { mqtt_connect(); } publish_message(«home/water_count/rssi», WiFi.RSSI(), true); } if (currentMillis — previous_wifi_uptime >= 1000 || previous_wifi_uptime > currentMillis) { previous_wifi_uptime = currentMillis; WiFi.ready() ? wifi_uptime++ : wifi_uptime = 0; //work with button and display int fg = digitalRead(A4); if (display_timeout > 0) { display_timeout -= 1; if (display_timeout == 0) { display.clearDisplay(); display.display(); } } if (fg == 0) { if (display_timeout == 0) { display.clearDisplay(); // clears the screen and buffer display.setTextSize(2); display.setTextColor(WHITE); display.setCursor(0,0); display.print(«C=»); display.println(counter[0].value, 4); display.setCursor(0,16); display.print(«H=»); display.println(counter[1].value, 4); display.setCursor(0,32); display.print(«Valve=»); display.print(valve[0].state); display.print(«|»); display.println(valve[1].state); display.setCursor(0,48); display.print(«Sensor=»); display.print(sensor[0].state); display.print(«|»); display.println(sensor[1].state); display.display(); } display_timeout = 10; } } //counter check if (currentMillis — previous_counter_read >= 500 || previous_counter_read > currentMillis) { previous_counter_read = currentMillis; for (int i=0; i < 2; i++) { byte count_state = digitalRead(counter[i].pin); if (count_state != counter[i].state) { counter[i].state = count_state; if (count_state == 0) { counter[i].value += 0.01; char buf18[30]; sprintf(buf18,»home/water_count/counter/%d», i); publish_message(buf18 , counter[i].value, true); } } // работаем с датчиком протечки byte sensor_state = digitalRead(sensor[i].pin); if (sensor_state != sensor[i].state) // { sensor[i].state = sensor_state; sensor[i].timeout = SENSOR_TIMEOUT; } if (sensor[i].timeout > 0) { sensor[i].timeout -= 1; if (sensor[i].timeout == 0) { char buf18[30]; sprintf(buf18,»home/water_count/sensor/%d», i); publish_message(buf18 , sensor[i].state, true); if (sensor[i].state == 0) { set_valve(0, 1); //close both valve set_valve(1, 1); //close both valve } } } } } // temp onewire if (currentMillis — start_temp_timer >= 299000 || start_temp_timer > currentMillis) { //стартуем расчет start_temp_timer = currentMillis; presense0 = start_temp0(); presense1 = start_temp1(); } if (currentMillis — read_temp_timer >= 300000 || read_temp_timer > currentMillis) {//считывваем темп read_temp_timer = currentMillis; start_temp_timer = currentMillis; if (presense0) read_temp0(); if (presense1) read_temp1(); //preasure calc and send char buf18[30]; for (int i=0; i < 2; i++) { sprintf(buf18,»home/water_count/pressure/%d», i); float read_val = analogRead(pressure[i]); float value = (read_val — 600.0) / 300.0 ; publish_message(buf18 , value, false); } } //Particle.process(); client.loop(); } void mqtt_connect() { if (client.connect(«water_count»)) { //подпись на spark и публикуем послднее состояние client.subscribe(«home/water_count/spark/set»); publish_message(«home/water_count/spark», Particle.connected() ? 1 : 0, true); client.subscribe(«home/water_count/valve/+/set»); client.subscribe(«home/water_count/counter/+/set»); } } bool start_temp0() { if ( !ds0.search(addr0)) { ds0.reset_search(); return false;} ds0.reset_search(); if (OneWire::crc8(addr0, 7) != addr0[7]) { return false;} ds0.reset(); ds0.select(addr0); ds0.write(0x44, 0); return true; } bool start_temp1() { if ( !ds1.search(addr1)) { ds1.reset_search(); return false;} ds1.reset_search(); if (OneWire::crc8(addr1, 7) != addr1[7]) { return false;} ds1.reset(); ds1.select(addr1); ds1.write(0x44, 0); return true; } bool read_temp0() { //delay(1000); ds0.reset(); ds0.select(addr0); ds0.write(0xBE, 0); for (int i = 0; i < 9; i++) { data[i] = ds0.read(); } int16_t raw = (data[1] << 8) | data[0]; float celsius = (float)raw * 0.0625; if (celsius < 0 || celsius > 100) return false; publish_message(«home/water_count/temp/0», celsius, false); //Serial.println(celsius); ds0.reset_search(); return true; } bool read_temp1() { //delay(1000); ds1.reset(); ds1.select(addr1); ds1.write(0xBE, 0); for (int i = 0; i < 9; i++) { data[i] = ds1.read(); } int16_t raw = (data[1] << 8) | data[0]; float celsius = (float)raw * 0.0625; if (celsius < 0 || celsius > 100) return false; publish_message(«home/water_count/temp/1″, celsius, false); //Serial.println(celsius); ds1.reset_search(); return true; } void set_valve(int vlv, byte state) { valve[vlv].state = state; digitalWrite(valve[vlv].pin, state); char buf26[26]; sprintf(buf26,»home/water_count/valve/%d», vlv); publish_message(buf26 , state , true); }

Через MQTT подключаемся к брокеру. Мониторим датчики и шлем в соответствующие ветки mqtt события и значения. Например home/water_count/valve/0 — привод хол воды. home/water_count/counter/0 — показания счетчика хол воды.

Подписываемся на команды изменения состояния привода и установки текущего значения счетчика (холодной и горячей воды ):

client.subscribe(«home/water_count/valve/+/set»); client.subscribe(«home/water_count/counter/+/set»);

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

STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));

Это интересная пограммно-аппаратная фишка контроллера stm, в reference Particle называют ее BackupSRAM. У Photon есть вывод vbat — это не батарейное питание и не зарядка. Пока есть напряжение на этой ноге, содержимое 4кбайт SRAM сохраняется при полной обесточенности контроллера. Таким образом отпадает проблема износа EEPROM.

В коде переменные, которые надо загнать в эту память объявляют с указанием: retained. Аппаратно я реализовал подпитку от суперконденсатора на 1,5F. По даташиту память сдохнет на 1,6v, по моим стендовым опытам на протоборде это настанет через 2 недели примерно с моим конденсатором. Логика закрытия кранов при срабатывании датчиков «автономна» и не зависит от подключения к openhab. Есть 3 ходовый переключатель прямого управления приводами — автоматика, OFF (открытые краны), Close (закрываем).

Схема платы ниже:

Проект Eagle вместе с кастомными либами можно скачать тут.

Плата делалась ЛУТ, в дорожках не мельчил.

Водяная баня лучшый друг ЛУТ

Блок питания. Нам надо и 12 и 5 вольт. Донор ищется на ebay по строке: «hard drive power adapter 5v 12v», типа такого.

Корпус

Распечатывался пластиком PLA на 3d принтере (Tarantula Tevo). Сопло 0.4мм, слой 0,25мм. Крышка является заодно и базой для крепления платы контроллера. База с блоком питания крепится к стене. База с крышкой не скрепляются винтами, хватает натяжения крышки (как у бабушки крышки на банках с вареньем) и работает слоистая структура стенок.

3D модель в архиве.

CS-CS.Net

Вот как это все выглядить смонтированное на водоразводке.

Openhab

Развернут на Orange Pi One под Armbian.

Конфиг Item

Без картинок — если оставить теги картинок, Хабр ломается. Number watercount_temp1 «T cool [%.1f °C]» (gWaterCount) { mqtt=»<[mqtt_bro:home/water_count/temp1:state:default]» } Number watercount_temp2 «T hot [%.1f °C]» (gWaterCount) { mqtt=»<[mqtt_bro:home/water_count/temp2:state:default]» } Number watercount_count0 «Count cool [%.2f М³]» (gWaterCount) { mqtt=»<[mqtt_bro:home/water_count/counter/0:state:default]» } Number watercount_count1 «Count hot [%.2f М³]» (gWaterCount) { mqtt=»<[mqtt_bro:home/water_count/counter/1:state:default]» } Number watercount_pressure0 «P cool [%.2f Атм.]» (gWaterCount) { mqtt=»<[mqtt_bro:home/water_count/pressure/0:state:default]» } Number watercount_pressure1 «P hot [%.2f Атм.]» (gWaterCount) { mqtt=»<[mqtt_bro:home/water_count/pressure/1:state:default]» } Number watercount_sensor0 «Sensor0 is [MAP(water_sensor.map):%s]» (gWaterCount) { mqtt=»<[mqtt_bro:home/water_count/sensor/0:state:default]» } Number watercount_sensor1 «Sensor1 is [MAP(water_sensor.map):%s]» (gWaterCount) { mqtt=»<[mqtt_bro:home/water_count/sensor/1:state:default]» } Number watercount_valve0 «Valve cool» (gWaterCount) { mqtt=»<[mqtt_bro:home/water_count/valve/0:state:default], >[mqtt_bro:home/water_count/valve/0/set:command:*:default]» } Number watercount_valve1 «Valve hot» (gWaterCount) { mqtt=»<[mqtt_bro:home/water_count/valve/1:state:default], >[mqtt_bro:home/water_count/valve/1/set:command:*:default]» } String watercount_sendStr «LastVol:[%s]» (gWaterCount) Number watercount_sendCool «Send cool [%.2f М³]» (gWaterCount) Number watercount_sendHot «Send hot [%.2f М³]» (gWaterCount) Number watercount_sendSwitch «Autosend» (gWaterCount) Number watercount_rssi «WaterCount [%d dB]» (gSpark_RSSI) { mqtt=»<[mqtt_bro:home/water_count/rssi:state:default]» } Number watercount_spark_state «WaterCount Spark» (gSpark) { mqtt=»<[mqtt_bro:home/water_count/spark:state:default], >[mqtt_bro:home/water_count/spark/set:command:*:default]» }

Нужно небольшое правило трансформации для показаний датчика протечки.

Конфиги transform

transform\water_sensor.map
1=dry
0=wet
undefined=undefined

Формируем страницу для управления:

Конфиги Sitemap

Sitemap
Text label=«Водоподготовка» icon=«water»
{
Frame
{
Text item=watercount_temp1
Text item=watercount_count0
Text item=watercount_pressure0
Switch item=watercount_valve0 mappings=[1=«Close», 0=«Open»]
}
Frame
{
Text item=watercount_temp2
Text item=watercount_count1
Text item=watercount_pressure1
Switch item=watercount_valve1 mappings=[1=«Close», 0=«Open»]
}
Frame
{
Text item=watercount_sensor0

CS-CS.Net

Text item=watercount_sensor1
}
Frame
{
Switch item=watercount_sendSwitch mappings=[0=«OFF», 1=«ON»]
Text item=watercount_sendStr
Text item=watercount_sendCool
Text item=watercount_sendHot
}
}

И правила обработки:

Конфиги Rules

rule «Check watercount_sensor0» when Item watercount_sensor0 received update then if ((watercount_sensor0.state as DecimalType) == 1) { if ((watercount_sensor0.historicState(now.minusSeconds(3)).state as DecimalType) == 1) { sendTelegram(«****_bot», «Sensor0 was wet less than 5 seconds») } else { sendTelegram(«****_bot», «Sensor0 become dry») } } else { if ((watercount_sensor0.historicState(now.minusSeconds(3)).state as DecimalType) == 0) { sendTelegram(«****_bot», «Sensor0 was dry less than 5 seconds»); } else { sendTelegram(«****_bot», «Sensor0 become wet! Valves will be closed!») } } end rule «Check watercount_sensor1» when Item watercount_sensor1 received update then if ((watercount_sensor1.state as DecimalType) == 1) { if ((watercount_sensor1.historicState(now.minusSeconds(3)).state as DecimalType) == 1) { sendTelegram(«****_bot», «Sensor1 was wet less than 5 seconds») } else { sendTelegram(«****_bot», «Sensor1 become dry») } } else { if ((watercount_sensor1.historicState(now.minusSeconds(3)).state as DecimalType) == 0) { sendTelegram(«****_bot», «Sensor1 was dry less than 5 seconds»); } else { sendTelegram(«****_bot», «Sensor1 become wet! Valves will be closed!») } } end rule «Check watercount_temp2» when Item watercount_temp2 received update then if ((watercount_temp2.state as DecimalType) < 37 ) { sendTelegram(«****_bot», String::format(«Hot water temp drop to %s», watercount_temp2.state.toString)); } end rule «Check watercount_pressure0» when Item watercount_pressure0 received update then if ((watercount_pressure0.state as DecimalType) < 1 && (watercount_pressure0.historicState(now.minusSeconds(3)).state as DecimalType) >= 1) { sendTelegram(«****_bot», String::format(«Cool pressure drop to %s», watercount_pressure0.state.toString)); } if ((watercount_pressure0.state as DecimalType) > 1 && (watercount_pressure0.historicState(now.minusSeconds(3)).state as DecimalType) <= 1) { sendTelegram(«****_bot», String::format(«Cool pressure rise to %s», watercount_pressure0.state.toString)); } end rule «Check watercount_pressure1» when Item watercount_pressure1 received update then if ((watercount_pressure1.state as DecimalType) < 1 && (watercount_pressure1.historicState(now.minusSeconds(3)).state as DecimalType) >= 1) { sendTelegram(«****_bot», String::format(«Hot pressure drop to %s», watercount_pressure1.state.toString)); } if ((watercount_pressure1.state as DecimalType) > 1 && (watercount_pressure1.historicState(now.minusSeconds(3)).state as DecimalType) <= 1) { sendTelegram(«****_bot», String::format(«Hot pressure rise to %s», watercount_pressure1.state.toString)); } end rule «Generate send string counters» //every 24 day of mounth in 00.01 minutes when Time cron «0 0 1 24 1/1 ?» then var float deltaCool = (watercount_count0.state as DecimalType).floatValue() — (watercount_sendCool.state as DecimalType).floatValue() var float deltaHot = (watercount_count1.state as DecimalType).floatValue() — (watercount_sendHot.state as DecimalType).floatValue() if (deltaCool >= 0 && deltaHot >= 0) { watercount_sendStr.postUpdate(String::format(» %.2f / %.2f м3″, deltaCool, deltaHot)) watercount_sendCool.state = watercount_count0.state watercount_sendHot.state = watercount_count1.state sendTelegram(«****_bot», String::format(«Лизюкова 23, корп 5, кв. 23. Счетчик №2560097 (хол.вода) = %.2f м3. Cчетчик №2538996 (гор.вода) = %.2f м3. %s», (watercount_sendCool.state as DecimalType).floatValue(), (watercount_sendHot.state as DecimalType).floatValue(), watercount_sendStr.state.toString())) } else { watercount_sendSwitch.postUpdate(0) sendTelegram(«****_bot», «Current counters value less than sended last time. Turn off autosend.») } end rule «Send string counters» when Time cron «0 0 23 24 1/1 ?» then if (watercount_sendSwitch.state == 1) { sendMail(«uk@uk.ru», «Лизюкова 23, корп 5, кв. 23», String::format(«Лизюкова 23, корп 5, кв. 23. Счетчик №2560097 (хол.вода) = %.2f м3. Cчетчик №2538996 (гор.вода) = %.2f м3», (watercount_sendCool.state as DecimalType).floatValue(), (watercount_sendHot.state as DecimalType).floatValue())); sendTelegram(«****_bot», «Send email with watercount values»); } else { sendTelegram(«****_bot», «Can’t send email with watercount values — autosend is OFF.»); } end rule «Rotate valves» when Time cron «0 0 05 25 1/1 ?» then if (watercount_valve0.state == 0 && watercount_valve1.state == 0) { watercount_valve0.postUpdate(1) Thread::sleep(1000) watercount_valve1.postUpdate(1) Thread::sleep(1000) watercount_valve0.postUpdate(0) Thread::sleep(1000) watercount_valve1.postUpdate(0) sendTelegram(«****_bot», «Valves was rotated.»); } else { sendTelegram(«****_bot», «Can’t rotate valves, it’s closed.»); } end

Для отправки сообщений не пользуюсь встроенным функционалом андроид приложения openhab, как и иинтеграцией с их облаком. Мне по душе бот Телеграмм. Как настроить и подлючить бота можно подсмотреть на wiki. Для отправки писем с почтового ящеика gmail, если у вас двухфактораная аутентификация, надо включить разовый пароль для почтового приложения и прописать именно этот пороль в конфиге openhab.

Пройдусь по правилам.

Check watercount_sensor — контроллер отправляет новые значения сенсора протечки только при смене значения или если было ложное срабатывание (менее 10 циклов). Анализируем пришедшее и историческое значение, формируем информационные сообщения. Есть нюанс — попытка получить prevoiusItem постоянно отдает текущее значение, решения не нашел — беру значение «-3 сек», если кто поборол — отпишите в коменты или в личку.

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

Check watercount_pressure — анализируем текущее и предыдущее значение, реагируем сообщением на падение ниже 1 атм и росту выше нее.

Generate send string counters — стартует по cron 24 числа каждого месяца в 1 час ночи. Проверяем, что значения сейчас больше отправленных в прошлый раз. Если меньше — выключаем автоотправку и формируем оповещение. Если ОК — запоминаем значения счетчиков для отправки в УК, отправляем в телеграмм будущее тело письма. Заодно в watercount_sendStr сохраняем, сколько мы потребили за прошлый месяц.

Generate send string counters — стартует по cron 24 числа в 23.00. Проверяет включена ли автоотправка, если вкл — шлем на почту кправляющей компании значения счетчиков. Получается у меня есть 24 числа весь день, что то исправить или просто вырубить автоотправку, если в телеграм пришла ошибка.

Update by comment. Rotate valves — правило для закрытия\открытия крана раз в месяц против закипания. 25 числа в 5 утра — что бы не попасть на работу посудомойки или стиралки, но даже если и попадет — не критично, перекрытие воды будет около 3-4 секунд.

И только тут начинается умный дом
Объединение систем в единой точке (openhab) позволяет строить логику, не доступную набору автономных систем. Например: пришло событие увеличения счетчика воды — система безопасности активна, замки входной двери закрыты, потребление электроэнергии посудомойкой и стиралкой менее 5 Вт — значит зафиксирована протечка мимо датчиков. Формируем команду на закрытие кранов, отправляем сообщение боту в Телеграмм. Но этом как нить потом.

Почему часто выходит из строя датчик холла?


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

Одной из наиболее распространенных причин выхода из строя датчика Холла является его механическое повреждение. Например, если датчик Холла был подвержен ударам, вибрации или другим физическим воздействиям, он может выйти из строя. Кроме того, датчик Холла может выйти из строя из-за воздействия магнитного поля или электромагнитных помех.

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

Кроме того, из-за того, что датчик Холла является электронным устройством, он может выйти из строя из-за неполадок в электронике, таких как короткое замыкание или перегрев. В этом случае, для восстановления работоспособности датчика Холла, может потребоваться его замена или ремонт.

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

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