
在本文中,我将介绍我的经验以及从空调研究红外遥控器的主要阶段。 从该工具中,您将需要mega328上的Arduino nano和IR信号接收器(我有VS1838B)。
一点背景我决定将我的伊莱克斯空调纳入智能家居系统中。 为此,有3种选择:使用Broadlink红外发射器,使用自制IR发射器以及将自制电路集成到空调本身中。 对于实验用空调而言,最安全,最便宜的是临时IR模块。
首先,您需要解析IR协议。 互联网上的搜索仅产生1个结果,该结果不合适,因为另一家公司的空调:
link 。 他拿了示波器,看了什么。 原来,该协议与NEC相似,但是该软件包太长了,以致无法装入我的USB示波器的缓冲区中。 没关系,我选择了arduino,找到了通用的IRremote库,然后……我意识到它不理解长度超过32位的命令,并且在6字节(48位)的行中至少包含3个部分。
我无法修复该库,这对我来说太复杂了。 折磨了一下之后,他写了一段代码,读取信号变化之间的时间间隔。 我在arduino方面不强,因此该代码可能歪曲了,并保证可以在具有ATMega328 MK和16 MHz频率的arduino上工作。 实际上,我可以用CVAVR编写更漂亮的代码,但是并不是每个人都可以重复我的经验,因为我需要程序员,所以我决定在arduino上全部完成。 我将从模型的功能开始。 我范围内的某些空调具有“我感觉”功能,该功能旨在确保在房间的那部分达到设定温度? 我在哪里(实际上是远程用户)。 文档指出在我的模型中确切没有什么功能,但事实证明确实如此。 事实证明,遥控器的电池耗尽了,遥控器开始将温度升高4度,即 而不是28,而是显示32。我将温度设置为28度,并将公寓冷却至26度。好的,我决定将冷却度设置为32度(这是上限),但是空调顽固地继续为房间制冷。 我认为有些东西坏了,或者温度传感器的触点被氧化并拆除了空调,以寻求故障。 没发现任何东西,以防万一,我决定更换电池,瞧瞧,空调开始正常工作了。 那么这是如何工作的呢? 遥控器和所有设置一起向空调发送测量的温度,并根据遥控器发送的内容来校正内部温度传感器的读数。
首先,我编写了一个代码,该代码读取处于低态和高态的IR接收器的信号持续时间,将其写入阵列,然后将其输出到计算机端口。
代号// IR D2 unsigned int timerValue; // unsigned int data_m[350]; unsigned int i=0; unsigned int n=0; byte temp; void setup() { Serial.begin(115200); // , 115200 // 1 TCCR1A = 0; TCCR1B = 0; attachInterrupt(0, inter_1, CHANGE); // 0- inter_1 TCCR1B = 2; // Serial.println("Start"); pinMode(2, INPUT); } void loop() { temp=TIFR1&0x01; // if (temp!=0) { TIFR1=0x01; // if (i!=0) {while (n<=i) // { Serial.print(n,DEC); Serial.print("="); Serial.print(data_m[n],DEC); Serial.print("\t"); if ((n&B00000011)==0) { Serial.println(" ");} n++; } Serial.println("End"); } i=0; // n=0; } } void inter_1() { timerValue = (unsigned int)TCNT1L>>1 | ((unsigned int)TCNT1H << 7); // data_m[i]=timerValue; i++; TCNT1H = 0; // TCNT1L = 0; }
单击空调遥控器的按钮后,数据进入端口:
包裹接收结果Start
0=20543
1=9038 2=4541 3=548 4=1675
5=548 6=1681 7=545 8=560
9=543 10=614 11=494 12=569
13=543 14=620 15=493 16=624
17=494 18=1688 19=544 20=603
21=494 22=1655 23=571 24=1685
25=545 26=613 27=495 28=568
29=543 30=622 31=493 32=625
33=495 34=613 35=494 36=604
37=494 38=607 39=493 40=561
41=545 42=612 43=495 44=620
45=491 46=622 47=494 48=624
49=494 50=565 51=543 52=602
53=494 54=1679 55=547 56=609
57=494 58=614 59=493 60=619
61=492 62=1693 63=546 64=1698
65=547 66=1685 67=548 68=603
69=494 70=608 71=493 72=609
73=495 74=615 75=493 76=571
77=540 78=620 79=494 80=626
81=493 82=565 83=545 84=605
85=492 86=607 87=493 88=613
89=492 90=596 91=512 92=619
93=492 94=622 95=493 96=624
97=494 98=594 99=521 100=7988
101=550 102=1674 103=546 104=608
105=492 106=1685 107=543 108=611
109=495 110=1689 111=547 112=620
113=495 114=625 115=491 116=1689
117=543 118=605 119=493 120=1654
121=572 122=611 123=494 124=614
125=493 126=1692 127=545 128=621
129=492 130=622 131=496 132=613
133=492 134=604 135=494 136=605
137=496 138=556 139=548 140=614
141=493 142=618 143=494 144=620
145=494 146=624 147=494 148=615
149=493 150=606 151=493 152=608
153=493 154=609 155=496 156=614
157=494 158=566 159=545 160=623
161=493 162=625 163=493 164=564
165=543 166=603 167=494 168=606
169=495 170=609 171=496 172=613
173=494 174=617 175=495 176=620
177=494 178=624 179=496 180=613
181=494 182=604 183=493 184=606
185=494 186=562 187=541 188=613
189=495 190=618 191=493 192=622
193=493 194=623 195=494 196=1663
197=569 198=603 199=494 200=1678
201=547 202=1686 203=543 204=1663
205=570 206=1692 207=545 208=619
209=494 210=624 211=495 212=614
213=494 214=1653 215=569 216=1657
217=571 218=611 219=493 220=1664
221=571 222=1691 223=544 224=1671
225=571 226=1699 227=547 228=1671
229=572 230=7995 231=551 232=603
233=493 234=606 235=493 236=612
237=493 238=608 239=497 240=618
241=494 242=621 243=492 244=626
245=493 246=584 247=524 248=556
249=541 250=1680 251=545 252=610
253=494 254=613 255=495 256=588
257=523 258=620 259=494 260=625
261=494 262=615 263=493 264=604
265=495 266=607 267=495 268=612
269=493 270=616 271=493 272=570
273=542 274=621 275=495 276=625
277=493 278=612 279=496 280=606
281=493 282=607 283=496 284=610
285=494 286=614 287=495 288=618
289=493 290=595 291=519 292=575
293=543 294=616 295=494 296=605
297=495 298=606 299=495 300=613
301=493 302=613 303=494 304=616
305=495 306=569 307=546 308=625
309=493 310=564 311=545 312=602
313=496 314=607 315=495 316=611
317=494 318=613 319=496 320=571
321=541 322=621 323=494 324=624
325=495 326=568 327=539 328=604
329=493 330=1679 331=547 332=610
333=495 334=614 335=493 336=618
337=495 338=572 339=543 340=623
341=496 342=596 343=484 344=0
完结
我们可以注意到,所有奇数数据都是相同的(除了起始脉冲),并且可以忽略。 此外,在中断代码中,存在一个相当的if(digitalRead(2)== 0)条件,该条件会丢弃控制器输入上的低状态持续时间。
void inter_1() { timerValue = (unsigned int)TCNT1L>>1 | ((unsigned int)TCNT1H << 7);
通常,该程序的工作方式如下:以8的除法系数(这是MC中提供的标准除法器之一)启动计时器,并且当输入D2的状态更改时,将执行中断-void inter_1()函数。 在此中断中,将读取计时器计数器的值并将其除以2,然后将其写入数组,并重置计时器本身。 计时器的工作频率比MK(16 MHz)的时钟频率低8倍。 要获得2 MHz的时间(以微秒为单位),必须将从计数器中读取的数字除以2。在程序主体中,将检查定时器溢出标志以及定时器计数器是否已满,即 计数到65535时,将检查接受间隔的计数器。 如果它不同于0,则显示所有接收到的数据,并且重置接收字节的计数器。 新数据如下所示:
命令结果Start
0=26938
1=4539 2=1675 3=1654 4=610
5=563 6=569 7=567 8=621
9=1687 10=602 11=1679 12=1658
13=613 14=615 15=619 16=576
17=561 18=602 19=591 20=607
21=611 22=615 23=620 24=622
25=611 26=602 27=1678 28=609
29=612 30=615 31=1693 32=1695
33=1659 34=601 35=605 36=610
37=611 38=566 39=564 40=622
41=611 42=601 43=604 44=608
45=614 46=615 47=618 48=620
49=594 50=7970 51=1676 52=604
53=1681 54=613 55=1688 56=620
57=623 58=1685 59=602 60=1680
61=1680 62=612 63=1687 64=619
65=624 66=611 67=602 68=604
69=607 70=558 71=616 72=619
73=624 74=560 75=603 76=605
77=562 78=611 79=615 80=566
81=622 82=612 83=601 84=603
85=606 86=611 87=564 88=618
89=572 90=612 91=601 92=604
93=608 94=611 95=569 96=621
97=622 98=1686 99=552 100=1678
101=1681 102=1660 103=1689 104=618
105=622 106=610 107=1675 108=1677
109=1681 110=1681 111=1691 112=1688
113=1696 114=1668 115=7966 116=599
117=603 118=562 119=558 120=612
121=617 122=621 123=565 124=601
125=1679 126=607 127=612 128=616
129=565 130=622 131=613 132=602
133=604 134=608 135=612 136=615
137=618 138=617 139=611 140=598
141=553 142=607 143=612 144=615
145=617 146=568 147=565 148=600
149=604 150=606 151=559 152=613
153=617 154=622 155=609 156=549
157=605 158=609 159=611 160=613
161=618 162=621 163=609 164=602
165=1679 166=609 167=612 168=615
169=618 170=622 171=595 172=0
End
根据获得的数据,很明显第一个数字是随机的-这是从最后一个定时器溢出到开始发送的时间。 接下来,一个4.5ms的起始脉冲和数据。 数据逐位传输,大约1690μs的间隔对应于一个逻辑单元,而560μs的间隔对应于一个逻辑零。 还可以看到,包裹被分为3个独立的部分,其中50 = 7970和115 = 7966是起始序列。
在代码中添加字节生成功能和对接收到的数据的少量解密。 我在最后写了最后一行,但没有产生几乎相同的代码。
Arduino代码 // IR D2 unsigned int timerValue; // unsigned int data_m[250]; byte i=0; byte n=0; byte temp; byte k=0; byte x,y; byte m1=0; byte dat[4][12]; byte temp2=0; char s[5]; void setup() { Serial.begin(115200); // , 9600 // 1 TCCR1A = 0; attachInterrupt(0, inter_1, CHANGE); // 0- inter_1 TCCR1B = 2; // 8 Serial.println("Start"); pinMode(2, INPUT); } void loop() { temp=TIFR1&0x01; // if (temp!=0) { TIFR1=0x01; // if (i!=0) { if (k==0) // FF {dat[y][x]=0xFF;} else {dat[y][x+1]=0xFF; dat[y][x]=m1; } /* while (n<=i) // { Serial.print(n,DEC); Serial.print("="); Serial.print(data_m[n],DEC); Serial.print("\t"); if ((n&B00000011)==0) { Serial.println(" ");} n++; }*/ for (int i1 = 0; i1 < 3; i1++) { for (int j = 0; j < 9; j++) { sprintf(s, "%02X ", dat[i1][j]); Serial.print(s); //dat[i1][j]=0; } Serial.println(""); } if ((dat[0][3]&0x0F)==0x2) Serial.print("Cool "); if ((dat[0][3]&0x0F)==0x0) Serial.print("Heat "); if ((dat[0][3]&0x0F)==0x3) Serial.print("Dry "); if ((dat[0][3]&0x0F)==0x04) Serial.print("Vent "); if ((dat[0][2]&0x03)==0x0) Serial.print("Vent=Auto "); if ((dat[0][2]&0x03)==0x1) Serial.print("Vent=Max "); if ((dat[0][2]&0x03)==0x2) Serial.print("Vent=Mid "); if ((dat[0][2]&0x03)==0x3) Serial.print("Vent=Min "); temp=((dat[0][3]&0xF0)>>4)+18; Serial.print("T="); Serial.print(temp,DEC); sprintf(s, " Time=%02d:%02d ",(dat[1][0]&0x7F),(dat[1][1]&0x7F)); Serial.print(s); Serial.print(" C="); temp=(dat[1][6]); Serial.println(temp,DEC); Serial.println("End"); // for (int i1 = 0; i1 < 3; i1++) { for (int j = 0; j < 9; j++) {dat[i1][j]=0;} } } i=0; // n=0; k=0; m1=0; x=0; y=0; } } void inter_1() { timerValue = (unsigned int)TCNT1L>>1 | ((unsigned int)TCNT1H << 7); // 2, .. 16, 8 2 if (digitalRead(2)==0) { data_m[i]=timerValue; i++; if ((timerValue>4400)&&(timerValue<4700)) {m1=0; k=0;} if ((timerValue>7500)&&(timerValue<8500)) {if (k==0) // FF {dat[y][x]=0xFF;} else {dat[y][x+1]=0xFF; dat[y][x]=m1; } x=0; y++; } if ((timerValue>1500)&&(timerValue<1800)) {m1=(m1>>1)+0x80; k++;} if ((timerValue>450)&&(timerValue<800)) {m1=m1>>1; k++;} if (k>=8) {k=0; dat[y][x]=m1; x++; m1=0; } } TCNT1H = 0; // TCNT1L = 0; }
值得注意的是,阵列的大小适合于我的遥控器,为了研究新的遥控器,应该对其进行扩展,以使所有组件完全合适。 还值得检查包中的位数,例如,我有50-2 = 48个第一个包,115-51 = 64和172-116 = 56(我从最后一个无关紧要的位(第一个有效位中减去)。 总共我们得到6个字节,8个字节和7个字节。 由于所有3种包装的长度不同,因此我决定用FF值标记包装的末端,因为在测试面板上几乎找不到这种数据。
正如我已经提到的那样,我的空调具有“我感觉”功能,其工作方式如下:遥控器每9分钟将温度和所有设置一起发送到空调,如果温度在遥控器区域内,它将根据发送的内容校正内部温度传感器的读数。遥控器。
顺便说一句
自动发送的命令Start
83 06 00 82 00 00 FF 00 00
16 30 00 00 00 80 1D 39 FF
00 00 00 00 00 00 00 FF 00
Cool Vent=Auto T=26 Time=22:48 C=29
End
83 06 00 82 00 00 FF 00 00
16 31 00 00 00 80 1D 38 FF
00 00 00 00 00 00 00 FF 00
Cool Vent=Auto T=26 Time=22:49 C=29
End
83 06 00 82 00 00 FF 00 00
16 3A 00 00 00 80 1D 33 FF
00 00 00 00 00 00 00 FF 00
Cool Vent=Auto T=26 Time=22:58 C=29
End
83 06 00 82 00 00 FF 00 00
17 07 00 00 00 80 1D 0F FF
00 00 00 00 00 00 00 FF 00
Cool Vent=Auto T=26 Time=23:07 C=29
End
然后,一切都是最简单,最有趣的-按下按钮,我们得到结果并尝试猜测是什么,负责任的。 事实证明,我的空调中的位是从最小的那个开始传输的。 我设法解密了大部分协议。
伊莱克斯空调远程协议说明
0.1字节 0x83 0x06显然是地址
2字节 0b00000000工作模式
如果按下摆动按钮,则7位= 1
漏极模式下的6 5 4位负责“电源” 110 = -7 ...- 2,101 = -1,000 = 0,001 = 1,010 = 2..7
睡眠模式下3位= 1(同时将风扇设置为最小)
如果按下电源按钮,则设置2位
1和0位对应于风扇模式-00自动,10最高速度,01平均速度,11低速。
3个字节 0b11000010工作模式和温度。 在此示例中,冷却至30度
高4位包含根据公式18 +此处记录的数字的设置温度,例如0b1100 = 12我们将18加30
低4位负责运行模式0010冷却,0000加热,0011排放,0001智能模式
4个字节 0b00000000未知字节
5字节 0b10010000超级冷却模式
在超级冷却模式下,风扇最大,温度为+18,此外,在其余模式下1001的最高4位为零。
前6个字节已完成,然后是高电平状态下8ms +低电平状态下0.5ms的开始序列,以及发送8个字节的第二部分。
0字节 0b10000110当前时间(小时),在此示例中为6小时。
始终设置7位。
5位= 1关闭室内机上的显示。
1字节 0b00000010本示例中的当前时间(分钟)02分钟
当关闭定时器开启时,将设置7位。
2个字节 0b00010111自动关闭时间(小时),此处为23小时。
3字节 0b10111010此处的自动关闭时间(分钟)58
自动唤醒定时器打开时设置7位。
4字节 0b00001100此处的自动开机时间(小时)12小时
5个字节 0b10000010此处的自动开机时间(分钟)2分钟
始终设置7位
6个字节 0b00011111遥控器测量的当前温度,此处为31。
7个字节的 CRC。
我找不到CRC算法。 (如果有人告诉我,我将不胜感激)。 我在在线计算器中尝试了所有建议的算法(10件),但是没有找到合适的算法。 显然,在计算CRC时,要考虑第一行和第二行。 当第一行或第二行中的任何字节更改时,CRC都会更改。huhen成员
提出了一种CRC计算方法:这是对第一个和第二个数据包中除地址之外的所有字节进行的XOR操作。 例如:83 06 60 ^ 73 ^ 00 ^ 00 ^ 00 ^ 00 ^ 96 ^ 04 ^ 00 ^ 00 ^ 00 ^ 80 ^ 1E = 1F
包裹的最后一部分是7个字节
0字节5位软模式
按下4位开关按钮
静音模式开启时设置3位(显示屏上的耳形图标点亮)
1个字节0x00 =自动发送,调光器按钮和时间设置时钟按钮
0x01 =按下电源按钮
0x02 =按下更改设定温度(电源)的按钮+或-0x03 =按下睡眠按钮
0x04 =按下超级冷却按钮
0x05 =自动开机定时器开启或关闭定时器开启
0x06 =按下模式按钮
0x07 =按下摇摆按钮(襟翼摇摆)
按下0x0B静音按钮
按下0x0SOFT(节能)按钮
0x0D我感到沮丧
按下0x0F开关按钮(关闭室内机显示)
0x11按钮按下的风扇模式
0x17 =按下智能按钮(自动操作)
0x1D启用或禁用自动关闭计时器计时器关闭
消耗模式下的
2个字节和智能(自动)负责“电源”,以及第一次发送的2个字节
0x14 = + -7
0x10 = + -6
0x0C = + -5
0x08 = + -4
0x02 = + -3
0x00 = 0,+ -1,+ -2
6个字节 0-5个
字节的校验和(第三行)。
在除湿或智能模式下,空调不设置温度,您只能在7到+7之间选择一个数字。 他们可能对权力负责。 并且该功率在第一封装的第二字节和第三封装的第二字节中传输。
我也有小米红外发射器。 他被强力调至空调,并将第一个6字节的包裹发送给空调。 空调对这种缩短的包装作出响应并正确执行它。 但是我真的不喜欢这个控制选项,因为它不允许空调传递当前温度,从而调节其操作。
然后,我只是单击了按钮。 在数据签名中T =设定温度
C =遥控器测得的当前温度。 通风口=风扇模式。
资料83 06 60 73 00 00 FF 00 00
96 04 00 00 00 80 1E 1F FF
00 02 08 00 00 00 0A FF 00
Dry Vent=Auto T=25 Time=22:04 C=30
End
83 06 01 74 00 00 FF 00 00
96 04 00 00 00 80 1E 79 FF
00 06 00 00 00 00 06 FF 00
Vent Vent=Max T=25 Time=22:04 C=30
End
83 06 00 50 00 00 FF 00 00
96 04 00 00 00 80 1E 5C FF
00 06 00 00 00 00 06 FF 00
Heat Vent=Auto T=23 Time=22:04 C=30
End
83 06 00 82 00 00 FF 00 00
96 04 00 00 00 80 1E 8E FF
00 06 00 00 00 00 06 FF 00
Cool Vent=Auto T=26 Time=22:04 C=30
End
83 06 00 73 00 00 FF 00 00
96 04 00 00 00 80 1E 7F FF
00 06 00 00 00 00 06 FF 00
Dry Vent=Auto T=25 Time=22:04 C=30
End
83 06 01 74 00 00 FF 00 00
96 04 00 00 00 80 1E 79 FF
00 06 00 00 00 00 06 FF 00
Vent Vent=Max T=25 Time=22:04 C=30
End
83 06 00 50 00 00 FF 00 00
96 04 00 00 00 80 1E 5C FF
00 06 00 00 00 00 06 FF 00
Heat Vent=Auto T=23 Time=22:04 C=30
End
83 06 00 82 00 00 FF 00 00
96 04 00 00 00 80 1E 8E FF
00 06 00 00 00 00 06 FF 00
Cool Vent=Auto T=26 Time=22:04 C=30
End
83 06 00 92 00 00 FF 00 00
96 04 00 00 00 80 1E 9E FF
00 02 00 00 00 00 02 FF 00
Cool Vent=Auto T=27 Time=22:04 C=30
End
83 06 00 A2 00 00 FF 00 00
96 04 00 00 00 80 1E AE FF
00 02 00 00 00 00 02 FF 00
Cool Vent=Auto T=28 Time=22:04 C=30
End
83 06 00 B2 00 00 FF 00 00
96 04 00 00 00 80 1E BE FF
00 02 00 00 00 00 02 FF 00
Cool Vent=Auto T=29 Time=22:04 C=30
End
83 06 00 C2 00 00 FF 00 00
96 04 00 00 00 80 1E CE FF
00 02 00 00 00 00 02 FF 00
Cool Vent=Auto T=30 Time=22:04 C=30
End
83 06 00 D2 00 00 FF 00 00
96 04 00 00 00 80 1E DE FF
00 02 00 00 00 00 02 FF 00
Cool Vent=Auto T=31 Time=22:04 C=30
End
83 06 00 E2 00 00 FF 00 00
96 04 00 00 00 80 1E EE FF
00 02 00 00 00 00 02 FF 00
Cool Vent=Auto T=32 Time=22:04 C=30
End
83 06 00 E2 00 00 FF 00 00
96 04 00 00 00 80 1E EE FF
00 02 00 00 00 00 02 FF 00
Cool Vent=Auto T=32 Time=22:04 C=30
End
83 06 00 E2 00 00 FF 00 00
B6 05 00 00 00 80 1E CF FF
00 00 00 00 00 00 00 FF 00
Cool Vent=Auto T=32 Time=54:05 C=30
End
83 06 00 E2 00 00 FF 00 00
96 05 00 00 00 80 1E EF FF
10 0F 00 00 00 00 1F FF 00
Cool Vent=Auto T=32 Time=22:05 C=30
End
83 06 03 E2 00 00 FF 00 00
96 05 00 00 00 80 1E EC FF
04 0B 00 00 00 00 0F FF 00
Cool Vent=Min T=32 Time=22:05 C=30
End
83 06 00 71 80 00 FF 00 00
96 05 00 00 00 80 1E FC FF
00 17 00 00 00 00 17 FF 00
Vent=Auto T=25 Time=22:05 C=30
End
83 06 80 71 00 00 FF 00 00
96 05 40 00 00 80 1E BC FF
00 07 00 00 00 00 07 FF 00
Vent=Auto T=25 Time=22:05 C=30
End
83 06 00 82 00 00 FF 00 00
96 05 00 00 00 80 1E 8F FF
00 06 00 00 00 00 06 FF 00
Cool Vent=Auto T=26 Time=22:05 C=30
End
83 06 04 82 00 00 FF 00 00
96 05 00 00 00 80 1E 8B FF
00 01 00 00 00 00 01 FF 00
Cool Vent=Auto T=26 Time=22:05 C=30
End