Беспроводной контроллер бытового кондиционера в OpenHAB по Modbus через RF24Network
После первой моей статьи про управление кондиционером с помощью контроллера прошло чуть больше 2х лет. За это время идея управлять кондиционером удалённо меня не оставляла и имела несколько перерождений. Главным условием было отсутствие каких-либо проводов до кондиционера.
То есть управление контроллером должно быть беспроводным.
Предыстория
Первым прототипом была Arduino UNO. Команды она принимала по UART и умела включать и выключать кондиционер. Т.к. практического смысла от подключенной к рабочему компьютеру ардуинки было мало, голова все время искала возможность подключить последнюю к домашнему серверу. Прямой видимости от сервера до виновника всех головоломок не было. Максимум это розетка с локалкой все у того же рабочего компа — благо он стоит почти напротив кондиционера. Ethernet-шилда в наличии не было. Но вспомнив что где-то в загашнике валяется не используемый уже давно dsl-модем D-link DSL-2500U как раз с одним портом на борту. Желание дать вторую жизнь железке подтолкнуло к гуглению, которое, в свою очередь, чудесным образом вывело на статью Превращаем ADSL-модем в Ethernet-шилд для Arduino/CraftDuino.
Забегая вперед и пропуская интереснейший процесс создания кастомной прошивки мне-таки удалось заставить модем слушать на нужном порту и «пробросить» через него UART. Таким образом я мог на домашнем сервере отправить команду на включение/выключение в порт на локальный адрес модема, который отправится на подключенную к нему ардуинку.
Но эта статья не об этом. Конечное решение использует протокол Modbus и беспроводную сеть RF24Network. А управляется все в OpenHAB.
За эти два года я успел заказать с поднебесной много всяких ништяков и большинство из них ждало своего часа. В этом списке был модуль NRF24L01+, купленный горстью для экспериментов. И лежал бы он дальше где-то в шкафу без дела, если бы однажды я не наткнулся на серию статей:
от Borich, за что ему спасибо!
Благодаря им я познакомился с протоколом Modbus. Но самое главное я открыл для себя OpenHAB — то, что я давно искал. Это меня и подтолкнуло приступить к реализации давней идей.
Поигравшись с примерами из статей я решил попробовать разнести контроллер и сервер используя вышеописанный ethernet-shield из модема. Такое решение позволяло достичь послтавленной задачи — шилд и контроллер располагались на рабочем столе, при этом не используя рабочий компьютер, в то же время шилд всегда подключен к локальной сети и доступен с домашнего сервера.
Нужно настроить подключение в OpenHAB. Но, к сожалению, Modbus Binding не умеет «RTU over TCP». На тот момент меня это не остановило — решено было допилить библиотеку ModbusRtu для ардуино c целью изменить формат пакета на TCP. Отличия оказались совсем небольшие и вот контроллер уже работает в связке OpenHAB-(ethernet)-модем-(UART)-контроллер.
Казалось бы, что еще нужно? Но проблема крылась в количестве Item в конфигурации. При тестировании я настроил только 1 Item и его опрос осуществлялся успешно. Но стоило добавить еще несколько — сразу же появлялись ошибки при передаче. Проблема, как мне казалось, была в ассинхронности TCP коннектора со стороны Modbus Binding. Т.е. мой шилд из модема не был рассчитан на несколько одновременных подключений и данные смешивались.
Мои попытки доработать Modbus Binding для возможности синхронизировать опросы Item'ов с одинаковым host-port-slaveID не привели к улучшению ситуации.
Решающим фактором поставить крест на таком решении стало то, что оно не было масштабируемым. При желании добавить в общую систему еще один контроллер потребует еще один такой шилд из модема. Да и беспроводностью здесь и не пахло, хотя удовлетворяло условиям задачи.
Идея
И вот тогда я окончательно утвердился в том, что нужно делать контроллер, доступный по воздуху. Не пропадать же модулям NRF24L01+!.. Правда это требовало как минимум два контроллера — один выполняет непосредственную роль, второй — роль маршрутизатора. Второй должен быть подключен к серверу и именно через него осуществяется беспроводная связь с остальными. Подключаясь к нему мы указываем ID подчиненного, которому предназначен пакет. Получается на одном последовательном порту доступно множество подчиненных устройств. Да — это беспроводная сеть на базе Modbus.
Modbus как нельзя кстати позволял строить сеть один мастер — много подчиненных. А библиотека RF24Network — сделать все беспроводным, да еще и с автоматической маршрутизацией между узлами сети.
Разработка библиотеки для Arduino
Для реализации такого решения потребовалось совсем немного доработать библиотеку Modbus-Master-Slave-for-Arduino для возможности пронаследоваться от нее и перегрузить пару методов. Моя реализация также обновлена(добавлен library.properties, файл библиотеки разбит на заголовок и тело) для последней на текущий момент Arduino IDE 1.6.5.
Библиотека Modbus-over-RF24Network-for-Arduino позволяет реализовать два возможных поведения — Proxy и Slave. Пример ModbusRF24Proxy фактически является реализацией «маршрутизатора» и не требует никаких доработок, кроме настроек нужных пинов.
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#include <ModbusRtu.h>
#include <ModbusRtuRF24.h>
#define stlPin 13 // номер выхода индикатора работы (расположен на плате Arduino)
// nRF24L01(+) radio attached using Getting Started board
RF24 radio(9, 10);
// Network uses that radio
RF24Network network(radio);
// Address of our node
const uint16_t this_node = 0;
//Задаём последовательный порт, выход управления TX
ModbusRF24 proxy(network, 0, 0);
int8_t state = 0;
unsigned long tempus;
void setup() {
// настраиваем входы и выходы
io_setup();
// настраиваем последовательный порт ведомого
proxy.begin(57600);
SPI.begin();
radio.begin();
network.begin(/*channel*/ 90, /*node address*/ this_node);
// зажигаем светодиод на 100 мс
tempus = millis() + 100;
digitalWrite(stlPin, HIGH);
}
void io_setup() {
digitalWrite(stlPin, HIGH);
pinMode(stlPin, OUTPUT);
}
void loop() {
// Pump the network regularly
network.update();
// обработка сообщений
state = proxy.proxy();
// если получили пакет без ошибок - зажигаем светодиод на 50 мс
if (state > 4) {
tempus = millis() + 50;
digitalWrite(stlPin, HIGH);
}
if (millis() > tempus) digitalWrite(stlPin, LOW);
}
Маршрутизатор или прокси, использует особый формат конструктора:
//Задаём последовательный порт, выход управления TX
ModbusRF24 proxy(network, 0, 0);
и функцию
proxy.proxy();
для обработки входящих пакетов по последовательному порту, отправки их в сеть RF24Network, получения ответа из сети, отправки результата обратно в последовательный порт.
В данной реализации прокси имеет RF24Network-адрес равный нулю:
// Address of our node
const uint16_t this_node = 0;
— т.е. это корневое устройство сети. При необходимости, положение в топологии контроллера-прокси можно изменить.
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#include <ModbusRtu.h>
#include <ModbusRtuRF24.h>
#define ID 1 // адрес ведомого
#define btnPin 2 // номер входа, подключенный к кнопке
#define ledPin 7 // номер выхода светодиода
// nRF24L01(+) radio attached using Getting Started board
RF24 radio(9, 10);
// Network uses that radio
RF24Network network(radio);
// Address of our node
const uint16_t this_node = ID;
//Задаём ведомому адрес
ModbusRF24 slave(network, ID);
// массив данных modbus
uint16_t au16data[11];
void io_setup() {
digitalWrite(ledPin, LOW);
pinMode(ledPin, OUTPUT);
pinMode(btnPin, INPUT);
}
void io_poll() {
//Копируем Coil[1] в Discrete[0]
au16data[0] = au16data[1];
//Выводим значение регистра 1.3 на светодиод
digitalWrite(ledPin, bitRead(au16data[1], 3));
//Сохраняем состояние кнопки в регистр 0.3
bitWrite(au16data[0], 3, digitalRead(btnPin));
//Копируем Holding[5,6,7] в Input[2,3,4]
au16data[2] = au16data[5];
au16data[3] = au16data[6];
au16data[4] = au16data[7];
//Сохраняем в регистры отладочную информацию
au16data[8] = slave.getInCnt();
au16data[9] = slave.getOutCnt();
au16data[10] = slave.getErrCnt();
}
void setup() {
// настраиваем входы и выходы
io_setup();
Serial.begin(57600);
Serial.println("RF24Network/examples/modbus_slave/");
SPI.begin();
radio.begin();
network.begin(/*channel*/ 90, /*node address*/ this_node);
}
void loop() {
// Pump the network regularly
network.update();
if (network.available()) {
slave.poll(au16data, 11);
}
//обновляем данные в регистрах Modbus и в пользовательской программе
io_poll();
}
Конструктор в этом случае уже другой:
//Задаём ведомому адрес
ModbusRF24 slave(network, ID);
Структура регистров Modbus
После первых удачных попыток управления кондиционером с возможностью только включить и выключить мой аппетит только увеличивался. Теперь уже этого было мало и раз в руках у меня функционал OpenHAB в мобильном приложении, то делать он должен как миминум весь функционал родного пульта управления.
Это значит вот такой список возможностей как минимум:
- индикация текущего состояния
- включение и выключение кондиционера, отдельных режимов(O2, ионизация, тихий режим);
- выбор текущего режима(авто, обогрев, охлаждение, осушение, вентилятор);
- указание температуры и скорости вентилятора для каждого режима. Для режима вентилятора только скорость;
- настройка вертикальной шторки(авто, 0°, 15°, 30°, 45°, 60°);
- настройка горизонтальной шторки(авто, "| |", "/ /", "/ |", "| \", "\ \" );
- настройка времени(час, минута);
- настройка таймера включения;
- настройка таймера выключения;
- настройка времени включения(час, минута);
- настройка времени выключения(час, минута);
В процессе разработки структура регистров менялась у меня много раз. Текуща версия ниже под спойлером.
Type | Byte | Bit | Name | Описание |
---|---|---|---|---|
Bit RO | 0 | 0 | CFG_OXYGEN | 1 — Есть кислородный режим |
Bit RO | 1 | CFG_ION | 1 — Есть режим ионизации | |
Bit RO | 2 | CFG_QUIET | 1 — Есть тихий режим | |
Bit RO | 3 | CFG_TIMER | 1 — Есть вкл/выкл по расписанию времени | |
Bit RO | 4 | CFG_DELAY | 1 — Есть отложенный вкл/выкл | |
Bit RO | 5 | CFG_SWING | 1 — Есть управление шторкой | |
Bit RO | 6 | CFG_SWINGH | 1 — Есть управление горизонтальной шторкой | |
Bit RO | 7 | CFG_SWINGV | 1 — Есть управление вертикальной шторкой | |
Bit RO | 8 | CFG_CLOCK | 1 — Есть часы | |
Bit RO | 9 | |||
Bit RO | 10 | |||
Bit RO | 11 | CFG_AUTO | 1 — Есть режим AUTO | |
Bit RO | 12 | CFG_COOL | 1 — Есть режим COOL | |
Bit RO | 13 | CFG_HEAT | 1 — Есть режим HEAT | |
Bit RO | 14 | CFG_DRY | 1 — Есть режим DRY | |
Bit RO | 15 | CFG_FAN | 1 — Есть режим FAN | |
Integer RO | 1 | CFG_TEMP | Мин. и макс. температуры: Min + Max*256 | |
Integer RO | 2 | CFG_FAN_SPEED | Макс скорость FAN | |
Bit RO | 3 | 0 | STATE_POWER | Кондиционер:0 — выкл, 1 — вкл |
Bit RO | 1 | STATE_OXYGEN | Кислород:0 — выкл, 1 — вкл | |
Bit RO | 2 | STATE_ION | Ионизация:0 — выкл, 1 — вкл | |
Bit RO | 3 | STATE_QUIET | Тихий:0 — выкл, 1 — вкл | |
Bit RO | 4 | STATE_TIMER | Таймер:0 — выкл, 1 — вкл | |
Bit RW | 8 | CONTROL_POWER | ||
Bit RW | 9 | CONTROL_OXYGEN | ||
Bit RW | 10 | CONTROL_ION | ||
Bit RW | 11 | CONTROL_QUIET | ||
Integer RO | 4 | RTC_HR_MI | 0x1308 | |
Integer RW | 5 | RTCW_HR_MI | 0x1308 | |
Integer RO | 6 | TEMPERATURE1 | Температура окружающая. INT16, сотые доли градуса | |
Integer RO | 7 | TEMPERATURE2 | Температура сопла. INT16, сотые доли градуса | |
Bit RW | 8 | 0 | MODE_AUTO | |
Bit RW | 1 | MODE_COOL | ||
Bit RW | 2 | MODE_HEAT | ||
Bit RW | 3 | MODE_DRY | ||
Bit RW | 4 | MODE_FAN | ||
Integer RW | 9 | TEMP_AUTO | Температура AUTO | |
Integer RW | 10 | TEMP_COOL | Температура COOL | |
Integer RW | 11 | TEMP_HEAT | Температура HEAT | |
Integer RW | 12 | TEMP_DRY | Температура DRY | |
Integer RW | 13 | FAN_AUTO | Скорость AUTO. 0: Auto | |
Integer RW | 14 | FAN_COOL | Скорость COOL. 0: Auto | |
Integer RW | 15 | FAN_HEAT | Скорость HEAT. 0: Auto | |
Integer RW | 16 | FAN_DRY | Скорость DRY. 0: Auto | |
Integer RW | 17 | FAN_SPEED | Скорость FAN. 0: Auto | |
Bit RW | 18 | 0 | SWING_AUTO | Автовращение вертикальное |
Bit RW | 1 | SWINGV_0 | Вертикальная шторка угол 0° | |
Bit RW | 2 | SWINGV_15 | Вертикальная шторка угол 15° | |
Bit RW | 3 | SWINGV_30 | Вертикальная шторка угол 30° | |
Bit RW | 4 | SWINGV_45 | Вертикальная шторка угол 45° | |
Bit RW | 5 | SWINGV_60 | Вертикальная шторка угол 60° | |
Bit RW | 19 | 0 | SWINGH_AUTO | Автовращение горизонтальное |
Bit RW | 1 | SWINGH_VV | Горизонтальная шторка | | | |
Bit RW | 2 | SWINGH_LL | Горизонтальная шторка / / | |
Bit RW | 3 | SWINGH_LV | Горизонтальная шторка / | | |
Bit RW | 4 | SWINGH_VR | Горизонтальная шторка | \ | |
Bit RW | 5 | SWINGH_RR | Горизонтальная шторка \ \ | |
Bit RW | 20 | 0 | TIMER_ON | |
Bit RW | 1 | TIMER_OFF | ||
Integer RW | 21 | TIME_ON_HOUR | Час включения | |
Integer RW | 22 | TIME_ON_MINUTE | Минута включения | |
Integer RW | 23 | TIME_OFF_HOUR | Час выключения | |
Integer RW | 24 | TIME_OFF_MINUTE | Минута выключения | |
Integer RW | 25 | DS18B20_ENV | Адрес. Окружение | |
Integer RW | 26 | DS18B20_NOZ | Адрес. Сопло |
Регистры организованы таким образом, что весь массив данных инициализируется из EEPROM при старте контроллера.
Первые 3 регистра(CFG_*) сожержат конфигурацию возможностей, никогда не меняются и инициализируются прошивкой EEPROM.
Регистры 3-7 всегда отображают текущее состояние контроллера. Старшие биты регистра 3 используются для смены состояния. Их изменения инициируют включение/выключение кондиционера и специальных режимов. После исполнения команды, значения младших бит этого регистра копируются в старшие.
Регистр 4 содержит текущее время контроллера, которое читается из RTC. Значение сохраняется в BCD формате, чтобы при отображении регистра в шестнадцатеричном исчислении время читалось как есть — 12:34 это 0x1234.
Регистр 5 используется для смены времени RTC.
Регистры 6-7 содержат температуру с датчиков DS18B20. Значение содержит знаковое целое и равно T*100, т.е. 25.67°С=2567. Максимум предусмотрено 2 датчика, но кол-во можно легко изменить расширив таблицу регистров для хранения адресов датчиков и их температур.
В регистрах 25-26 хранятся последние 2 байта адреса датчиков. При смене датчиков нужно обнулить соответствующий регистр адреса. При обнаружении нового датчика его адрес проверяется на наличие в регистрах 25-26. Если адрес присутствует в таблице, значение температуры датчика заносится в соотв регистр 6-7. Если адреса нет в таблице и в таблице есть нулевые ячейки, текущий адрес датчика прописывается в свободной ячейке.
Регистры 8-26 при изменении пользователем сохраняются в EEPROM.
Железки
Аппаратная часть состоит из следующих компонент:
- Arduino Pro Mini.
Китайский вариант
- NRF24L01+ — беспроводной модуль 2.4ГГц
- LM1117-3.3 — стабилизатор 3.3В для NRF24L01+
- DS1302 — RTC
- кварц 32768кГц для RTC
- DS18B20 — датчик температуры. 2шт
- Мелочевка — оптопары, резисторы, конденсатор
Обратная связь с кондиционером реализована подключением к соответсвующим светодиодам оптопар. Таким образом обеспечена гальваническая развязка с электросхемой кондиционера. Ток входов для оптопар был экспериментально подобран с помощью сопротивлений таким образом, чтобы и выход оптопары открывался и яркость основного светодиода на кондиционере не падала.
ИК-светодиод был установлен рядом с ИК-приемником кондиционера.
Питание контроллера осуществляется от mini-USB зарядки от какого-то китайского чуда. Из корпуса зарядки были демонтированы китайские контакты сетевого напряжения, выведены провода. Сама зарядка поселилась в недрах корпуса кондиционера, подключена на вход 220 параллельно с ним. Контроллер подключается к зарядке обычным кабелем USB A-B.
Контроллер «маршрутизатора» построен на базе Arduino Mega2560 и NRF24L01+ с отдельным LM1117-3.3. Кроме отдельного питания 3.3 к беспроводному модулю подключен электролит(у меня нашелся на 470мкф*16в) на ноги питания. Как известно внутренняя шина питания меги2560 на 3.3в очень шумная и модуль отказывался передавать данные, хотя и отвечал корректно. Но даже с отдельным питанием без конденсатора связь была очень нестабильной.
Чтобы мега2560 не сбрасывалась при открытии порта через USB на пин сброса подключен 10мкф электролит.
OpenHAB
В статье Arduino & OpenHAB описана особенность плагина Modbus Binding, что при каждом опросе контроллера, плагин отправляет в шину событие, даже если ничего не изменилось. Я последовал примеру и доработал плагин.
modbus:serial.ac_hall_state.connection=/dev/ttyACM0:57600:8:none:1:rtu
modbus:serial.ac_hall_state.id=1
modbus:serial.ac_hall_state.start=48
modbus:serial.ac_hall_state.length=5
modbus:serial.ac_hall_state.type=discrete
#Управление
modbus:serial.ac_hall_power.connection=/dev/ttyACM0:57600:8:none:1:rtu
modbus:serial.ac_hall_power.id=1
modbus:serial.ac_hall_power.start=56
modbus:serial.ac_hall_power.length=4
modbus:serial.ac_hall_power.type=coil
#Часы
modbus:serial.ac_hall_rtc.connection=/dev/ttyACM0:57600:8:none:1:rtu
modbus:serial.ac_hall_rtc.id=1
modbus:serial.ac_hall_rtc.start=4
modbus:serial.ac_hall_rtc.length=1
modbus:serial.ac_hall_rtc.type=holding
#Температура датчики
modbus:serial.ac_hall_temperature.connection=/dev/ttyACM0:57600:8:none:1:rtu
modbus:serial.ac_hall_temperature.id=1
modbus:serial.ac_hall_temperature.start=6
modbus:serial.ac_hall_temperature.length=2
modbus:serial.ac_hall_temperature.type=holding
modbus:serial.ac_hall_temperature.valuetype=int16
#Режим
modbus:serial.ac_hall_mode.connection=/dev/ttyACM0:57600:8:none:1:rtu
modbus:serial.ac_hall_mode.id=1
modbus:serial.ac_hall_mode.start=8
modbus:serial.ac_hall_mode.length=1
modbus:serial.ac_hall_mode.type=holding
#температура режима
modbus:serial.ac_hall_temp.connection=/dev/ttyACM0:57600:8:none:1:rtu
modbus:serial.ac_hall_temp.id=1
modbus:serial.ac_hall_temp.start=9
modbus:serial.ac_hall_temp.length=4
modbus:serial.ac_hall_temp.type=holding
#Скорость режима
modbus:serial.ac_hall_fan.connection=/dev/ttyACM0:57600:8:none:1:rtu
modbus:serial.ac_hall_fan.id=1
modbus:serial.ac_hall_fan.start=13
modbus:serial.ac_hall_fan.length=5
modbus:serial.ac_hall_fan.type=holding
#Шторки
modbus:serial.ac_hall_swing.connection=/dev/ttyACM0:57600:8:none:1:rtu
modbus:serial.ac_hall_swing.id=1
modbus:serial.ac_hall_swing.start=18
modbus:serial.ac_hall_swing.length=2
modbus:serial.ac_hall_swing.type=holding
#Таймеры
modbus:serial.ac_hall_timer.connection=/dev/ttyACM0:57600:8:none:1:rtu
modbus:serial.ac_hall_timer.id=1
modbus:serial.ac_hall_timer.start=320
modbus:serial.ac_hall_timer.length=2
modbus:serial.ac_hall_timer.type=coil
#Время таймеров
modbus:serial.ac_hall_timer_time.connection=/dev/ttyACM0:57600:8:none:1:rtu
modbus:serial.ac_hall_timer_time.id=1
modbus:serial.ac_hall_timer_time.start=21
modbus:serial.ac_hall_timer_time.length=4
modbus:serial.ac_hall_timer_time.type=holding
#Адреса датчиков DS18B20
modbus:serial.ac_hall_ds18b20.connection=/dev/ttyACM0:57600:8:none:1:rtu
modbus:serial.ac_hall_ds18b20.id=1
modbus:serial.ac_hall_ds18b20.start=25
modbus:serial.ac_hall_ds18b20.length=2
modbus:serial.ac_hall_ds18b20.type=holding
Contact AC_HALL_STATE_POWER "AC_HALL_STATE_POWER [MAP(air_cond.map):%s]" (){modbus="ac_hall_state:0"} Contact AC_HALL_STATE_OXYGEN "AC_HALL_STATE_OXYGEN [MAP(air_cond.map):%s]" (){modbus="ac_hall_state:1"} Contact AC_HALL_STATE_ION "AC_HALL_STATE_ION [MAP(air_cond.map):%s]" (){modbus="ac_hall_state:2"} Contact AC_HALL_STATE_QUIET "AC_HALL_STATE_QUIET [MAP(air_cond.map):%s]" (){modbus="ac_hall_state:3"} Contact AC_HALL_STATE_TIMER "Таймер[MAP(air_cond.map):%s]" (){modbus="ac_hall_state:4"} Switch AC_HALL_CONTROL_POWER "Кондиционер" <climate> (){modbus="ac_hall_power:0"} Switch AC_HALL_CONTROL_OXYGEN "Генератор O2" (){modbus="ac_hall_power:1"} Switch AC_HALL_CONTROL_ION "Ионизация" (){modbus="ac_hall_power:2"} Switch AC_HALL_CONTROL_QUIET "Тихий режим" (){modbus="ac_hall_power:3"} Number AC_HALL_RTC "RTC[%x]" (){modbus="ac_hall_rtc:0"} String AC_HALL_RTC_S "Время контроллера[%s]" <clock> () Group gAC_HALL_TEMPERATURE "Living Room temp" Number AC_HALL_TEMPERATURE_ENV "Комната[%d]" (){modbus="ac_hall_temperature:0"} Number AC_HALL_TEMPERATURE_NOZ "Поток[%d]" (){modbus="ac_hall_temperature:1"} Number AC_HALL_TEMPERATURE_ENVF "Комната [%.2f °C]" <temperature> (gAC_HALL_TEMPERATURE) Number AC_HALL_TEMPERATURE_NOZF "Поток [%.2f °C]" <temperature> (gAC_HALL_TEMPERATURE) Number AC_HALL_DS18B20_ENV "ENV[%x]" (){modbus="ac_hall_ds18b20:0"} Number AC_HALL_DS18B20_NOZ "NOZZLES[%x]" (){modbus="ac_hall_ds18b20:1"} Number AC_HALL_MODE "" (){modbus="ac_hall_mode:0"} Number AC_HALL_TEMP_AUTO "Температура[%d °C]" <temperature> (){modbus="ac_hall_temp:0"} Number AC_HALL_TEMP_COOL "Температура[%d °C]" <temperature> (){modbus="ac_hall_temp:1"} Number AC_HALL_TEMP_HEAT "Температура[%d °C]" <temperature> (){modbus="ac_hall_temp:2"} Number AC_HALL_TEMP_DRY "Температура[%d °C]" <temperature> (){modbus="ac_hall_temp:3"} Number AC_HALL_FAN_AUTO "Скорость[%d]" (){modbus="ac_hall_fan:0"} Number AC_HALL_FAN_COOL "Скорость[%d]" (){modbus="ac_hall_fan:1"} Number AC_HALL_FAN_HEAT "Скорость[%d]" (){modbus="ac_hall_fan:2"} Number AC_HALL_FAN_DRY "Скорость[%d]" (){modbus="ac_hall_fan:3"} Number AC_HALL_FAN_SPEED "Скорость[%d]" (){modbus="ac_hall_fan:4"} Number AC_HALL_SWINGV "" (){modbus="ac_hall_swing:0"} Number AC_HALL_SWINGH "" (){modbus="ac_hall_swing:1"} Switch AC_HALL_TIMER_ON "Таймер включения" <clock> (){modbus="ac_hall_timer:0"} Switch AC_HALL_TIMER_OFF "Таймер выключения" <clock> (){modbus="ac_hall_timer:1"} Number AC_HALL_TIME_ON_HR "Час[%02d]" (){modbus="ac_hall_timer_time:0"} Number AC_HALL_TIME_ON_MI "Минута[%02d]" (){modbus="ac_hall_timer_time:1"} Number AC_HALL_TIME_OFF_HR "Час[%02d]" (){modbus="ac_hall_timer_time:2"} Number AC_HALL_TIME_OFF_MI "Минута[%02d]" (){modbus="ac_hall_timer_time:3"}
Правила используются для преобразования целочисленных температур в вещественные, а также для форматирования времени контроллера к виду HH:MM.
import org.openhab.core.library.types.* import org.openhab.core.persistence.* import org.openhab.model.script.actions.* import java.io.File rule "Update AC_HALL ENV temp" when Item AC_HALL_TEMPERATURE_ENV received update then var Number T = AC_HALL_TEMPERATURE_ENV.state as DecimalType var Number H = T/100 postUpdate(AC_HALL_TEMPERATURE_ENVF, H) end rule "Update AC_HALL NOZZLES temp" when Item AC_HALL_TEMPERATURE_NOZ received update then var Number T = AC_HALL_TEMPERATURE_NOZ.state as DecimalType var Number H = T/100 postUpdate(AC_HALL_TEMPERATURE_NOZF, H) end rule "Update AC_HALL_RTC clock" when Item AC_HALL_RTC received update then var Number T = AC_HALL_RTC.state as DecimalType var H = T.intValue / 256 var M = T.intValue % 256 var S = String::format("%02x:%02x",H,M) postUpdate(AC_HALL_RTC_S, S) end
sitemap demo label="Demo House"{ Frame label="HOME"{ Text label="Кондиционер зал" icon="ac_cond"{ Frame label="Управление" { Switch item= AC_HALL_CONTROL_POWER labelcolor=[AC_HALL_STATE_POWER==OPEN="blue"] Switch item= AC_HALL_CONTROL_OXYGEN labelcolor=[AC_HALL_STATE_OXYGEN==OPEN="blue"] Switch item= AC_HALL_CONTROL_ION labelcolor=[AC_HALL_STATE_ION==OPEN="blue"] Switch item= AC_HALL_CONTROL_QUIET labelcolor=[AC_HALL_STATE_QUIET==OPEN="blue"] Text item=AC_HALL_STATE_TIMER labelcolor=[AC_HALL_STATE_TIMER==OPEN="blue"] icon="clock-on" Text item=AC_HALL_RTC_S Text item=AC_HALL_TEMPERATURE_ENVF Text item=AC_HALL_TEMPERATURE_NOZF } Frame label="Режим"{ Selection item=AC_HALL_MODE label="Режим" mappings=[1=AUTO, 2=COOL, 4=HEAT, 8=DRY, 16=FAN] Text item=AC_HALL_TEMP_AUTO visibility=[AC_HALL_MODE==1] Text item=AC_HALL_TEMP_COOL visibility=[AC_HALL_MODE==2] Text item=AC_HALL_TEMP_HEAT visibility=[AC_HALL_MODE==4] Text item=AC_HALL_TEMP_DRY visibility=[AC_HALL_MODE==8] Text item=AC_HALL_FAN_AUTO visibility=[AC_HALL_MODE==1] Text item=AC_HALL_FAN_COOL visibility=[AC_HALL_MODE==2] Text item=AC_HALL_FAN_HEAT visibility=[AC_HALL_MODE==4] Text item=AC_HALL_FAN_DRY visibility=[AC_HALL_MODE==8] Text item=AC_HALL_FAN_SPEED visibility=[AC_HALL_MODE==16] Selection item=AC_HALL_SWINGV label="Вертикальное" mappings=[1=AUTO, 2="0°", 4="15°", 8="30°", 16="45°", 32="60°"] Selection item=AC_HALL_SWINGH label="Горизонтальное" mappings=[1=AUTO, 4="/ /", 8="/ |", 2="| |", 16="| \\", 32="\\ \\"] Text label="Настройки" icon="settings"{ Frame label="AUTO"{ Setpoint item=AC_HALL_TEMP_AUTO minValue=16 maxValue=30 step=1 Switch item=AC_HALL_FAN_AUTO mappings=[0=AUTO, 1="1", 2="2", 3="3", 4="4", 5="5"] } Frame label="COOL"{ Setpoint item=AC_HALL_TEMP_COOL minValue=16 maxValue=30 step=1 Switch item=AC_HALL_FAN_COOL mappings=[0=AUTO, 1="1", 2="2", 3="3", 4="4", 5="5"] } Frame label="HEAT"{ Setpoint item=AC_HALL_TEMP_HEAT minValue=16 maxValue=30 step=1 Switch item=AC_HALL_FAN_HEAT mappings=[0=AUTO, 1="1", 2="2", 3="3", 4="4", 5="5"] } Frame label="DRY"{ Setpoint item=AC_HALL_TEMP_DRY minValue=16 maxValue=30 step=1 Switch item=AC_HALL_FAN_DRY mappings=[0=AUTO, 1="1", 2="2", 3="3", 4="4", 5="5"] } Frame label="FAN"{ Switch item=AC_HALL_FAN_SPEED mappings=[0=AUTO, 1="1", 2="2", 3="3", 4="4", 5="5"] } Frame label="Прочее"{ Text item=AC_HALL_DS18B20_ENV Text item=AC_HALL_DS18B20_NOZ } } } Frame label="Таймер" { Switch item= AC_HALL_TIMER_ON labelcolor=[AC_HALL_STATE_TIMER==OPEN="blue"] Setpoint item=AC_HALL_TIME_ON_HR minValue=0 maxValue=23 step=1 Setpoint item=AC_HALL_TIME_ON_MI minValue=0 maxValue=50 step=5 Switch item= AC_HALL_TIMER_OFF labelcolor=[AC_HALL_STATE_TIMER==OPEN="blue"] Setpoint item=AC_HALL_TIME_OFF_HR minValue=0 maxValue=23 step=1 Setpoint item=AC_HALL_TIME_OFF_MI minValue=0 maxValue=50 step=5 } } Text item=AC_HALL_RTC_S Text item=AC_HALL_TEMPERATURE_ENVF{ Frame label="Последний час"{ Chart item=gAC_HALL_TEMPERATURE period=h refresh=60000 } Frame label="Последние 4 часа" { Chart item=gAC_HALL_TEMPERATURE period=4h refresh=600000 } } } }
Результат выглядит примерно так через браузер:
Заключение
Полученным результатом я доволен как слон. Управление кондиционером сейчас доступно мне везде где есть мобильный интернет или WiFI. Используя VPN-клиент на смарфоне мне становится доступен OpenHAB на домашнем сервере как через браузер, так и через мобильное приложение.
Беспроводное решение позволило тесно интегрировать контроллер с кондиционером.
Наличие обратной связи дает уверенность, что отправленная команда принята кондиционером, а датчики температуры наглядно это демонстрируют — через несколько секунд можно наблюдать изменение показаний температуры на выходе из кондиционера. Ну а через несколько минут — и окружающей температуры.
Интересно было наблюдать за профилем работы кондиционера при приближении к заданным условиям.
Несомненно это будет не единственный беспроводной контроллер который я планирую использовать.
В планах есть задействование ИК-приемника самого кондиционера, чтобы читать команды родного пульта для актуализации настроек в контроллере. Тем более, что сам ИК-приемник уже подключен через оптопару к контроллеру.
Дочитавшим до конца отдельное спасибо!
Ссылки
- Arduino library ModbusRtu Modbus-Master-Slave-for-Arduino
- Arduino library ModbusRtuRF24 Modbus-over-RF24Network-for-Arduino