軟硬件開源項目-紅外遙控網(wǎng)關(guān):IRext 紅外碼庫+遠程實現(xiàn)紅外開關(guān)空調(diào)+紅外學習
【下載地址】:紅外遙控網(wǎng)關(guān)源碼
本倉庫提供了一個完整的紅外遙控網(wǎng)關(guān)源碼,旨在幫助開發(fā)者了解紅外控制和紅外學習的實現(xiàn)過程。項目包含了紅外控制和紅外學習的完整代碼。
項目地址:https://gitee.com/daadadada/infrared 小程序源碼地址:https://pan.baidu.com/s/17PZ9PxKzOpBtRuvA9ePOTw?pwd=4hgn
1.前言
當智能家居從概念走向現(xiàn)實,傳統(tǒng)紅外設(shè)備的“智能改造”卻成了多數(shù)家庭的痛點:不同品牌空調(diào)的紅外協(xié)議千差萬別,遙控器堆成小山仍難實現(xiàn)統(tǒng)一控制。盡管市面上有很多智能控制 APP,但是這需要設(shè)備端支持網(wǎng)絡,一些生產(chǎn)較早的空調(diào)等設(shè)備大多都不支持網(wǎng)絡這一功能。如果有一個類似遙控器的產(chǎn)品能遠程控制市面上大部分的空調(diào)品牌,并且支持紅外學習實現(xiàn)控制,那么問題就迎刃而解了。由于 WiFi 信號容易受影響并且做通信功耗相對較高,因此我使用以太網(wǎng)做通訊。之前做項目用過 WIZnet 的芯片實現(xiàn)以太網(wǎng)功能,近期他們出了個帶 MCU 的以太網(wǎng)芯片 W55MH32,找他們銷售申請了一套開發(fā)板。計劃是小程序做控制端,開發(fā)板做設(shè)備端,通過以太網(wǎng)進行通訊。紅外學習到的紅外碼在設(shè)備端使用 w25q64 本地存儲。
目前開發(fā)板用著可以,這款芯片集成了全硬件 TCP/IP 協(xié)議棧、MAC 及 PHY,開發(fā)板也已經(jīng)集成了網(wǎng)絡變壓器和網(wǎng)口。Flash 和 SRAM 也足夠大。一些不是很懂的網(wǎng)絡協(xié)議也可以去他們[官網(wǎng)例程]去了解,里面也有一些講如何配置 Keil 和燒錄,資料很充足。
目前的功能已經(jīng)實現(xiàn)了遠程控制空調(diào),但是紅外學習機制是通過外界發(fā)射紅外信號來觸發(fā),并在串口打印,紅外學習的功能并沒有完全實現(xiàn)。后續(xù)會通過小程序按鍵來觸發(fā)紅外學習,并且能下發(fā)命令實現(xiàn)控制。未被收錄的紅外碼通過 w25q64 和 FatFs 文件系統(tǒng),實現(xiàn)本地存儲!另外,后面也會自己畫原理圖和 pcb 板,將所有的硬件集成到板子上,也會把代碼、原理圖以及 pcb 都開源出來。
2.紅外
紅外遙控碼是一種基于 38000Hz 或者 56000Hz 載波頻率的控制編碼,接收方通過識別帶有或不帶有載波的時間間隔片進行編碼識別。
在控制層通常使用高低物理電平對的序列來表示一個獨立的控制信號,例如以下的 NEC 碼:
它使用 9000us 載波+4500us 無載波時間序列表示引導碼,并且使用 500us 載波+500us 無載波時間序列表示邏輯 0,使用 500us 載波+1500us 無載波時間序列表示邏輯 1,采用 2 字節(jié)地址碼和 2 字節(jié)命令碼構(gòu)成,全碼時間序列長度為 67.5ms。
IRext 紅外庫移植
IRext 是一個開源萬能紅外遙控碼庫、編解碼壓縮算法以及免費周邊服務。它向智能家居開發(fā)者提供:
- 支持 16 類 1000 多品牌,上萬個型號的家用電器遙控。
- 在線以及離線的萬能紅外碼庫,包括按照品牌分類的索引以及遙控編碼。
- 靈活的服務部署方式,利用開源的服務端以及控制臺代碼在容器環(huán)境內(nèi) 5 分鐘快速搭建自己的碼庫服務。
- 碼庫和解碼算法經(jīng)過了極限壓縮,能存儲和運行在配置低至 51 MCU 的嚴苛硬件環(huán)境中。
- 豐富的平臺適配支持和示例代碼。
- 詳盡的文檔資源,覆蓋萬能遙控開發(fā)的每一個環(huán)節(jié)。
- 開發(fā)者可使用碼庫擴展工具自行擴展碼庫,可基于開源代碼自由修改方案功能細節(jié)。
前往[IRext 官網(wǎng)]下載離線碼庫和 IRext 解碼庫,將 IRext 解碼庫放進單片機工程目錄里并修改頭文件目錄,讓其包含解碼庫中的頭文件。根據(jù) IRext 提供的品牌索引庫從離線碼庫下載所需要的品牌紅外碼庫??纱娣诺絾纹瑱C FLASH 或放到服務器上。IRext 解碼庫支持從文件系統(tǒng)解碼或從內(nèi)存解碼。
從文件系統(tǒng)解碼
ir_file_open(category, sub_category, "my_ir_code_file.bin");
ir_decode(key_code, user_data, ac_status, change_wind_dir);
ir_close();
加載到內(nèi)存解碼
ir_binary_open(category, sub_category, buffer, buffer_length);
ir_decode(key_code, user_data, &ac_status, change_wind_dir);
ir_close();
解碼之后在 decoded_data 中獲得可供輸出的 IR 時間序列,將此序列遞交給 IR 設(shè)備驅(qū)動即可實現(xiàn)紅外發(fā)送。不過,目前 IRext 庫只支持開關(guān),風速,溫度控制和掃風這些功能。
3.程序功能實現(xiàn)
小程序與設(shè)備通信功能
使用小程序可以做到遠程開啟空調(diào)而無需在設(shè)備旁邊,并且小程序無需下載安裝,通過微信等平臺就能直接訪問,因此我選擇微信小程序來作為控制端。如圖為小程序界面圖:
小程序與設(shè)備通信的方式為,小程序調(diào)用 OneNET 平臺提供的 HTTP 接口實現(xiàn)設(shè)備屬性下發(fā),云平臺通過 mqtt 協(xié)議將數(shù)據(jù)發(fā)送給設(shè)備。小程序與云平臺以及設(shè)備交互的數(shù)據(jù)格式如下,Command 用于下發(fā)控制或紅外學習命令。
其中 AC_Status 是一個結(jié)構(gòu)體,存儲空調(diào)的狀態(tài),具體內(nèi)容如下,其中的結(jié)構(gòu)體成員分別是空調(diào)的品牌、型號、電源、模式、溫度、風速和操作,其中操作是因為 IRext 庫需要在解碼的時候作為形參傳入,所以必須定義,這個操作對應著小程序端按下的按鍵,比如,開關(guān)機,升溫等。
在設(shè)備端我用到 OneNET 平臺的通信主題有$sys/{pid}/{device-name}/thing/property/set
這個用于訂閱,接收下發(fā)的控制命令;$sys/{pid}/{device-name}/thing/property/set_reply
用于應答下發(fā)的控制命令。一些其他的通信主題沒有用到,感興趣的小伙伴可以訪問OneNET mqtt 通信主題了解。
小程序端使用的是 OneNET 平臺提供的 http 接口,具體用到的接口為 https://iot-api.heclouds.com/thingmodel/set-device-property ,用于下發(fā)設(shè)備屬性設(shè)置命令到設(shè)備,設(shè)備會返回設(shè)置結(jié)果。其他的 http 接口可以訪問OneNET http 接口。
另外在發(fā)起 https 請求前,還需要在 Headers 中攜帶統(tǒng)一的安全鑒權(quán)信息 authorization 才能成功請求接口。具體方法需前往安全鑒權(quán)查看詳細內(nèi)容。
我們在 W55MH32 提供的開發(fā)套件下寫代碼,一些依賴的頭文件和 pack 包都在里面。
將開發(fā)套件下的 do_mqtt.c 和 do_mqtt.h 文件復制到項目里,并修改 mqtt_params 里的參數(shù)為自己要連接的 mqtt 地址等參數(shù)。
do_mqtt()函數(shù)是一個基于狀態(tài)機的 MQTT 客戶端處理函數(shù),負責管理 MQTT 連接、訂閱、?;詈拖⒔邮盏炔僮鳌.敽瘮?shù)第一次運行時,會來到 conn,開始和 MQTT 服務器建立連接,之后再根據(jù)狀態(tài)進行判斷狀態(tài),進入到 SUB 訂閱主題或者 KEEPALIVE ?;畹?。在狀態(tài)為 ERR 時,代表著 mqtt 連接之間出現(xiàn)了錯誤,在 ERR 狀態(tài)中,可以重新連接或者自定義一些處理。由于在 mqtt 的狀態(tài)機中我不需要發(fā)布消息,所以我把發(fā)布消息這一節(jié)點刪除了,代碼如下:
void do_mqtt(void)
{
uint8_t ret;
switch (run_status)
{
case CONN:
{
ret = MQTTConnect(&c, &data); /* 連接到MQTT服務器 */
printf("Connect to the MQTT server: %d.%d.%d.%d:%drn", mqtt_params.server_ip[0], mqtt_params.server_ip[1], mqtt_params.server_ip[2], mqtt_params.server_ip[3], mqtt_params.port);
printf("Connected:%srnrn", ret == SUCCESSS ? "success" : "failed");
if (ret != SUCCESSS)
{
run_status = ERR;
}
else
{
run_status = SUB;
}
break;
}
case SUB:
{
ret = MQTTSubscribe(&c, mqtt_params.subtopic, mqtt_params.subQoS, message_Arrived); /* 訂閱主題 */
printf("Subscribing to %srn", mqtt_params.subtopic);
printf("Subscribed:%srnrn", ret == SUCCESSS ? "success" : "failed");
if (ret != SUCCESSS)
{
run_status = ERR;
}
else
{
run_status = KEEPALIVE;
}
break;
}
case KEEPALIVE:
{
if (MQTTYield(&c, 30) != SUCCESSS) /* ?;?MQTT */
{
run_status = ERR;
}
delay_ms(100);
}
case RECV:
{
if (mqtt_recv_flag)
{
mqtt_recv_flag = 0;
json_decode(mqtt_recv_msg);
}
delay_ms(100);
break;
}
case ERR: /* 錯誤處理 */
printf("system ERROR!");
delay_ms(1000);
break;
default:
break;
}
}
messageArrived 函數(shù)是當我們訂閱的主題發(fā)來消息時的回調(diào)函數(shù),當有消息到達時,會觸發(fā)此函數(shù),并把消息內(nèi)容作為參數(shù)傳遞給此函數(shù)。這里會打印一些參數(shù),如主題、消息內(nèi)容、QOS 等級等。代碼如下:
void message_Arrived(MessageData *md)
{
char topicname[64] = {0};
char msg[512] = {0};
sprintf(topicname, "%.*s", (int)md- >topicName- >lenstring.len, md- >topicName- >lenstring.data);
sprintf(msg, "%.*s", (int)md- >message- >payloadlen, (char *)md- >message- >payload);
printf("recv data from %s", topicname);
if (strcmp(topicname, mqtt_params.subtopic) == 0)
{
mqtt_recv_flag = 1;
memset(mqtt_recv_msg, 0, sizeof(mqtt_recv_msg));
memcpy(mqtt_recv_msg, msg, strlen(msg));
}
}
json_decode 函數(shù)用于解析 JSON 數(shù)據(jù),這里可以根據(jù)我們在云平臺定義的數(shù)據(jù)來解析數(shù)據(jù)。解析完成后,會做一個回復,表示接收到數(shù)據(jù),做應答。代碼如下:
void json_decode(char *msg)
{
int ret;
char replymsg[128] = {0};
cJSON *jsondata = NULL;
cJSON *id = NULL;
cJSON *params = NULL;
cJSON *AC = NULL;
cJSON *data = NULL;
jsondata = cJSON_Parse(msg);
if (jsondata == NULL)
{
printf("json parse fail.rn");
return;
}
id = cJSON_GetObjectItem(jsondata, "id");
params = cJSON_GetObjectItem(jsondata, "params");
data = cJSON_GetObjectItem(params, "Command");
// 如果不是控制命令則返回
if (strcmp(data- >valuestring, "Control"))
{
return;
}
// 解析json并獲取空調(diào)狀態(tài)
AC = cJSON_GetObjectItem(params, "AC");
data = cJSON_GetObjectItem(AC, "ACBrand");
brand = (unsigned char)data- >valueint;
data = cJSON_GetObjectItem(AC, "ACType");
type = (unsigned char)data- >valueint;
data = cJSON_GetObjectItem(AC, "openFlag");
ac_status.ac_power = (t_ac_power)data- >valueint;
data = cJSON_GetObjectItem(AC, "modeGear");
ac_status.ac_mode = (t_ac_mode)data- >valueint;
data = cJSON_GetObjectItem(AC, "tempature");
ac_status.ac_temp = (t_ac_temperature)(data- >valueint - 16);
data = cJSON_GetObjectItem(AC, "fengsuGear");
ac_status.ac_wind_speed = (t_ac_wind_speed)data- >valueint;
data = cJSON_GetObjectItem(AC, "opertion");
operation = (unsigned char)data- >valueint;
// 回復
pubmessage.qos = QOS0;
sprintf(replymsg, "{"id":"%s","code":200,"msg":"success"}", id- >valuestring);
printf("reply:%srn", replymsg);
pubmessage.payload = replymsg;
pubmessage.payloadlen = strlen(replymsg);
ret = MQTTPublish(&c, mqtt_params.subtopic_reply, &pubmessage);
if (ret != SUCCESSS)
{
run_status = ERR;
}
else
{
printf("publish:%s,%srnrn", mqtt_params.subtopic_reply, (char *)pubmessage.payload);
}
cJSON_Delete(jsondata);
irSendFlag = 1;
}
紅外發(fā)射功能
由于紅外發(fā)射是一種基于 38000Hz 載波頻率的控制編碼,因此我們需要使用到 PWM 功能和 GPIO 口,創(chuàng)建 GPIOConfiguration 和 TIMConfiguration 函數(shù),分別初始化 GPIO 口和 PWM。GPIO 口,即 PWM 輸出口設(shè)為 PA3。定時器 2 采用的是 PCLK1 時鐘,頻率盡量最大,這樣輸出的紅外誤碼率低。我選擇頻率為 216MHz,因此不需要分頻。計數(shù)值設(shè)置為 5736,216MHz/5736≈38KHz。紅外發(fā)射的占空比一般設(shè)為 1/3,因此將占空比,即 TIM_Pulse 設(shè)為 1912。PWM 代碼如下:
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 5736 - 1; // 216Mhz/5736≈38Khz
TIM_TimeBaseStructure.TIM_Prescaler = 1 - 1; // 216Mhz/3=216Mhz
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 配置PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 1912; // 占空比計算
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC4Init(TIM2, &TIM_OCInitStructure);
TIM_Cmd(TIM2, ENABLE);
TIM_SetCompare4(TIM2, 0);
在 IRControl.c 文件中創(chuàng)建 getIrCode 函數(shù),通過 mqtt 解析的 json 數(shù)據(jù),即可知道要控制哪個品牌以及類型的空調(diào),目前僅支持三個品牌。接著調(diào)用 IRext 提供的 API 解碼,并將解碼后的數(shù)據(jù)存放到先前定義的 user_data 變量里,代碼如下:
unsigned char *number_point = NULL;
unsigned short *length_point = NULL;
switch (brand)
{
case 0:
{
number_point = Gree[type];
length_point = Gree_Length;
break;
}
case 1:
{
number_point = Midea[type];
length_point = Midea_Length;
break;
}
case 2:
{
number_point = Haier[type];
length_point = Haier_Length;
break;
}
default:
break;
}
unsigned char ret = ir_binary_open(1, 1, number_point, length_point[type]);
length = ir_decode(operation_list[operation], user_data, &ac_status, 1);
if (length == 0)
printf("Decode error");
ir_close();
在 IRControl.c 中創(chuàng)建 runIr 函數(shù),用于驅(qū)動紅外發(fā)射模塊,根據(jù)解碼后的時間序列,驅(qū)動 PWM 輸出占空比為 33%或 0%的 PWM 波即可控制空調(diào),代碼如下。在最后輸出一個 40ms 的占空比為 0%的無載波時間片,來終止紅外。
for (unsigned short i = 0; i < length; i++)
{
if (!(i % 2))
TIM_SetCompare4(TIM2, 1912);
else
TIM_SetCompare4(TIM2, 0);
delay_us(user_data[i]);
}
TIM_SetCompare4(TIM2, 0);
delay_us(40000);
最后創(chuàng)建 ir_control 函數(shù)并調(diào)用 getIrCode 和 runIr 函數(shù)即可。
紅外學習功能
在初始化階段會配置 GPIO 口中斷用于監(jiān)聽紅外信號,定時器中斷用于計時。由于定時器的重裝值為 16 位,最大只能到 65535us,而紅外信號會有重復碼,很容易就超過了定時器的最大值,因此需要使用定時器溢出中斷來轉(zhuǎn)換成 32 位計數(shù)值。在每次觸發(fā) TIM4 更新中斷時,就將溢出計數(shù)加 1,在需要獲取時間時就可以將溢出計數(shù)左移 16 位并與上當前定時器 的計數(shù)值就是 32 位計數(shù)值。例如:
return (timer_overflow_count << 16) | TIM_GetCounter(TIM4);
紅外接收有 2 個狀態(tài)分別是 IR_IDLE(空閑)、IR_RECEIVING(接收完成)。當紅外信號到來時,會進入到 EXTI0_IRQHandler 中斷處理函數(shù)中,并將空閑狀態(tài)改變?yōu)榻邮諣顟B(tài),之后開始接收數(shù)據(jù),完整代碼如下:
void EXTI0_IRQHandler(void)
{
uint32_t current_time = 0;
uint32_t time_interval = 0;
// 檢查是否是該EXTI線產(chǎn)生的中斷
if (EXTI_GetITStatus(IR_EXTI_LINE) != RESET)
{
// 獲取當前時間
current_time = Get_32bit_Timer_Value();
// 計算時間間隔
time_interval = current_time - ir_last_time;
// 如果是第一個數(shù)據(jù)點或者是接收狀態(tài),則記錄時間
if (ir_state == IR_IDLE)
{
// 第一個邊沿,開始接收數(shù)據(jù)
ir_state = IR_RECEIVING;
ir_data_count = 0;
ir_data_ready = 0;
}
// 存儲時間數(shù)據(jù)(如果緩沖區(qū)未滿)
if (ir_data_count < IR_DATA_BUFFER_SIZE)
{
ir_time_data[ir_data_count] = time_interval;
ir_data_count++;
}
// 更新時間
ir_last_time = current_time;
// 清除中斷標志位
EXTI_ClearITPendingBit(IR_EXTI_LINE);
}
}
我們還需要一個判斷接收完畢的函數(shù),函數(shù)內(nèi)會聲明兩個全局靜態(tài)變量,分別是用于記錄上次進入函數(shù)的時間 last_activity_time 和上次記錄的紅外數(shù)據(jù)計數(shù)值 previous_count。首先,我們需要判斷當前紅外狀態(tài)是否是接收狀態(tài),其次,我們需要判斷紅外數(shù)據(jù)計數(shù)值不為 0。當都滿足時,開始計算時間間隔,如果當前的紅外數(shù)據(jù)計數(shù)值和上次的不一樣就重置 last_activity_time 并更新 previous_count,返回 0,否則判斷當時間間隔超過 10ms 時就認為紅外接收完成并返回 1,并重置一些參數(shù)。完整代碼如下:
uint8_t IR_Is_Ready(void)
{
// 如果正在接收數(shù)據(jù)且有數(shù)據(jù)
if (ir_state == IR_RECEIVING && ir_data_count > 0)
{
// 使用全局靜態(tài)變量來跟蹤超時
static uint32_t last_activity_time = 0;
static uint16_t previous_count = 0;
uint32_t current_time = Get_32bit_Timer_Value();
// 如果數(shù)據(jù)計數(shù)發(fā)生變化,說明有新的數(shù)據(jù)到來
if (previous_count != ir_data_count)
{
previous_count = ir_data_count;
last_activity_time = current_time;
}
else
{
// 數(shù)據(jù)計數(shù)沒有變化,檢查是否超時
uint32_t time_since_last_activity = current_time - last_activity_time;
// 如果超過10ms沒有新數(shù)據(jù),認為接收完成
if (time_since_last_activity > 10000) // 10ms = 10000us
{
// 設(shè)置數(shù)據(jù)就緒標志
ir_data_ready = 1;
ir_state = IR_IDLE;
// 重置跟蹤變量
previous_count = 0;
return 1;
}
}
return 0;
}
return 0;
}
在主循環(huán)里輪詢 IR_Is_Ready,如果返回為 1,則說明數(shù)據(jù)就緒,開始紅外學習,調(diào)用函數(shù) ir_learn,ir_learn 函數(shù)用于重復獲取紅外信號,用于后續(xù)濾波。在函數(shù)內(nèi)也會計時,當超過 30s 后,認為紅外學習失敗,若在 30s 內(nèi)完成 3 次紅外接收,則認為紅外學習成功,并開始均值濾波。均值濾波即將數(shù)組相同下標的值相加并除以數(shù)組的個數(shù)。濾波后通過串口打印紅外時間序列。完整代碼如下:
/**
* @brief 對二維數(shù)組進行均值濾波處理
* @param dataCount: 每個一維數(shù)組中有效數(shù)據(jù)的個數(shù)
*/
void IR_Mean_Filter(uint8_t dataCount)
{
uint16_t i;
uint32_t sum;
uint16_t mean_value;
// 對每個數(shù)據(jù)位置進行均值濾波
for (i = 0; i < dataCount && i < IR_DATA_BUFFER_SIZE; i++)
{
sum = 0;
// 累加所有數(shù)組在同一位置的值
for (unsigned char j = 0; j < 3; j++)
{
sum += receive_data[j][i];
}
// 計算均值
mean_value = (uint16_t)(sum / 3);
// 存儲到濾波后的數(shù)據(jù)數(shù)組中
filtering_data[i] = mean_value;
}
}
void ir_learn()
{
// 計數(shù)
unsigned char count = 0;
unsigned char successFlag = 0;
unsigned int dataCount = 0;
IR_Get_Time_Data(receive_data[count], IR_Get_Data_Count());
// 獲取紅外數(shù)據(jù)數(shù)量
dataCount = IR_Get_Data_Count();
IR_Clear_Data();
count++;
// 啟動30s計時
RTC_30Sec_Start();
while (!RTC_30Sec_IsTimeout())
{
if (IR_Is_Ready())
{
IR_Get_Time_Data(receive_data[count], IR_Get_Data_Count());
IR_Clear_Data();
count++;
if (count == 3)
{
successFlag = 1;
break;
}
}
}
RTC_30Sec_Stop();
if (!successFlag)
{
// 超時,停止紅外學習,并返回
printf("timeout return rn");
return;
}
// 均值濾波
IR_Mean_Filter(dataCount);
printf("IR learn success rn");
printf("length:%d,紅外時間序列:rn", dataCount);
for (unsigned int i = 1; i < dataCount; i++)
{
printf("%drn", filtering_data[i]);
}
printf("rn");
}
主函數(shù)代碼
主函數(shù)完整代碼如下:
#include "bsp_rcc.h"
#include "bsp_tim.h"
#include "bsp_uart.h"
#include "delay.h"
#include "do_mqtt.h"
#include "socket.h"
#include "stdlib.h"
#include "wiz_interface.h"
#include "wizchip_conf.h"
#include < stdio.h >
#include < stdlib.h >
#include < string.h >
#include "IR_Control.h"
#include "IR_Receive.h"
#include "RTC.h"
#include "ir_ac_control.h"
#define SOCKET_ID 0
#define ETHERNET_BUF_MAX_SIZE (1024 * 2)
/* 網(wǎng)絡配置信息 */
wiz_NetInfo default_net_info = {
.mac = {0x00, 0x08, 0xdc, 0x12, 0x22, 0x05},
.ip = {192, 168, 2, 40},
.gw = {192, 168, 2, 1},
.sn = {255, 255, 255, 0},
.dns = {8, 8, 8, 8},
.dhcp = NETINFO_DHCP};
uint8_t ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {0};
static uint8_t mqtt_send_ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {0};
static uint8_t mqtt_recv_ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {0};
// 第三方庫IRext定義的結(jié)構(gòu)體,用于存儲空調(diào)狀態(tài)
t_remote_ac_status ac_status;
// 品牌
unsigned char brand = 0;
// 型號
unsigned char type = 0;
// 操作類型
unsigned char operation = 0;
int main(void)
{
// 時鐘初始化,使能外部高速晶振時鐘,主頻倍頻至為216MHz,PCLK1、PCLK2都設(shè)為216MHz
rcc_clk_config();
delay_init();
console_usart_init(115200);
tim3_init();
printf("Infrared Remote Control Gatewayrn");
wiz_toe_init();
wiz_phy_link_check();
network_init(ethernet_buf, &default_net_info);
// 紅外初始化
ir_Config();
IR_Receive_Init();
// rtc 時鐘初始化
RTC_Init();
mqtt_init(SOCKET_ID, mqtt_send_ethernet_buf, mqtt_recv_ethernet_buf);
while (1)
{
do_mqtt();
// 檢查是否收到紅外發(fā)送命令
if (IR_is_send())
{
printf("start IR controlrn");
// 紅外控制
ir_Control(brand, type, operation, &ac_status);
Reset_IR_Send();
// 清除紅外接收到的數(shù)據(jù),避免觸發(fā)紅外學習
IR_Clear_Data();
}
// 檢查是否收到紅外學習命令
if (IR_is_learn())
{
printf("start IR learnrn");
ir_Learn(getFileName());
Reset_IR_learn();
}
}
}
4.功能驗證
經(jīng)過驗證,目前的功能能夠穩(wěn)定運行。
5.結(jié)語
基于 W55MH32Q-EVB 構(gòu)建的紅外遙控?關(guān),以其獨特的技術(shù)優(yōu)勢為智能家居領(lǐng)域的紅外設(shè)備控制提供了全新解決?案。W55MH32Q 芯?的?性能內(nèi)核與全硬件?絡協(xié)議棧,確保了數(shù)據(jù)處理與通信的?效穩(wěn)定,讓微信?程序與?關(guān)的交互響應迅速。
IRext 紅外庫的靈活移植,突破了品牌壁壘,實現(xiàn)了多品牌空調(diào)的統(tǒng)?控制;JSON 數(shù)據(jù)解析則讓指令傳遞更精準?效。從硬件設(shè)計到軟件實現(xiàn),整個系統(tǒng)?縫銜接,既解決了傳統(tǒng)遙控器混亂的痛點,?降低了智能家居改造的門檻。
未來,隨著功能的進?步拓展,該?關(guān)有望兼容更多類型的紅外設(shè)備,為?戶帶來更便捷、智能的?活體驗,也為紅外設(shè)備的智能化升級提供了可借鑒的技術(shù)范式。
感謝?家的耐?閱讀,想了解紅外學習、irext 紅外庫,F(xiàn)atFs 文件系統(tǒng)的請關(guān)注我。后續(xù)會更新文章。另外,如果你在閱讀過程中有任何疑問,歡迎隨時通過私信留?。我會盡快回復消息。
審核編輯 黃宇
-
單片機
+關(guān)注
關(guān)注
6071文章
45256瀏覽量
660069 -
網(wǎng)關(guān)
+關(guān)注
關(guān)注
9文章
6167瀏覽量
54773 -
智能家居
+關(guān)注
關(guān)注
1938文章
9907瀏覽量
192945 -
紅外控制
+關(guān)注
關(guān)注
0文章
26瀏覽量
11822
發(fā)布評論請先 登錄
智能紅外遙控開關(guān)控制器
云智能紅外轉(zhuǎn)WIFI網(wǎng)關(guān)
紅外學習與接收
基于紅外學習的離線語音控制低成本方案
基于紅外遙控的智能語音芯片在空調(diào)中的應用
紅外遙控的相關(guān)資料下載
用ESP8266實現(xiàn)的紅外學習遙控器介紹
基于NiosⅡ的紅外學習型遙控器設(shè)計
基于51系列單片機的紅外遙控設(shè)計

ESP8266紅外學習遙控器

智能學習型紅外空調(diào)遙控器的設(shè)計與實現(xiàn)

評論