Пользователь
0,0
рейтинг
6 августа 2013 в 13:51

ИК-контроллер бытового кондиционера или реверс-инжиниринг пульта ДУ из песочницы

Приветствую, уважаемые хабрачитатели! В этой, первой для меня, статье я хотел поделиться своими исследованиями и наработками. Имея в своей квартире кондиционер я ощущал необходимость управлять им когда меня нет дома. Возвращаясь с дачи или просто с работы и, включив кондиционер, приходится ждать некоторое время пока не спадет жара. А хочется приходить в уже легкую прохладу.
«Вот бы можно было удаленно включить кондишн...» — подумал я.


Итак, у меня имеется сплит-система Panasonic CS-XE9DKE. Идея управлять кондиционером сводилась к созданию «своего» ИК-пульта. Назовем его «ИК-контроллер». А уж управлять самим контроллером — это задача десятая. И это зависит от конкретного места применения. Мне, например, было бы удобнее управлять с домашнего сервера через WizFi200(только вот достать его за приемлемые деньги проблематично). Моим родителям — с помощью отправки SMS(на SIM900) на номер контроллера. Возможны и другие варианты. Но это не главное, о чем я хотел рассказать.

Реверс-инжиниринг


Так как же родной пульт передает команду? Имея в наличии ИК-приемник TSOP17XX я начал анализировать поток данных от пульта. Выяснилось, что пульт шлет 2 посылки с небольшой паузой. Первая — заголовок, вторая — команда. Заголовок был всегда одинаковый.

Ознакомившись с существующими системами кодирования ИК-сигналов(RC5, RC6, NEC, JVC и другие) стало ясно, что здесь применяется что-то другое, хотя принцип схож.

Сингал выглядит следующим образом:
(активный уровень — низкий)

image

(значения бит для примера, время в мкс приблизительно)

В начале идет пилотный сигнал, за ним примерно вдвое короче второй пилотный(пауза), за ним стартовый бит длительностью примерно в 8 раз короче чем первый пилотный. Далее начинается битовый поток. Один бит кодируется длительностью паузы(высокий уровень). Если пауза одинарная — это НОЛЬ, если пауза тройная — это ЕДИНИЦА.
Всего при одном нажатии кнопки пульт шлет 2 таких посылки. Первая — заголовок, вторая — команда. Пауза между посылками около 10 мс.

Таким образом заголовок содержит 64 бита, или 8 байт. Сначала идут младшие биты.

Поток данных заголовка выглядит следующим образом:

0100000000000100000001110010000000000000000000000000000001100000
Или в байтах(HEX): 02 20 E0 40 00 00 00 06

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

Теперь немного отвлечемся на сам пульт. Пульт имеет следующие кнопки:
  • ON/OFF(включение/выключение)
  • ± (настройка температуры)
  • O2 (генератор кислорода)
  • ion (ионизатор)
  • quiet (тихий режим)
  • mode ( auto, heat, cool, dry, fan)
  • fan speed
  • swing <>(горизонтальное направление)
  • swing ^v (вертикальное направление)

Также есть кнопки для установки текущего времени, таймеров включения и выключения(раздельно, самостоятельно).

3 кнопки, которые отличаются от других, это:
  • ion
  • quiet
  • O2

Смысл в том, что эти кнопки не посылают все настройки, а включают/выключают конкретный режим. Т.е. Эти 3 режима можно включать/выключать в любом состоянии кондиционера.

Теперь вернемся к протоколу передачи. Любой команде предшествует заголовок.
Байты заголовка: 02 20 E0 40 00 00 00 06
Далее идет вторая посылка с командой.
Команды трех особых кнопок:
ion: 02 20 E0 04 80 48 33 01
oxyg: 02 20 E0 04 80 50 33 09
quiet: 02 20 E0 04 80 81 33 3A
Отмечу, что, как и в заголовке, последний байт — контрольная сумма.

А вот все остальные кнопки посылают пакет со всеми настройками сразу. И формат этого пакета следующий:

Поток бит:
0100000000000100000001110010000000000000PNF1mmm00ccccc0000000001vvvvFFFF
hhhh0000nnnnnnnnnnnXfffffffffffY000010000000000010000000ttttttttttt00000ssssssss

Расшифровка полей по порядку следования:
P — 1, если нажата кнопка ON/OFF. При нажатии других кнопок здесь 0.
N — 1, если установлен таймер включения
F — 1, если установлен таймер выключения
mmm — режим(Mode). Auto — 0, heat — 4, cool — 3, dry — 2, fan — 6
ccccc — температура. От 16 до 30
vvvv — вертикальное направление. Auto — 15, 1 — под потолок, … 5 — в пол.
FFFF — скорость вентилятора. Auto — 10, F1 — 3, F2 — 4, F3 — 5, F4 — 6, F5 — 7
hhhh — горизонтальное направление. Auto — 13, | | — 6, / / — 9, / | — 10, | \ — 11, \ \ — 12
nnnnnnnnnnn — время включения. Номер минуты в сутках. (например 16:00 = 960)
X — 1, если нажата кнопка установки/сброса таймера
fffffffffff — время выключения. Номер минуты в сутках.
Y — 1, если нажата кнопка установки/сброса таймера
ttttttttttt — текущее время.
ssssssss — контрольная сумма посылки.

Сигналы X и Y — установлены в 1 только когда выполняется установка/сброс таймеров, т.е. когда непосредственно нажимается SET или CANCEL. Не путать с N и F!
N и F — только сообщают, что таймеры включены/выключены, при этом nnnnnnnnnnn или fffffffffff могут быть «не известны»(0x600) в данный момент. Например при нажатии кнопки увеличения температуры, если в данный момент включен любой таймер, значение N/F равно 1, а nnnnnnnnnnn/fffffffffff cсодержит конкретное время только для включенного таймера(для выключенного содержит 0x600). Кондиционер сменит свой статус N/F только если соответсвующий бит X/Y равен 1.
Если таймер включения не установлен, то время включения д.б. 1536 = 0x600 = 0b11000000000. С временем таймера выключения аналогично. По крайней мере так шлет родной пульт.

Разделим поток бит на байты и развернем биты:
00000010 02
00100000 20
11100000 E0
00000100 04
00000000 00
0mmm1FNP Mode<<4 + 8 + FlagOFF<<2 + FlagON<<1 + Power
00ccccc0 Temperature<<1
10000000 80
FFFFvvvv Fan<<4 + Vert
0000hhhh Horiz
nnnnnnnn OnTime&0xFF
ffffXnnn OnTime>>8 + OffTime&0x0F + ChangeOn<<3
Yfffffff OffTime>>4 + ChangeOff<<7
00010000 10
00000000 00
00000001 01
tttttttt Time&0xFF
00000ttt Time>>8
ssssssss Sum

Итого команда 19 байт.

Контроллер


Для тестирования я собрал схему(позаимствовал здесь) на макетке для беспаечного монтажа. Компоненты:
  • atmega8
  • ИК-диод
  • транзистор кт361
  • резистор 470 на базу транзистора
  • резистор 220 ограничивает ток диода
  • резистор 10к на RESET

Отличие моей схемы от схемы по ссылке — светодиод подключен к PB1, а база транзистора к PB3.

При отпускании кнопки на порту PD7 запускается таймер 1 в режиме CTC с выводом сигнала на PB1/OC1A, а так же таймер 2 с выводом на PB3/OC2 для несущей частоты, на которой и модулируется основной сигнал. Таймеры работают с предделителем 8.

Прошивка



Исходный код для AVR Studio 4.
#include <avr/io.h>
#include <avr/interrupt.h>

#define F_CPU 8000000

#define LED_CARIER DDB3
#define LED_SIGNAL DDB1

#define BASE 480
#define PAUSE 10000
#define START_1 BASE*8
#define START_2 BASE*4
#define DELIM 520
#define PULSE1 BASE*3
#define PULSE0 BASE


unsigned char state = 0;
unsigned char byteIndex = 0;
unsigned char frameIndex = 0;
unsigned char bit = 0;
unsigned char phase = 0;
unsigned char packetLen = 0;
unsigned char process = 0;

char header[] = {0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x06};
char cmd_ion[] = {0x02, 0x20, 0xE0, 0x04, 0x80, 0x48, 0x33, 0x01};
char cmd_oxygen[] = {0x02, 0x20, 0xE0, 0x04, 0x80, 0x50, 0x33, 0x09};
char cmd_quiet[] = {0x02, 0x20, 0xE0, 0x04, 0x80, 0x81, 0x33, 0x3A};

char* data;
char* cmd;
unsigned char cmdLen;

void stop();

ISR(SIG_OUTPUT_COMPARE1A) {
	switch (state) {
		case 0:
			OCR1AH = START_2 / 256;
			OCR1AL = START_2 % 256;
			state = 1;
			break;
		case 1:
			OCR1AH = DELIM / 256;
			OCR1AL = DELIM % 256;
			state = 2;
			byteIndex = 0;
			frameIndex++;
			if (frameIndex == 1) {
				data = header;
				packetLen = 8;
			} else {
				data = cmd;
				packetLen = cmdLen;
			}
			bit = 0;
			phase = 0;
			break;

		case 2:
			if (byteIndex < packetLen) {
				if (phase == 0) {
					if (data[byteIndex]&(1 << bit)) {
						OCR1AH = PULSE1 / 256;
						OCR1AL = PULSE1 % 256;
					} else {
						OCR1AH = PULSE0 / 256;
						OCR1AL = PULSE0 % 256;
					}
				} else {
					OCR1AH = DELIM / 256;
					OCR1AL = DELIM % 256;
					bit++;
					if (bit == 8) {
						bit = 0;
						byteIndex++;
					}
				}
				phase = 1 - phase;
			} else {
				OCR1AH = PAUSE / 256;
				OCR1AL = PAUSE % 256;
				if (frameIndex == 2) {
					stop();
				} else {
					state = 3;
				}
			}
			break;

		case 3:
			OCR1AH = START_1 / 256;
			OCR1AL = START_1 % 256;
			state = 0;
			break;
	}
}

void start() {
	state = 0;
	byteIndex = 0;
	frameIndex = 0;
	packetLen = sizeof (header);
	TCNT1H = 0;
	TCNT1L = 0;
	OCR1AH = START_1 / 256;
	OCR1AL = START_1 % 256;
	TCCR1A = (1 << COM1A0);
	TCCR1B = (1 << WGM12) | (1 << CS11);

	TCNT2 = 0;
	OCR2 = 13;
	TCCR2 = (1 << WGM21) | (1 << COM20) | (1 << CS21);

	TIMSK = (1 << OCIE1A);
	sei();
}

void stop() {
	cli();
	TCCR2 = (1 << WGM21) | (1 << COM20);
	TCCR1A = (1 << FOC1A);
	TCCR1B = (1 << WGM12) | (0 << CS12) | (0 << CS11) | (0 << CS10);
	TIMSK = 0;
	PORTB = (1 << LED_SIGNAL) | (1 << LED_CARIER);
	process = 0;
}

int main(void) {

	process = 0;
	PORTB = (1 << LED_SIGNAL) | (1 << LED_CARIER);
	DDRB = (1 << LED_SIGNAL) | (1 << LED_CARIER);

	PORTD = (1 << 7); // PD7 input, pull up
	DDRD = 0;

	while (1) {
		int but = (PIND & (1 << PD7));
		if (process == 0 && but == 0) {
			process = 1;
		} else if (process == 1 && but != 0) {
			process = 2;
			cmd = cmd_oxygen;
			cmdLen = 8;
			start();
		}
	}
}



Вывод


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

UPD
История получила продожение в статье Беспроводной контроллер бытового кондиционера в OpenHAB по Modbus через RF24Network.
Андрей @karakum22
карма
8,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое

Комментарии (11)

  • +1
    Лучше сформированную команду слать, имхо — универсальность выше. Это, если Вы собираетесь контроллер использовать не только с кондиционером.
  • +4
    Я делал проще — считываем поток с ИК при нажатии на кнопку n раз. берем общий участок и можно не разбираться что там за протокол. Так с любыми пультами обычно удается.

    По поводу реализации — на мой взгляд нужна команда «работай на 23 градуса», «выключись» и «статус»(прислать температуру в смс) + обязательно сделать авто выключение, если например за час никто не пришел в комнату.
  • 0
    Я делал проще — считываем поток с ИК при нажатии на кнопку n раз. берем общий участок и можно не разбираться что там за протокол. Так с любыми пультами обычно удается.

    Такая фишка с пультом от кондиционера не прокатит. Ну разве что вам нужно всегда включать 1 режим(с конкретной температурой и другими параметрами). И я не уверен смогут ли универсальные пульты переварить 2 посылки. Все таки обычные пульты шлют всегда только одну(ну или еще плюсом повторы нажатий)
  • +1
    Поздравляю с дебютом!
    Очень кстати статья, была у меня мысль «автоматизировать» кондиционер. Есть еще вариант купить второй пульт и просто микроконтроллером нажимать на нем кнопки.
    • 0
      Ну и дальнейшее развитие — микроконтроллер на текстовом дисплее просто отображает команды охраннику «Шоб к моему приезду было прохладно».
  • 0
    Спасибо за описание протокола ;)

    не в тему ну мое имхо
    не надо выключать кондиционер ;)
    его надо включить весной и выключить осенью
    и ставить на нем нормальную температуру (+24-25) (в зависимости от температуры за бортом)
    и пусть он себе работает, лишнего он есть не будет после того как доведет всю квартиру до нужной температуры
    (а при включении летом это будет несколько дней)
    зато потом, его вообще не заметно, он поддерживает нужную температуру только
    • 0
      Не на всех широтах так жарко, что кондиционер нужен каждый день всё лето. А вот свежий воздух нужен постоянно ;)
      • +2
        свежий воздух нуже всегда ;)
        при использовании кондишена — проветривание обязательно

        только в ситуация «включить чтоб к приходу было прохладно» явно намекает на другой сценарий
  • 0
    Посмотрите на Nest — термостат (контроллер кондиционера), может реализуете некоторый его функционал.
    Очень приятная вещь в работе.
  • 0
    Подскажи пожалуйста, каким готовым решением без пайки можно считать ик сигнал с пульта кондиционера? и увидеть его на пк
  • 0
    У меня чуть другая ситуация — штатные термодатчики кондиционера спятили и засыпают. Логика смешная получается, включаешь Авто на 26 градусах, он холодит, по идее до 25 должен (согласно инструкции), но он и после 25 дальше продолжает херачить (потому что датчик «залип»), и к примеру доходит до 23. Я замерзаю, переключаю режим на вентилятор, датчик просыпается, показывает уже реальные 23, переключаю тут же на Авто снова, проснувшийся датчик заставляет кондер в режим нагрева переходить (ну правильно, ведь 23, а целевая 25), он греет до 25, и продолжает греть до 27 (датчик снова уснул зараза)) и вот такое поведение в общем… Причем они (Ballu) походу один и тот же датчик в пульте и самом кондере использовали. В пульте тоже засыпает, точно так же. Пробовал включать режим замера температуры пультом. Прям вижу как засыпает на определенном градусе. Рядом радионяня лежит, какое-то время они синхронно меряют, а потом все — хоп! и няня фиксирует что температура все ниже и ниже, а пульт — нет, замирает. Разница до 4 градусов доходила.
    По гарантии сдавать в лом, это ж все снимать надо. В целом то все работает. Вот и думаю, мозги свои прикрутить, да еще фича с удаленным управлением появится как доп.опция ;) Автору респект за классную статью! Буду тоже пробовать)) Мигание лампочкой не влияет на гарантию )))))

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