如何几块钱红外遥控格力空调?| 附代码

夏天来了,空调成了必备,如何让空调定时器启动/关闭,或者人回来了自动开启空调?

首先要了解的是红外编、解码。

需要购买两个小模块,一个用于接收,一个用于发送,几块钱搞定。

另外需要一个示波器,或者逻辑分析仪,能看到高低电平即可,不需要很高的带宽。如果都没有,那么自己通过单片机也可自行处理解析高低电平数据。

5 V 供电,3.3 V 也能工作,可能距离短一点。

首先我们要了解自己的遥控器编码,简单一点,直接遥控器(型号 YAP0F3)对准接收器,可以看到接收模块灯亮了,同时逻辑分析仪可以清楚知道电平状态:

第一个数据:

第二个数据:

按下一次按键后全部发送数据展示:

使用 nec 解码器

可以看到,按下按键时,发送了两次数据,每次数据 8 字节。有可能不是标准 nes,因此可以看到后面的 4 字节无法解析。

可以看到,有几个元素:

起始位:

低电平持续时间 : 9000us (即 IO 翻转频率 38KHz,驱动红外发射管,此时接收管表现为低电平状态)

高电平持续时间: 4500us  (不驱动红外发射管,设置 0 或 1 都可)

数据位定义(字节内,低位先发送,低字节先发):

bit:0

低电平持续时间 : 650 us 

高电平持续时间: 650 us 

bit:1

低电平持续时间: 650 us 

高电平持续时间 : 1650 us 

共 4*8 + 3 bit,后面的 3bit 为固定时间 0b010

然后是

连接码 1 bit:

低电平持续时间 : 650 us 

高电平持续时间 : 20000us 

之后跟随 32 bit 数据。

最后一个停止码:

低电平持续时间 : 650 us 

高电平持续时间 : 40000us 

一次数据传输结束。

然后又是一个起始码,重复上一次输出,但是数据有所不同,鱼鹰还没搞清楚为什么要发两次不同的码,有了解的道友可以留言讨论一下。

学习红外,都会看到 38Khz 载波,其实对于工程师来说,你只要知道,使用 PWM 38KHz 驱动红外发射管,接收管会显示低电平即可,剩下的就是如何组织数据和时序了。

下面附关键代码:

typedef enum {
    GREE_MODE_AUTO = 0,             // 自动
    GREE_MODE_COOL = 1,             // 制冷
    GREE_MODE_HUMIDIFICATION = 2,   // 加湿
    GREE_MODE_FAN = 3,              // 送风
    GREE_MODE_HEAT = 4,             // 加热
}GREE_MODE;
typedef enum {
    GREE_FAN_SPEED_AUTO = 0,
    GREE_FAN_SPEED_1 = 1,
    GREE_FAN_SPEED_2 = 2,
    GREE_FAN_SPEED_3 = 3,
}GREE_FAN_SPEED;
typedef struct 
{
    union {
        uint32_t data; 
        struct {
            GREE_MODE  mode_flag:3; // 模式
            uint32_t  on_off:1;     // 开关
            uint32_t  fan_speed:2;  // 风速
            uint32_t  swing_flap:1; // 扫风
            uint32_t  sleep:1;      // 睡眠
            uint32_t  temprature:4; // 16°C = 0  30°C = 0111,   =26°C-16°C
            uint32_t  timer:8;      // 定时
            uint32_t  humidification:1; // 加湿
            uint32_t  lamplight:1;  // 灯光
            uint32_t  anion:1;  // 负离子
            uint32_t  save_electricity:1;   // 节电
            uint32_t  aeration:1;   // 换气
            uint32_t  fix_data:7;   // 固定值? 0001010b ?   40,56 ?
        }bits;
    }first; // 后面有 3 bit 固定数据和连接码
    union {
        uint32_t data; 
        struct {
            uint32_t  fan_up_down:1;    // 上下扫风
            uint32_t  reserver_1:3;     // 000b
            uint32_t  fan_left_right:1; // 左右扫风
            uint32_t  reserver_2:3;     // 000b
            uint32_t  display_temperature:2; // 温度显示
            uint32_t  reserver_3:16;
            uint32_t  energy_conservation:1;   // 节能
            uint32_t  reserver_4:1;            // 
            uint8_t   sum2  :4;  // checksum of the previous bytes (8-14)
        }bits;
    }second;
}nec_gree_data_def;

void nec_carrier(uint32_t bit, uint32_t time_us)
{
    if(bit) {
        GPIOC->BSRR = GPIO_PIN_14;
        hw_delay_us(time_us);
    }
    else {
        for(int i = 0; i < time_us/13; i++) { // 38 Khz, 
            if(i &1)  {
                GPIOC->BSRR = GPIO_PIN_14;
            }
            else {
                GPIOC->BRR = GPIO_PIN_14;
            }
            hw_delay_us(13);
        }
    }
}

void send_bit(uint32_t bit) 
{
    nec_carrier(0, 650);  // 687us
    nec_carrier(1, bit? 1620: 650); // 1619us
}

void nec_send(uint32_t data, uint32_t sec_data)
{
    nec_carrier(0, 8800);  // 9.021ms
    nec_carrier(1, 4500);  // 5.143ms
    for(int i = 0;i < 32; i++)
    {
        send_bit(data&1);  // low bit fist
        data >>= 1;
    }
    // 后面跟随三位 固定值
    send_bit(0);
    send_bit(1);
    send_bit(0);
    // 连接码
    nec_carrier(0, 560);
    nec_carrier(1, 20000);

    for(int i = 0; i < 32; i++)
    {
        send_bit(sec_data&1);
        sec_data >>= 1;
    }
    nec_carrier(0, 560);
}

关机代码(可以自己写优雅一点,方便阅读):

uint8_t tx_data[4] = {0x79, 0x0a, 00, 0x50};   // 开机,只需这个好像就可以。
uint8_t tx_data_2[4] = {0x0, 0x00, 0x00, 0xD0};
nec_gree_data_def gree_data;
gree_data.first.data = *(uint32_t*)tx_data;
gree_data.second.data = *(uint32_t*)tx_data_2;
nec_send(gree_data.first.data, gree_data.second.data);
// 后面这个不知道有啥用
uint8_t tx_data3[4] = {0x79, 0x0a, 00, 0x70};   // 开机
uint8_t tx_data4[4] = {0x0, 0x00, 0x30, 0x00};
nec_gree_data_def gree_data2;
gree_data2.first.data = *(uint32_t*)tx_data3;
gree_data2.second.data = *(uint32_t*)tx_data4;
nec_send(gree_data2.first.data, gree_data2.second.data);

开机代码(可以自己写优雅一点):

uint8_t data[4] = {0x71, 0x0a, 00, 0x50}; // 关机
uint8_t data_2[4] = {0x0, 0x00, 0x00, 0x50};
nec_gree_data_def gree_data3;
gree_data3.first.data = *(uint32_t*)data;
gree_data3.second.data = *(uint32_t*)data_2;
nec_send(*(uint32_t*)data, *(uint32_t*)data_2);
hw_delay_us(40000);
uint8_t data3[4] = {0x71, 0x0a, 00, 0x70}; // 关机
uint8_t data4[4] = {0x0, 0x00, 0x30, 0x80};
nec_send(*(uint32_t*)data3, *(uint32_t*)data4);

网上找的校验码,发现和我手上的遥控器不一致,仅供参考:

uint8_t sum2 = ((uint8_t)gree_data.first.bits.mode_flag - 1) + gree_data.first.bits.temprature + 5 +
        gree_data.second.bits.fan_left_right + gree_data.first.bits.aeration + 
        gree_data.second.bits.energy_conservation - gree_data.first.bits.on_off;

上面的数据解析可能有所不同,但大体不差,大家可以根据自己的情况修改。

由于校验值没法得出,因此算是一个小遗憾,不过能控制开关机就不错了。后面有时间可以参考:

https://github.com/crankyoldgit/IRremoteESP8266

另外为简单起见,没有使用 PWM,后续可以优化一下,同时没有学习功能,如果有的话,就可以轻松自动学习了,不用额外的逻辑分析仪了。

声明:本内容为作者独立观点,不代表电子星球立场。未经允许不得转载。授权事宜与稿件投诉,请联系:editor@netbroad.com
觉得内容不错的朋友,别忘了一键三连哦!
赞 1
收藏 2
关注 165
成为作者 赚取收益
全部留言
0/200
成为第一个和作者交流的人吧