前言

2021夏天刷爆朋友圈的朋友圈空调大家有没有玩过呢?再来看一眼:
000.png
好玩归好玩,并没有实际的用处,娱乐而已。那有没有办法将这个程序和家里的空调交互,实现真正的空调控制呢?

红外解码

正好之前用单片机做过一个远程电视遥控器,两者大同小异,都是38kHz红外载波,无非就是红外编码不一样,将空调的编码进行解码就可以了。

红外接收

家里的空调是美的的,理论上美的的所有空调编码应该是通用的。我们用VS1838红外接收头接收空调遥控器发出的红外信号,并用示波器抓取波形。接收电路如图:
VS1838.png
示波器抓取VS1838的PIN 1,波形如图:
1.png

从波形不难看出,波形的前半部分和后半部分一样,而完整的信号应该包含起始位信号,数据信号,分隔信号,数据信号,如图:
2.png

波形分析

起始信号

接下来我们逐个分析,起始信号:
3.png
起始信号由4.3ms低电平4.6ms高电平组成。

数据信号

数据波形:
4.png
从波形来看,我们发现波形变化的规律,即低电平时间几乎一样,而高电平则有长和短两种。如果有逻辑分析仪就能很快解析出数据,咱条件有限只能自己分析。我们知道电路数据只有高电平和低电平两种状态,对应1和0,所以我们忙猜波形高电平的时间长的表示1,时间短的表示1(如果最终数据不正确则反过来,高电平的时间长的表示0),如下图:
5.png
可以看到每一位数据由一段时间的低电平开始,然后由高电平来区分0和1。接下来我们抓取低电平和高电平的时间,低电平:
6.png
可以看到每次低电平的时间都不一样,但都在560us左右,考虑误差,所以我们多抓几个波形测量最终取众数560us。高电平:
7.png
同理,多次抓取波形取众数1.60ms。
知道了数据的变化规律,我们就可以读出数据了,这里我们留到最后说。

分隔信号

接下来再看两个数据信号之间的分隔时间:
8.png
至此一个完整的信号已经分析完毕,一个信号包含的信息格式为:
起始信号 数据信号 分隔信号 数据信号

数据解析

知道了波形的组成,我们再来分析数据信号的内容:
空调遥控器显示的内容为制冷17℃风速1级无定时,按下空调打开按键抓出波形并读出数据:

起始101100100100110110011111011000000000000011111111
分隔1011001001001101 10011111011000000000000011111111

可以看到两个数据是一样的,所以应该是为了保证数据传输稳定才发了两组数据用于检验,后续我们只读出一个数据。不同温度读出数据如下:

制冷18℃风速1级无定时:1011 0010 0100 1101 1001 1111 0110 0000 0001 0000 1110 1111   
制冷25℃风速1级无定时:1011 0010 0100 1101 1001 1111 0110 0000 1100 0000 0011 1111  
制冷26℃风速1级无定时:1011 0010 0100 1101 1001 1111 0110 0000 1101 0000 0010 1111  
制冷27℃风速1级无定时:1011 0010 0100 1101 1001 1111 0110 0000 1001 0000 0110 1111   
制冷28℃风速1级无定时:1011 0010 0100 1101 1001 1111 0110 0000 1000 0000 0111 1111   
制冷29℃风速1级无定时:1011 0010 0100 1101 1001 1111 0110 0000 1010 0000 0101 1111   
制冷30℃风速1级无定时:1011 0010 0100 1101 1001 1111 0110 0000 1011 0000 0100 1111 

将每4位分开,共有12组不同温度下数据变化的是第9组和第11组,并且第11组的数据是第9组数据的反码
再来看看不同风速下的数据:

制冷25℃风速自动无定时:1011 0010 0100 1101 1011 1111 0100 0000 1100 0000 0011 1111  
制冷25℃风速1级无定时:1011 0010 0100 1101 1001 1111 0110 0000 1100 0000 0011 1111  
制冷25℃风速2级无定时:1011 0010 0100 1101 0101 1111 1010 0000 1100 0000 0011 1111  
制冷25℃风速3级无定时:1011 0010 0100 1101 0011 1111 1100 0000 1100 0000 0011 1111  

不同温度下数据变化的是第5组和第7组,并且第7组的数据是第5组数据的反码
按照这种方法我们分别抓取不同模式温度风速等功能,总结出数据组成为:

Index数据
11#
22#
31#反码
42#反码
5风速
6定时
7风速反码
8定时反码
9温度
10模式
11温度反码
12模式反码

前4组数据暂时不知道是什么,可能是固定的用户编码,也可能是别的数据,我家空调遥控器不管按哪个键前4组数据都不会变化,所以我就暂时把它当成固定值。

最终,解析出数据如下:

温度:
温度数据
17℃0000
18℃0001
25℃1100
26℃1101
27℃1001
28℃1000
29℃1010
30℃1011

我以为这个温度是按规律+1变化的,实际不是,所以没抓中间的数据,这里留下一个坑以后有机会再重新抓波形!

模式
制冷制热
00001100
风速
风速数据
自动0100
1级0110
2级1010
3级1100
定时
时间数据
30分钟0001
1小时0011
1H30m1011
2小时0111
关定时1111

总结

红外解码至此已经全部解析出来了,把想要的数据任意组合发送红外信号就能实现对应的功能。但是直到写下这篇文章才发现几个坑:

  1. 温度数据的编码没有全部抓出。
  2. 只抓出开机的波形,关闭空调的波形没有抓。

以后有机会再重新抓吧!后续的单片机与网页交互实现远程控制空调我有时间再整理(估计要等到下辈子了,懒)!
实现方案为ESP8266EX芯片使用MQTT协议连接阿里云物联网平台,在原网页的基础上再编写HTTP API接口调用阿里云的接口实现指令下发至MCU,MCU根据指令发出38kHz红外信号控制空调,顺便接入天猫精灵实现语音控制。

附上STM32生成38kHz代码:

//用TIM2通道1产生38KHz频率;对应的MCU管脚是PA0,默认复用功能;
void TIM2_PWM_Init(u16 arr,u16 psc)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;
    
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);    //使能定时器2时钟
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟,必须先开启辅助功能时钟
    
    //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; 
//    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出 注意
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO
    
    //初始化TIM2
    TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
    TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 
    TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    
    //初始化TIM2 Channel1 PWM模式     
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2 ??? 配置为PWM模式1
     TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
    //新增
    TIM_OCInitStructure.TIM_Pulse = 75;       //设置跳变值,当计数器计数到这个值时,电平发生跳变//86(84~90) //91(90~92)   //60(5~86) //60(10~86)
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高 当定时器计数值小于pulse时为高电平
    
    TIM_OC1Init(TIM2, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM2 OC1 使能通道1
    TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);  //使能TIM2在CCR2上的预装载寄存器
 
//  TIM_OC2Init(TIM4, &TIM_OCInitStructure);     //使能通道2
//  TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);
//    
//  TIM_OC3Init(TIM4, &TIM_OCInitStructure);     //使能通道3
//  TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);
//  
//  TIM_OC4Init(TIM4, &TIM_OCInitStructure);     //使能通道3
//  TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);
 
    TIM_ARRPreloadConfig(TIM2, ENABLE);             // 使能TIM2重载寄存器ARR ???
  
    TIM_Cmd(TIM2, ENABLE);  //使能TIM2
}


int main(void)
{
    ......
    TIM2_PWM_Init(99,18);//在PA0产生38KHz波特率
    //在主函数里面根据需要加上设置占空比,也可以不加上;
    TIM_SetCompare1(TIM2,50);         
    .......
}
最后修改:2022 年 10 月 28 日,缓存于2025-01-16 18:55:14
看都看了,点个赞吧!