Pull to refresh

Управление климатом дешево и сердито (библиотека IRremoteESP8266 и бризер Tion 02)

Reading time6 min
Views21K
Нашел способ подружить кондиционер (или другой прибор управляемым пультом дистанционного управления) и умный дом, либо сделать управление этим устройством, например с сотового.
В теории просто, подключаем 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>



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

Надеюсь данная инструкция кому-то пригодится. Прошу делиться доработанными прошивками.
Tags:
Hubs:
Total votes 18: ↑17 and ↓1+16
Comments22

Articles