#include <rtthread.h>
#include <rtdevice.h>
#include "driver/gpio.h"
#include "driver/rmt_tx.h"
/* 根据原理图修正的引脚定义 */
#define UART_TX_PIN 16
#define UART_RX_PIN 17
#define KEY1_PIN 0 // 注意GPIO0的特殊性
#define KEY2_PIN 1
#define IR_TX_GPIO_NUM 18 // 红外发射控制
#define LED_PIN 3 // 状态指示灯
/* 格力空调协议参数 */
#define GREE_ADDRESS 0x00FF
#define GREE_POWER_ON 0x12
#define GREE_POWER_OFF 0x1A
#define GREE_TEMP_UP 0x45
#define GREE_TEMP_DOWN 0x46
#define GREE_REPEAT_COUNT 3
/* 精确时序参数(单位:μs) */
#define GREE_HEADER_MARK 9000
#define GREE_HEADER_SPACE 4500
#define GREE_BIT_MARK 560
#define GREE_ONE_SPACE 1680
#define GREE_ZERO_SPACE 560
#define GREE_REPEAT_SPACE 2250
#define GREE_END_SPACE 20000
/* 空调控制参数 */
static rt_bool_t ac_power = RT_FALSE;
static rt_int32_t temperature = 25;
static rt_tick_t key1_press_time = 0;
static const rt_tick_t long_press_threshold = 1000;
static const rt_int32_t MIN_TEMP = 16;
static const rt_int32_t MAX_TEMP = 30;
/* RMT红外控制 */
static rmt_channel_handle_t ir_tx_chan = NULL;
static rmt_encoder_handle_t ir_encoder = NULL;
static rmt_transmit_config_t ir_tx_config = { .loop_count = 0 };
/* 协议帧结构 */
typedef struct {
uint16_t address;
uint8_t command;
uint8_t command_inverse;
uint8_t checksum;
} gree_ir_frame_t;
/* 按键状态枚举 - 全局定义解决重复声明问题 */
typedef enum {
KEY_IDLE,
KEY_PRESSED,
KEY_LONG_PRESS
} key_state_t;
/* 函数声明 */
void create_ir_encoder(void);
uint8_t calculate_gree_checksum(uint16_t address, uint8_t command);
void send_ir_command(uint8_t command);
rt_err_t init_ir_tx(void);
void init_keys(void);
void key_scan_thread_entry(void *parameter);
void hardware_init(void);
void test_ir_circuit(void);
/* 温度限制函数替代RT_CLAMP */
static rt_int32_t clamp_temperature(rt_int32_t temp, rt_int32_t min, rt_int32_t max) {
return (temp < min) ? min : ((temp > max) ? max : temp);
}
/*----------------------------------------------------------
* 红外编码器创建
*---------------------------------------------------------*/
void create_ir_encoder(void) {
rmt_bytes_encoder_config_t config = {
.bit0 = {
.level0 = 1,
.duration0 = GREE_BIT_MARK,
.level1 = 0,
.duration1 = GREE_ZERO_SPACE
},
.bit1 = {
.level0 = 1,
.duration0 = GREE_BIT_MARK,
.level1 = 0,
.duration1 = GREE_ONE_SPACE
},
.flags.msb_first = 1
};
if(rmt_new_bytes_encoder(&config, &ir_encoder) != ESP_OK) {
rt_kprintf("[错误] 编码器创建失败!\n");
return;
}
rt_kprintf("[IR] NEC编码器创建成功 (载波:38kHz, 占空比:35%%)\n");
}
/*----------------------------------------------------------
* 计算校验和
*---------------------------------------------------------*/
uint8_t calculate_gree_checksum(uint16_t address, uint8_t command) {
uint16_t data = (address << 8) | command;
return (data & 0xFF) + ((data >> 8) & 0xFF);
}
/*----------------------------------------------------------
* 发送红外命令(优化版本)
*---------------------------------------------------------*/
void send_ir_command(uint8_t command) {
gree_ir_frame_t frame = {
.address = GREE_ADDRESS,
.command = command,
.command_inverse = ~command,
.checksum = calculate_gree_checksum(GREE_ADDRESS, command)
};
const char* cmd_desc = command == GREE_POWER_ON ? "开机" :
command == GREE_POWER_OFF ? "关机" :
command == GREE_TEMP_UP ? "升温" : "降温";
rt_kprintf("[IR] 发送%s命令 (0x%02X), 校验和:0x%02X\n", cmd_desc, command, frame.checksum);
// 构造完整数据帧 (24位: 地址16位 + 命令8位)
uint32_t data = (frame.address << 8) | frame.command;
// 发送重复帧
for (int repeat = 0; repeat < GREE_REPEAT_COUNT; repeat++) {
// 发送起始码
rmt_symbol_word_t start = {
.level0 = 1,
.duration0 = GREE_HEADER_MARK,
.level1 = 0,
.duration1 = GREE_HEADER_SPACE
};
rmt_transmit(ir_tx_chan, ir_encoder, &start, sizeof(start), &ir_tx_config);
rmt_tx_wait_all_done(ir_tx_chan, RT_WAITING_FOREVER);
// 发送数据位 (MSB first)
for(int i = 15; i >= 0; i--) { // 16位地址
rmt_symbol_word_t bit = {
.level0 = 1,
.duration0 = GREE_BIT_MARK,
.level1 = 0,
.duration1 = (data & (1 << i)) ? GREE_ONE_SPACE : GREE_ZERO_SPACE
};
rmt_transmit(ir_tx_chan, ir_encoder, &bit, sizeof(bit), &ir_tx_config);
rmt_tx_wait_all_done(ir_tx_chan, RT_WAITING_FOREVER);
}
for(int i = 7; i >= 0; i--) { // 8位命令
rmt_symbol_word_t bit = {
.level0 = 1,
.duration0 = GREE_BIT_MARK,
.level1 = 0,
.duration1 = (frame.command & (1 << i)) ? GREE_ONE_SPACE : GREE_ZERO_SPACE
};
rmt_transmit(ir_tx_chan, ir_encoder, &bit, sizeof(bit), &ir_tx_config);
rmt_tx_wait_all_done(ir_tx_chan, RT_WAITING_FOREVER);
}
// 发送结束码
rmt_symbol_word_t end = {
.level0 = 1,
.duration0 = GREE_BIT_MARK,
.level1 = 0,
.duration1 = GREE_END_SPACE
};
rmt_transmit(ir_tx_chan, ir_encoder, &end, sizeof(end), &ir_tx_config);
rmt_tx_wait_all_done(ir_tx_chan, RT_WAITING_FOREVER);
rt_kprintf("[成功] 命令发送完成 (重复#%d)\n", repeat + 1);
rt_thread_mdelay(40); // 重复帧间隔
}
}
/*----------------------------------------------------------
* 空调控制函数
*---------------------------------------------------------*/
void control_ac_power(rt_bool_t power) {
rt_kprintf("[控制] 请求%s空调\n", power ? "开启" : "关闭");
if (power != ac_power) {
send_ir_command(power ? GREE_POWER_ON : GREE_POWER_OFF);
rt_thread_mdelay(800);
ac_power = power;
rt_kprintf("[状态] 空调已%s\n", power ? "开启" : "关闭");
} else {
rt_kprintf("[提示] 空调已是%s状态\n", power ? "开启" : "关闭");
}
}
void control_ac_temperature(rt_int32_t new_temp) {
if (!ac_power) {
rt_kprintf("[警告] 请先开启空调!\n");
return;
}
new_temp = clamp_temperature(new_temp, MIN_TEMP, MAX_TEMP);
if (new_temp != temperature) {
rt_kprintf("[动作] 温度调整: %d℃ → %d℃\n", temperature, new_temp);
uint8_t cmd = new_temp > temperature ? GREE_TEMP_UP : GREE_TEMP_DOWN;
send_ir_command(cmd);
temperature = new_temp;
rt_kprintf("[状态] 当前温度: %d℃\n", temperature);
} else {
rt_kprintf("[提示] 温度无需调整\n");
}
}
/*----------------------------------------------------------
* 红外初始化(适配原理图电路)
*---------------------------------------------------------*/
rt_err_t init_ir_tx(void) {
// 配置GPIO18为输出模式(根据原理图,高电平导通三极管)
rt_pin_mode(IR_TX_GPIO_NUM, PIN_MODE_OUTPUT);
rt_pin_write(IR_TX_GPIO_NUM, 0);
rmt_tx_channel_config_t tx_config = {
.gpio_num = IR_TX_GPIO_NUM,
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 1000000, // 1MHz, 1μs分辨率
.mem_block_symbols = 64,
.trans_queue_depth = 4,
.flags.invert_out = false, // 正常输出,不反转
};
if(rmt_new_tx_channel(&tx_config, &ir_tx_chan) != ESP_OK) {
return RT_ERROR;
}
// 配置38kHz载波
rmt_carrier_config_t carrier = {
.duty_cycle = 0.33,
.frequency_hz = 38000,
};
if(rmt_apply_carrier(ir_tx_chan, &carrier) != ESP_OK) {
return RT_ERROR;
}
if(rmt_enable(ir_tx_chan) != ESP_OK) {
return RT_ERROR;
}
create_ir_encoder();
return RT_EOK;
}
/*----------------------------------------------------------
* 按键初始化(适配原理图的上拉+电容消抖电路)
*---------------------------------------------------------*/
void init_keys(void) {
// 根据原理图,按键已外部上拉,配置为输入模式(无上拉)
rt_pin_mode(KEY1_PIN, PIN_MODE_INPUT);
rt_pin_mode(KEY2_PIN, PIN_MODE_INPUT);
// 特别处理GPIO0(BOOT引脚)
#ifdef SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP
gpio_deep_sleep_wakeup_enable(KEY1_PIN, GPIO_INTR_LOW_LEVEL);
#endif
rt_kprintf("[状态] 按键GPIO配置完成 (KEY1:GPIO%d, KEY2:GPIO%d)\n", KEY1_PIN, KEY2_PIN);
// 创建按键扫描线程(提高优先级)
rt_thread_t thread = rt_thread_create(
"key_scan",
key_scan_thread_entry,
RT_NULL,
2048, // 增大堆栈
20, // 提高优先级
10
);
if (thread) {
rt_thread_startup(thread);
rt_kprintf("[状态] 按键扫描线程启动\n");
} else {
rt_kprintf("[错误] 按键线程创建失败!\n");
}
}
/*----------------------------------------------------------
* 优化的按键扫描线程(状态机+硬件消抖)
*---------------------------------------------------------*/
void key_scan_thread_entry(void *parameter) {
key_state_t key1_state = KEY_IDLE;
key_state_t key2_state = KEY_IDLE;
rt_tick_t key1_tick = 0, key2_tick = 0;
rt_kprintf("[按键] 扫描线程启动\n");
while (1) {
// 读取按键状态(低电平表示按下)
int key1_val = rt_pin_read(KEY1_PIN);
int key2_val = rt_pin_read(KEY2_PIN);
/* KEY1处理 - 支持短按/长按 */
switch(key1_state) {
case KEY_IDLE:
if(key1_val == 0) { // 按下
key1_state = KEY_PRESSED;
key1_tick = rt_tick_get();
rt_kprintf("[按键] KEY1按下\n");
}
break;
case KEY_PRESSED:
if(key1_val == 1) { // 释放
rt_tick_t duration = rt_tick_get() - key1_tick;
if(duration > 50) { // 消抖
if(duration < long_press_threshold) {
rt_kprintf("[按键] KEY1短按\n");
if(ac_power) control_ac_temperature(temperature + 1);
}
}
key1_state = KEY_IDLE;
}
else if(rt_tick_get() - key1_tick >= long_press_threshold) {
rt_kprintf("[按键] KEY1长按\n");
control_ac_power(!ac_power);
key1_state = KEY_LONG_PRESS;
}
break;
case KEY_LONG_PRESS:
if(key1_val == 1) { // 释放
key1_state = KEY_IDLE;
}
break;
}
/* KEY2处理 - 仅短按 */
switch(key2_state) {
case KEY_IDLE:
if(key2_val == 0) { // 按下
key2_state = KEY_PRESSED;
key2_tick = rt_tick_get();
rt_kprintf("[按键] KEY2按下\n");
}
break;
case KEY_PRESSED:
if(key2_val == 1) { // 释放
rt_tick_t duration = rt_tick_get() - key2_tick;
if(duration > 50 && duration < long_press_threshold) {
rt_kprintf("[按键] KEY2短按\n");
if(ac_power) control_ac_temperature(temperature - 1);
}
key2_state = KEY_IDLE;
}
break;
}
// LED反馈按键状态
rt_pin_write(LED_PIN, (key1_val == 0 || key2_val == 0) ? 1 : 0);
rt_thread_mdelay(20); // 50Hz扫描频率
}
}
/*----------------------------------------------------------
* 硬件初始化(整合各模块初始化)
*---------------------------------------------------------*/
void hardware_init(void) {
// LED初始化
rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT);
rt_pin_write(LED_PIN, 0);
rt_kprintf("[硬件] LED就绪 (GPIO%d)\n", LED_PIN);
// 按键初始化
init_keys();
// 红外初始化
if (init_ir_tx() != RT_EOK) {
rt_kprintf("[错误] 红外初始化失败!\n");
while(1) {
rt_pin_write(LED_PIN, !rt_pin_read(LED_PIN));
rt_thread_mdelay(200);
}
}
rt_kprintf("[系统] 硬件初始化完成\n");
}
/*----------------------------------------------------------
* 主函数
*---------------------------------------------------------*/
int main(void) {
rt_kprintf("\n===== 格力空调控制器 v3.2 =====\n");
rt_kprintf("硬件: ESP32-C6\n");
rt_kprintf("红外: GPIO%d (38kHz)\n", IR_TX_GPIO_NUM);
rt_kprintf("按键: KEY1(GPIO%d), KEY2(GPIO%d)\n", KEY1_PIN, KEY2_PIN);
rt_kprintf("温度: %d℃ (16~30℃)\n", temperature);
rt_kprintf("编译: %s %s\n", __DATE__, __TIME__);
rt_kprintf("==============================\n");
hardware_init();
// 初始发送一次开机命令测试
send_ir_command(GREE_POWER_ON);
ac_power = RT_TRUE;
while (1) {
rt_thread_mdelay(1000);
}
return 0;
}修改以上代码,使其可以控制标准的格力空调 基于ESP32
最新发布