Нашел способ подружить кондиционер (или другой прибор управляемым пультом дистанционного управления) и умный дом, либо сделать управление этим устройством, например с сотового.
В теории просто, подключаем esp8266 (ценой около 2 долларов у китайцев) к инфракрасном светодиоду, заливаем прошивку и готово.


На практике оказалось все несколько сложнее, т.к. у меня ревизия esp-01 пришлось припаять (примерно так) проводок к ножке микросхемы размером 5*5мм, вывести дополнительный GPIO. Рекомендую использовать ревизию постарше, например ESP-12.
Читателям не знакомым с esp8266 предлагаю ознакомится этой статьей.

Компоненты и модули


* Esp8266 ревизии esp-01
* FT232RL: USB to Serial 232 TTL Adapter + провода для подключения
* Фотоприемник Tsop 4838
* Регулятор 3.3v
* Транзистор S9014
* Светодиод инфракрасный 5013IRAB (длина волны 940 nm)
* Резистор 330ом
* Проектная плата
* Паяльник, припой, флюс.
* Для работы с IR используется замечательная библиотека IRremoteESP8266. Авторы Mark Szabo, Sebastien Warin, Ken Shirriff.

Сохранение кодов


* Подключаем фотоприемник, VCC на +3.3, GND на ноль, OUT на GPIO.
* Открываем пример для дампа кодов из нашей библиотеки \IRremoteESP8266\examples\IRrecvDump\IRrecvDump.ino
* Если надо — меняем номер пина к которому мы подключили OUT фотоприемника (строка «int RECV_PIN = 2;»).
* Заливаем прошивку. Подключаемся к выводу модуля. Поочередно зажимаем кнопки пульта для дампа кодов.
Для бризера Tion O2 у меня получились такие кода:
Down
16711935
Decoded NEC: FF00FF (32 bits)
— Up
16724175
Decoded NEC: FF30CF (32 bits)
— Set
16722135
Decoded NEC: FF28D7 (32 bits)
— Power
16720095
Decoded NEC: FF20DF (32 bits)


Фотоприемник больше не нужен, пока не потребуется «сграбить» кода другого пульта.

Управление устройством


* Подключаем ИК светодиод по схеме.

(Картинка заимствована у Fritzing)
GPIO можно использовать другой. От напряжения 3.3v светодиод так-же нормально работает.
UPD: Как справедливо заметили в комментариях желательно использовать токоограничивающий резистор.
* Демо прошивка с сервером находится тут
\IRremoteESP8266\examples\IRServer\IRServer.ino
* Меняем в ней название и пароль вашей точки доступа, чтобы esp мог подключиться к ней. А так-же номер используемого GPIO
const char* ssid = ".....";
const char* password = ".....";
IRsend irsend(0);

* Прошиваем модуль.
* При подключении в консоль будет выведен IP адрес выделенный esp.

Теперь можно отправлять кода вводя в броузере адрес вида 192.168.1.1/ir?code=16720095 (просто подставьте нужный IP и код).

Код прошивки с обновлением по воздуху
/*
 * IRremoteESP8266: IRServer - demonstrates sending IR codes controlled from a webserver
 * An IR LED must be connected to ESP8266 pin 0.
 * Version 0.1 June, 2015
 */

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <IRremoteESP8266.h>
 
const char* ssid = "FFFF";
const char* password = "XXXX";
unsigned long last_cmd_send_time = 0;
MDNSResponder mdns;

ESP8266WebServer server(80);
const char* serverIndex = "<form method='POST' action='/update' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form>";

// IRsend irsend(2);
IRsend irsend(13);

void handleRoot() {
 server.send(200, "text/html", "<html><head> <title>ESP8266 Demo (Web Update)</title></head><body><h1>Hello from ESP8266, you can send NEC encoded IR signals from here!</h1><p><a href=\"ir?code=16769055\">Send 0xFFE01F</a></p><p><a href=\"ir?code=16429347\">Send 0xFAB123</a></p><p><a href=\"ir?code=16771222\">Send 0xFFE896</a></p></body></html>");
}

void handleIr(){
  for (uint8_t i=0; i<server.args(); i++){
    if(server.argName(i) == "code") 
    {
      unsigned long code = server.arg(i).toInt();
      irsend.sendNEC(code, 32);
    }
  }
  handleRoot();
}

void handleSeq(){
  unsigned long code = 0;
  unsigned long cnt = 0;
  unsigned long dl = 0;
  unsigned long nwt = 0;
  for (uint8_t i=0; i<server.args(); i++){
    if(server.argName(i) == "code") 
    {
      code = server.arg(i).toInt();
    }
    if(server.argName(i) == "count") 
    {
      cnt = server.arg(i).toInt();
    }
    if(server.argName(i) == "delay") 
    {
      dl = server.arg(i).toInt();
    }
    if(server.argName(i) == "need_wait") 
    {
      nwt = server.arg(i).toInt();
    }
  }
  if (nwt > 0){
    unsigned long wt = millis() - last_cmd_send_time;
    if (wt < nwt && wt > 0) {
      delay(nwt - wt);
    }
  }
  if (code != 0) {
    for (uint8_t i=0; i<cnt; i++){
      irsend.sendNEC(code, 32);
      delay(dl);
    }
  }
  last_cmd_send_time = millis();
  handleRoot();
}


void handleNotFound(){
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET)?"GET":"POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i=0; i<server.args(); i++){
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
}
 
void setup(void){
  irsend.begin();
  
  Serial.begin(115200);
  WiFi.mode(WIFI_AP_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  
  if (mdns.begin("esp8266", WiFi.localIP())) {
    Serial.println("MDNS responder started");
  }
    
  server.on("/", handleRoot);
  server.on("/ir", handleIr);
  server.on("/seq", handleSeq);
   
  server.on("/inline", [](){
    server.send(200, "text/plain", "this works as well");
  });

    server.on("/update", HTTP_GET, [](){
      server.sendHeader("Connection", "close");
      server.sendHeader("Access-Control-Allow-Origin", "*");
      server.send(200, "text/html", serverIndex);
    });
    server.on("/update", HTTP_POST, [](){
      server.sendHeader("Connection", "close");
      server.sendHeader("Access-Control-Allow-Origin", "*");
      server.send(200, "text/plain", (Update.hasError())?"FAIL":"OK");
      ESP.restart();
    },[](){
      HTTPUpload& upload = server.upload();
      if(upload.status == UPLOAD_FILE_START){
        Serial.setDebugOutput(true);
        WiFiUDP::stopAll();
        Serial.printf("Update: %s\n", upload.filename.c_str());
        uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
        if(!Update.begin(maxSketchSpace)){//start with max available size
          Update.printError(Serial);
        }
      } else if(upload.status == UPLOAD_FILE_WRITE){
        if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){
          Update.printError(Serial);
        }
      } else if(upload.status == UPLOAD_FILE_END){
        if(Update.end(true)){ //true to set the size to the current progress
          Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
        } else {
          Update.printError(Serial);
        }
        Serial.setDebugOutput(false);
      }
      yield();
    });


  server.onNotFound(handleNotFound);
  
  server.begin();
  Serial.println("HTTP server started");
}
 
void loop(void){
  server.handleClient();
} 



Я сделал вот такую страничку для удобства управления своим бризером.
brizer.html
<html>
	<head>
		<script>
			function sendIR(str) {
				if (str.length == 0) { 
					// document.getElementById("txtHint").innerHTML = "";
					return;
				} else {
					var xmlhttp = new XMLHttpRequest();
					xmlhttp.onreadystatechange = function() {
						if (this.readyState == 4 && this.status == 200) {
							// document.getElementById("txtHint").innerHTML = this.responseText;
						}
					};
					xmlhttp.open("GET", "http://192.168.0.193/" + str, true);
					xmlhttp.send();
				}
			}
		</script>
	</head>
	<body>
		<div id="demo">
			<button type="button" onclick="sendIR('ir?code=16724175')">Up</button>
			<button type="button" onclick="sendIR('ir?code=16711935')">Down</button>
			<button type="button" onclick="sendIR('ir?code=16722135')">Set</button>
			<button type="button" onclick="sendIR('ir?code=16720095')">Power</button>  
			<button type="button" onclick="sendIR('seq?need_wait=11000&code=16722135&count=2&delay=20');sendIR('seq?code=16711935&count=50&delay=20');sendIR('seq?code=16724175&count=21&delay=20')">day</button>  
			<button type="button" onclick="sendIR('seq?need_wait=11000&code=16722135&count=2&delay=20');sendIR('seq?code=16711935&count=50&delay=20');sendIR('seq?code=16724175&count=35&delay=20')">night</button>  
			<a href="http://192.168.0.193/update">update</a>
		</div>
	</body>
</html>



Конкретно в моем случае бризер работает с пультом необычно, при первом нажатии он «просыпается», а именно включает подсветку экранчика, и только после этого начинает принимать команды. При автоматизации стоит учитывать это.

Надеюсь данная инструкция кому-то пригодится. Прошу делиться доработанными прошивками.
Поделиться с друзьями
-->

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


  1. boingo-00
    16.02.2017 18:57
    +1

    Китайский свисток на PL2303 покатит? FT232 в основном либо дорого, либо подделка


    1. e_butcher
      17.02.2017 09:11

      Можно и ардуину использовать, если есть. Для этого на ней надо замкнуть RST на GND, а ESP подключить следующим образом: RX->RX, TX->TX. Несмотря на различные напряжения логических уровней (5В у ардуино и 3,3В у ESP) на практике все прекрасно работает.


  1. ed007
    16.02.2017 21:35
    +1

    А есть у кого irobot roomba? — хочется коды пульта


    1. dlinyj
      17.02.2017 11:02

      Развлекался управлением робота, когда работал в Виртурилке.


  1. apple01
    16.02.2017 22:35

    спасибо, полезная вещь в хозяйстве


  1. N0Good
    16.02.2017 23:12
    +1

    играюсь с этим уже не первый день. Основная проблема в том, что для сплита пульт шлёт не тупо команды а-ля включить, выключить, температуру выше-ниже, а код состояния с уже указанной температурой и доп опциями (типа турборежим или работа направляющих). Причём для включения-выключения используется повтор последнего состояния. Да и разобрать что в отосланном коде что ощначает тоже непросто.


    1. apple01
      17.02.2017 03:28
      +1

      погуглил, окызавается продают универсальные пульты которые можно запрограммировать и даже приложения для телефона, а коды уже собраны для разных брендов, один из примеров здесь http://removeandreplace.com/2016/06/17/universal-air-conditioner-remote-control-codes/.


    1. natan555
      17.02.2017 09:28
      +1

      https://geektimes.ru/post/258064/
      http://www.instructables.com/id/Reverse-engineering-of-an-Air-Conditioning-control/?ALLSTEPS
      Могу поделится наполовину разобранным кодом от пульта Samsung ARH-465


  1. borisxm
    17.02.2017 09:28
    +1

    Схема несколько кривовата. Ток через светодиоды нужно ограничивать.


  1. mitgard
    17.02.2017 09:29
    +1

    От напряжения 3.3v светодиод так-же нормально работает.

    Через диод протекает некий ток, и при 3.3V очевидно он будет меньше. А значит надо смотреть какая будет интенсивность излучения на получившемся токе. Посмотрите как рассчитать диод — это не сложно.
    И вообще отсутствие резистора последовательно с диодом- плохая практика.


    1. mitgard
      17.02.2017 09:57

      Забыл добавить, что ток коллектор-эмиттер тоже надо ограничить.


    1. KonstantinSoloviov
      17.02.2017 10:54

      Добавим так же, что включать диоды параллельно — не комильфо из-за крутых и нелинейных ВАХ — один всегда страдает больше другого.

      Итого: либо к каждому диоду добавить по токоограничительному резистору, либо включаем диоды последовательно, но все равно с резистором.

      Почему «оно и так работает» — объяснить просто:
      (1) импульсный режим (диод сгореть не успевает, сколько проживет — вопрос),
      (2) внутреннее сопротивление источника (которое задает предельный ток).


  1. gibson_dev
    17.02.2017 10:09
    +3

    Было бы не плохо все таки ESP сразу в бризер воткнуть. У него же есть расширение MAC позволяющее использовать MagicAir — значит все выводы по управлению уже есть (может там и uart) банальный


  1. dlinyj
    17.02.2017 11:02
    +1

    Хорошо бы видео, да скриншоты странички. А за идею спасибо!


  1. lingvo
    17.02.2017 18:56

    А нет готового продукта, который выполнял бы такую функцию? Желательно бы с поддержкой протокола MQTT.


  1. basicus
    18.02.2017 09:17
    +1

    Для управления кондиционером Gree сделал похожее устройство.
    Сначала тоже как Вы пытался сделать через чтение кодов пульта, но не вышло. Как оказалось там идёт две посылки, и они не являются повтором одного и того же кода (это я узнал уже позже). Да и этот метод не подходит для кондиционеров, там надо управлять параметрами. Недостаточно записать несколько состояний.
    Также у меня стояла задача интегрировать управление кондиционером в умный дом wirenboard. Это требует поддержки mqtt.


    В результате удалось всё реализовать: кондиционер полностью интегрирован в интерфейс управления умным домом, я могу управлять температурой, скоростью, режимом. Всё стабильно работает с декабря на даче. Так как основное отопление дачи кондиционер стало удобно: уезжаем — температура 20, вечером за день до приезда — включаю на 27. Дом прогревается. Управляю в основном с мобильного, через сайт.


    Для решения задачи использовал как и Вы ESP-01, но к ножкам чипа не подключался. Я использовал уже выведенные пины. Для решения проблемы с запуском после включения подбирал резисторы подтягивающие. Ещё были сложности с подбором резисторов цепи IR чтобы можно было управлять кондиционером хотя бы с метров 2-3, так как возможности повесить ESP прямо на кондиционер нет.


    Что касается управления кондиционером, то нашёл таки на гитхаб код под Gree, использовал его. Еще позже нашёл библиотеку с поддержкой большего количества производителей кондиционеров. Так же использовал библиотеку mqtt для соединения в wirenboard. В mqtt умного дома создал необходимые топики. Esp при подключении подписывается на них и соответственно выставляет режим.
    Чтобы все контролы объединить в интерфейсе, пришлось модифицировать код Homeui wirenboard. Для этого пришлось немного изучить angular.


    Изначально хотел написать статью и выложить код на github, но нехватает времени.


    1. LexB
      18.02.2017 09:32

      Спасибо за развернутый комментарий.

      но к ножкам чипа не подключался. Я использовал уже выведенные пины. Для решения проблемы с запуском после включения подбирал резисторы подтягивающие
      К слову полезное замечание. Почему-то по подключению светодиодов много комментариев, а об этой проблеме никто не упомянул.
      Изначально хотел написать статью и выложить код на github, но нехватает времени.
      Все-же надеюсь что время найдется.


      1. basicus
        18.02.2017 19:46

        В каком ключе будет интересна статья?
        Решение проблемы интеграции кондиционера в систему умный дом на основе mqtt (wirenboard) или что-то большее?


        1. LexB
          18.02.2017 19:57

          Просто выгрузите свои наработки с небольшими пояснениями. Наверняка кому-то пригодится.


    1. SolarW
      19.02.2017 05:34
      -1

      Интересно было бы увидеть ссылку на библиотеку с поддержкой большого количества кондиционеров.


    1. andjel
      22.02.2017 16:31
      +1

      Поделитесь сслками на Гитхаб итп пожалуйста