esp8266实现红外遥控收发 实现homekit+siri控制灯光+空调

本文共有6824个字,关键词:

前言 (feihua

前两天研究了一下homekit可以模拟一个开关实现siri语音控制,但是模拟的毕竟是模拟的,我要把它变成现实。
先拿主灯下手。晒一下装备,这是个红外线解码仪,很久之前淘宝买着玩的,要控制对着按就能显示数据码。支持多种制式可以选择。
除了空调遥控,目前还没遇到解不出来的。(空调遥控由于编码很长,这个也没法解)
解码仪.jpeg

接下来普及一下红外调制方式,一般红外线是38khz载波发送代表1,不发数据代表0.网上找了个图,应该很形象表示调制解调的过程。
ggtA51PHfACaRc0kSc7.gif

接下来就进入主题了,如何用esp8266发送和接受红外编码。
首先我们先去官网下载arduino IDE https://www.arduino.cc/en/Main/Software?setlang=cn#
我这是macos 所以下载的macos版本
当然esp8266也有官方固件烧写工具,但是arduino里面现成的库很全面,所以这里选用他

下载好我们还不能烧写固件,因为这个是给arduino用的,我们要添加esp8266开发板,
如图在首选项 -> 附加开发板管理器网址里面填入
http://arduino.esp8266.com/stable/package_esp8266com_index.json
2019-08-05T14:02:56.png

接着在工具-> 开发板 -> 点击开发板管理器->滚到最下面找到esp8266 by ESP8266 Community安装
2019-08-05T14:06:00.png

到这里,工具-> 开发板里面应该会有esp8266选项,选择我们的型号就可以开始esp8266编程了。
2019-08-05T14:06:36.png


硬件部分

首先焊接模块,需要注意的是,esp8266是3.3v供电。我这里选用ams1117把5v变成3.3v,按照最小系统把电路组装起来。
吐槽一下,esp8266脚间距比正常的小,我这里用了个转接板。上万能板。
2019-08-05T14:14:53.png

我们这里用了两个1.4v的红外LED串联,接在GPIO14口,红外接收器接在GPIO15
板载的蓝色LED在GPIO2口,可以直接展示信息,但是不要作为输入口用,可能会有干扰。

需要注意的是GPIO9GPIO10口,不要碰,那两个是连寄存器的,碰了程序会崩。
也不是完全不能用,好像flash mode调到非QIO就不会占用那两个口,具体没研究。
GPIO6~GPIO11不要使用,否则引起存储错误,不停重启;
GPIO0GPIO2GPIO15也都不要使用。
对于ESP-12模块,板载灯在GPIO2,也是低电(LOW)平点亮。
GPIO16只能做为输出,不能输入,否则也会引起错误

最后成品是这样。拨动开关是写入程序用的,微动开关是复位用的。
正面.jpeg
背面.jpeg

通过3.3v的ttl线,和电脑连接。

软件部分

我们在工具 -> 管理库中搜索IRremoteESP8266安装。
more info里面打开github页面,可以找到示例代码

我们找到这个红外发射代码放如IDE中,这里写的推荐4脚,但是官方写的推荐14脚,不知道信谁的,我这里选择14脚。

#include <Arduino.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <IRrecv.h>
#include <IRutils.h>

IRsend irsend(14);
IRrecv irrecv(4);
decode_results results;

void setup() {
  Serial.begin(115200,SERIAL_8N1,SERIAL_TX_ONLY);
  irsend.begin();
  irrecv.enableIRIn();  // Start the receiver
}

void dump(decode_results *results) {
  serialPrintUint64(results->value, 16);
  Serial.print("\n{ ");
  for (uint16_t i = 1; i < results->rawlen; i++) {
    if (i != 1)
      Serial.print(", ");
    Serial.print(results->rawbuf[i] * kRawTick, DEC);
  }
  Serial.println("};");
}

void loop() {
  if (irrecv.decode(&results)) {
    dump(&results);
    irrecv.resume();
  }
//  delay(1000);
//  irsend.sendNEC(0x41B6649B, 32);
//  delay(1000);
}


/* 付自家nec灯遥控编码 CH1 | CH2
 *  41B6659A  41B6649B  全灯
 *  41B63DC2  41B63CC3  夜灯
 *  41B67D82  41B67C83  关灯
 *  41B6DD22  41B6DC23  变暗
 *  41B65DA2  41B65CA3  变亮
 */

刷入发射红外可以看到解码仪上有显示


踩坑记录,红外收码没法使用,刚刚看到有人说GPIO15口不要用,我刚好收码放到15口。哎。。。
明天去学校改下电路。。。


电路改完回来了,把红外接收的VS1838B换到GPIO4口,果然能接收数据了。
我这里遥控器全灯编码是41B6649B其中前4位是用户码,5-6位是数据码,7-8位是数据码反码
5-6位数据码 加 7-8位数据码反码 要等于 FF

不过有个问题,就是esp8266接收的遥控器是用户吗是41B6
但是解码仪显示的是826D,我们分析一下两个数值的二进制找一下关系
41B6 = 0100 0001 1011 0110
826D = 1000 0010 0110 1101
可以明显发现是 每个字节 逆序了。也就是二进制每8位逆序。
我们逆逆得顺就能把41B6还原成826D
后面数据码同理。
具体js计算方法如下,输入c('826d26')得到“41b6649b”,还能自动加上补码

cc = (a)=>parseInt(('00000000'+parseInt(a,16).toString(2)).slice(-32).replace(/.{8}/g,(a)=>a.split('').reverse().join('')),2).toString(16).replace(/.{2}$/,(a)=>a+('00'+(255-parseInt(a,16)).toString(16)).slice(-2))

加上控制空调代码,以及能接收红外编码

#include <Arduino.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <IRrecv.h>
#include <IRutils.h>

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>

const char *ssid = "ASUS";
const char *password = "Aa+121212";
ESP8266WebServer server(80);

IRsend irsend(14);
IRrecv irrecv(4, 1024, 50, true);
decode_results results;

void setup() {
  pinMode(2, OUTPUT);
  bool pin2 = false;

  Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY);
  irsend.begin();

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    pin2 = !pin2;
    digitalWrite(2, pin2);
    Serial.print(".");
  }
  digitalWrite(2, 1);

  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  if (MDNS.begin("esp8266")) {
    Serial.println("MDNS responder started");
  }

  server.on("/send_nec", handle_SEND_NEC);
  server.on("/read_nec", handle_READ_NEC);
  server.on("/send_daikin", handle_SEND_DAIKIN);
  server.on("/read_daikin", handle_READ_DAIKIN);
  server.begin();
  Serial.println("HTTP server started");
}


bool handle_IR(unsigned int delayTime = 5000) {
  unsigned long startMillis = millis();
  irrecv.enableIRIn();
  while (!irrecv.decode(&results)) {
    if ((millis() - startMillis >= delayTime)) {
      irrecv.disableIRIn();
      return false;
    }
    delay(100);
  }
  irrecv.disableIRIn();
  return true;
}

void handle_READ_NEC() {
  digitalWrite(2, 0);
  if (!handle_IR()) {
    server.send(200, "text/plain", "no recv");
  } else {
    String code = uint64ToString(results.value, 16);
    server.send(200, "text/plain", code);
    Serial.println(code);
  }
  digitalWrite(2, 1);
  irrecv.resume();
}

void handle_READ_DAIKIN() {
  digitalWrite(2, 0);
  if (!handle_IR()) {
    server.send(200, "text/plain", "no recv");
  } else {
    String code = resultToHexidecimal(&results);
    server.send(200, "text/plain", code);
    Serial.println(code);
  }
  digitalWrite(2, 1);
  irrecv.resume();
}

void handle_SEND_NEC() {
  digitalWrite(2, 0);
  uint32_t code = strtoul(server.argName(0).c_str(), NULL, 16);
  irsend.sendNEC(code);
  server.send(200, "text/plain", "ok");
  digitalWrite(2, 1);
}

void handle_SEND_DAIKIN() {
  digitalWrite(2, 0);
  String code_str = server.argName(0);
  uint8_t code_byte = code_str.length() / 2;
  uint8_t code_array[kStateSizeMax] = {0};
  for (int i = 0; i < code_byte; i++) {
    code_array[i] = strtoul(code_str.substring(2 * i, 2 * i + 2).c_str(), NULL, 16);
  }
  irsend.send(decode_type_t::DAIKIN, code_array, code_byte);
  server.send(200, "text/plain", "ok");
  digitalWrite(2, 1);
}

void loop(void) {
  server.handleClient();
  MDNS.update();
}

参考:

  1. Android开发笔记(一百六十五)利用红外发射遥控电器

「一键投喂 软糖/蛋糕/布丁/牛奶/冰阔乐!」

pch18

(๑>ڡ<)☆谢谢老板~

使用微信扫描二维码完成支付

版权声明:如无特别说明,本文为作者原创,如需转载须联系作者本人同意,未经作者本人同意不得擅自转载。
添加新评论
暂无评论