※ 2019.8 後補「積木式免焊接 記憶型空調紅外線轉譯器」一文,可供快速製作成品。
紅外線訊號轉譯程式
程式製作流程概要:
1. 錄製米家APP內小米萬能遙控器內建的任一個NEC編碼遙控器 0~9 數字鍵的紅外線編碼
2. 錄製自家冷氣空調的 10組常用信號,對應上 1 的 0~9 數字鍵,10組的話日常使用應足夠
3. 程式基本流程為
A. 讀取紅外線接收器的編碼信號
B. 比對符合電視遙控器數字鍵(上 1 )中的那個數字
C. 發射與數字對應的自家空調紅外線錄製信號(上 2)
D. 過程中 LED 、蜂鳴器會有閃光及聲音作提示
錄製米家APP內小米萬用遙控器的 0~9 數字鍵的紅外線編碼
請先將前篇 04中修改好的 IRrecvDumpV2 程式編譯上傳至 Arduino 開發板內,並將實體的小米萬能遙控器對準電路板上的紅外線接收器,準備接收記錄信號。
PS: 購買的實體小米萬能遙控器請先確認已完成綁定米家APP ( 以下使用小米萬能遙控器一代 )
打開手機中米家APP內的萬能遙控器圖示,選擇 添加遙控器 -> 點選機頂盒 -> 點選衛星/機頂盒 -> 點選 創維 Skyworth (隨機挑選的NEC編碼遙控器,配合下方紅外線轉譯程式使用NEC 編碼判別,若要用別的遙控編碼協定請再自行修改程式碼)
接著請依螢幕中的按鍵測試,均選"有響應"以完成建立步驟 -> 點選 數字 -> 出現數字鍵盤
請依序按下數字鍵的 0~9 ,並複製紀錄下各數字的紅外線編碼,如數字 0,對應的NEC編碼如下 0x80BFE11E (0x 表示16進位)
創維機頂盒 遙控器數字 0~9 的NEC編碼紀錄如下:
0 0x80BFE11E
1 0x80BF49B6
2 0x80BFC936
3 0x80BF33CC
4 0x80BF718E
5 0x80BFF10E
6 0x80BF13EC
7 0x80BF51AE
8 0x80BFD12E
9 0x80BF23DC
錄製自家冷氣空調常用設定的紅外線編碼
請先構思自家空調常用的設定有那些,將之編號為 0~9 (沒那麼多組也沒關係,日後可再添加修改),舉例如下:
0 關機 (不用考慮其它參數設定)
1 保留
2 保留
3 保留
4 保留
5 快速冷房:開機 , 冷氣 , 25度 , 風速4級(最大) , 風向自動
6 一般使用:開機 , 冷氣 , 26度 , 風速自動, 風向自動
7 一般使用:開機 , 冷氣 , 27度 , 風速自動, 風向自動
8 一般使用:開機 , 冷氣 , 28度 , 風速自動, 風向自動
9 搭配風扇:開機 , 冷氣 , 29度 , 風速自動, 風向自動
請按出構思的空調設定,並同上方式複製紀錄下紅外線編碼參數,如 關機,對應的編碼資料如下
錄製好所有的編碼後,可能發現每個設定編碼其下方藍色參數部份可能會不盡相同,可以直接套用任何一組使用,應該都還在取樣誤差範圍內,或可以取出現最多次的數值作為較佳參數使用。
下方的 byte rawData 陣列,在其上方一行請手動為其加上設定的註解以方便日後判讀了解( // 後為註解符號,不會被編譯),並為每個 rawData 陣列加上數字編號以供程式識別 (紅色部份),全部取樣整理如下:
#define RAW_HDR_MARK 3350
#define RAW_HDR_SPACE 1800
#define RAW_BIT_MARK 400 // 450 -> 17 times. 400 -> 66 times. 350 -> 55 times.
#define RAW_ONE_SPACE 1350 // 1350 -> 11 times. 1300 -> 11 times. 500 -> 65 times.
#define RAW_ZERO_SPACE 500 // 500 -> 65 times. 450 -> 40 times. 400 -> 2 times.
#define RAW_RPT_MARK 400 //Repeat Gap
#define RAW_RPT_SPACE 16950
int rawData_Bits = 144 ;
//0 關機 (不用考慮其它參數設定)
byte rawData_0[18] = { 0xC4, 0xD3, 0x64, 0x80, 0x0, 0x0, 0x18, 0xB0, 0x6C, 0x2, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8 };
//5 快速冷房:開機 , 冷氣 , 25度 , 風速4級(最大) , 風向自動
byte rawData_5[18] = { 0xC4, 0xD3, 0x64, 0x80, 0x0, 0x4, 0x18, 0x90, 0x6C, 0x22, 0x46, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4C };
//6 一般使用:開機 , 冷氣 , 26度 , 風速自動, 風向自動
byte rawData_6[18] = { 0xC4, 0xD3, 0x64, 0x80, 0x0, 0x4, 0x18, 0x50, 0x6C, 0x2, 0x46, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF4 };
//7 一般使用:開機 , 冷氣 , 27度 , 風速自動, 風向自動
byte rawData_7[18] = { 0xC4, 0xD3, 0x64, 0x80, 0x0, 0x4, 0x18, 0xD0, 0x6C, 0x2, 0x46, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC };
//8 一般使用:開機 , 冷氣 , 28度 , 風速自動, 風向自動
byte rawData_8[18] = { 0xC4, 0xD3, 0x64, 0x80, 0x0, 0x4, 0x18, 0x30, 0x6C, 0x2, 0x46, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8C };
//9 搭配風扇:開機 , 冷氣 , 29度 , 風速自動, 風向自動
byte rawData_9[18] = { 0xC4, 0xD3, 0x64, 0x80, 0x0, 0x4, 0x18, 0xB0, 0x6C, 0x2, 0x46, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4C };
完成紅外線轉譯程式
請注意執行本程式前
1. 需將前篇03、04中修改的 IRremoteInt.h 檔案內的 RAWBUF、_GAP 二個參數改回預設值,以維持程式穩定:
#define RAWBUF 101
#define _GAP 5000
2. 再來貼上對應的創維遙控器編碼
N00~N09必須有值,沒有的部份請補 0,若使用相同的遙控器則不用修改
#define N00 0x80BFE11E
#define N01 0x80BF49B6
#define N02 0x80BFC936
#define N03 0x80BF33CC
#define N04 0x80BF718E
#define N05 0x80BFF10E
#define N06 0x80BF13EC
#define N07 0x80BF51AE
#define N08 0x80BFD12E
#define N09 0x80BF23DC
3. 貼上自家的冷氣空調錄製編碼
A. 從第35行 #define RAW_HDR_MARK 行數開始對應貼上
B. rawData_0[XX] ~ rawData_9[XX] 必須有值,沒有的部份請改成
byte rawData_X[1] = { 0 }; //X為對應的數字, 可參考 48 ~ 58 行中的語法
其它就不用修改了,測試正常後可再依自己的需求去修改程式內容,上面已有提過程式基本流程為
1. 需將前篇03、04中修改的 IRremoteInt.h 檔案內的 RAWBUF、_GAP 二個參數改回預設值,以維持程式穩定:
#define RAWBUF 101
#define _GAP 5000
2. 再來貼上對應的創維遙控器編碼
N00~N09必須有值,沒有的部份請補 0,若使用相同的遙控器則不用修改
#define N00 0x80BFE11E
#define N01 0x80BF49B6
#define N02 0x80BFC936
#define N03 0x80BF33CC
#define N04 0x80BF718E
#define N05 0x80BFF10E
#define N06 0x80BF13EC
#define N07 0x80BF51AE
#define N08 0x80BFD12E
#define N09 0x80BF23DC
3. 貼上自家的冷氣空調錄製編碼
A. 從第35行 #define RAW_HDR_MARK 行數開始對應貼上
B. rawData_0[XX] ~ rawData_9[XX] 必須有值,沒有的部份請改成
byte rawData_X[1] = { 0 }; //X為對應的數字, 可參考 48 ~ 58 行中的語法
其它就不用修改了,測試正常後可再依自己的需求去修改程式內容,上面已有提過程式基本流程為
A. 讀取紅外線接收器的編碼信號
B. 比對符合電視遙控器數字鍵中的那個數字
C. 發射與數字對應的自家空調紅外線錄製信號
D. 過程中 LED 、蜂鳴器會有閃光及聲音作提示
完成的程式碼如下:
/*
IRremote
An IR LED must be connected to Arduino PWM pin 3.
Copyright Ken Shirriff
http://arcfn.com
2019.7.16 By MGP.
Only for Air conditioner. https://www.mygraphpaper.com blog.
*/
#include <IRremote.h>
//請注意執行本程式前, 需將 IRremoteInt.h 內 RAWBUF, _GAP 改回預設值
//IRremoteInt.h
//#define RAWBUF 101
//#define _GAP 5000
//創維機頂盒 遙控器數字 0~9 的NEC編碼
//N00~N09必須有值,沒有的請補 0
#define N00 0x80BFE11E
#define N01 0x80BF49B6
#define N02 0x80BFC936
#define N03 0x80BF33CC
#define N04 0x80BF718E
#define N05 0x80BFF10E
#define N06 0x80BF13EC
#define N07 0x80BF51AE
#define N08 0x80BFD12E
#define N09 0x80BF23DC
//自家的冷氣空調編碼參數
//所有的 rawData_X 陣列必須有值,沒有的請補 byte rawData_X[1] = { 0 }; //(X為對應的數字)
#define RAW_HDR_MARK 3350
#define RAW_HDR_SPACE 1800
#define RAW_BIT_MARK 400 // 450 -> 17 times. 400 -> 66 times. 350 -> 55 times.
#define RAW_ONE_SPACE 1350 // 1350 -> 11 times. 1300 -> 11 times. 500 -> 65 times.
#define RAW_ZERO_SPACE 500 // 500 -> 65 times. 450 -> 40 times. 400 -> 2 times.
#define RAW_RPT_MARK 400 //Repeat Gap
#define RAW_RPT_SPACE 16950
int rawData_Bits = 144 ;
//0 關機 (不用考慮其它參數設定)
byte rawData_0[18] = { 0xC4, 0xD3, 0x64, 0x80, 0x0, 0x0, 0x18, 0xB0, 0x6C, 0x2, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8 };
//1 保留 不使用的數字請用下行宣告做預設值
byte rawData_1[1] = { 0 };
//2 保留 不使用的數字請用下行宣告做預設值
byte rawData_2[1] = { 0 };
//3 保留 不使用的數字請用下行宣告做預設值
byte rawData_3[1] = { 0 };
//4 保留 不使用的數字請用下行宣告做預設值
byte rawData_4[1] = { 0 };
//5 快速冷房:開機 , 冷氣 , 25度 , 風速4級(最大) , 風向自動
byte rawData_5[18] = { 0xC4, 0xD3, 0x64, 0x80, 0x0, 0x4, 0x18, 0x90, 0x6C, 0x22, 0x46, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4C };
//6 一般使用:開機 , 冷氣 , 26度 , 風速自動, 風向自動
byte rawData_6[18] = { 0xC4, 0xD3, 0x64, 0x80, 0x0, 0x4, 0x18, 0x50, 0x6C, 0x2, 0x46, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF4 };
//7 一般使用:開機 , 冷氣 , 27度 , 風速自動, 風向自動
byte rawData_7[18] = { 0xC4, 0xD3, 0x64, 0x80, 0x0, 0x4, 0x18, 0xD0, 0x6C, 0x2, 0x46, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC };
//8 一般使用:開機 , 冷氣 , 28度 , 風速自動, 風向自動
byte rawData_8[18] = { 0xC4, 0xD3, 0x64, 0x80, 0x0, 0x4, 0x18, 0x30, 0x6C, 0x2, 0x46, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8C };
//9 搭配風扇:開機 , 冷氣 , 29度 , 風速自動, 風向自動
byte rawData_9[18] = { 0xC4, 0xD3, 0x64, 0x80, 0x0, 0x4, 0x18, 0xB0, 0x6C, 0x2, 0x46, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4C };
//以下為程式碼--------------------------------------------------------------------------------------------------------------------------
#define IR_NUM_SIZE 10 //使用0-9,共10個按鍵
int recvPin = 2; //紅外線接收在 Pin2; 發射內定在 Pin3
//int Pin9_SW = 9; //Pin9 按鈕保留
int Recv_Enable_LED_Pin = 10; //Pin10 啟動指示燈
int LED_Buzzer_Pin = 11; //Pin11 動作指示燈及聲音
int khz = 38; //使用 38kHz 頻率發射
IRrecv irrecv(recvPin); //設定接收用 Pin
IRsend irsend; //發射用
decode_results results; //儲存接收結果用
uint8_t IR_MAP_Index; //儲存找到對應的 0-9 數字
struct NUM_MAP { //將0-9的紅外線編碼對應成數字 0-9 ,方便比對搜尋
unsigned long IR_CODE;
uint8_t INDEX_NUM;
};
static struct NUM_MAP IR_MAP[IR_NUM_SIZE] = {{N00, 0}, {N01, 1}, {N02, 2}, {N03, 3}, {N04, 4}, {N05, 5}, {N06, 6}, {N07, 7}, {N08, 8}, {N09, 9}};
//控制 動作指示燈及聲音
void ST_LED_Buzzer (int Send_Status) {
while ( Send_Status > 0 ) {
if (Send_Status >= 5) {
digitalWrite(LED_Buzzer_Pin, HIGH);
delay(400);
digitalWrite(LED_Buzzer_Pin, LOW);
delay(400);
Send_Status = Send_Status - 5;
} else {
digitalWrite(LED_Buzzer_Pin, HIGH);
delay(150);
digitalWrite(LED_Buzzer_Pin, LOW);
delay(150);
Send_Status = Send_Status - 1;
}
}
}
//發送紅外線編碼陣列, 位元由左至右發送, 資料共計 rawData_Bits 位元
//void RAWIRSend(發送陣列資料, 陣列大小, 發射位元數, 發射頻率)
void RAWIRSend(byte SendRAWData[], unsigned int SendRAWData_Len, unsigned int SendRAWData_Bits, unsigned int hz)
{
int Rep_Temp = 0;
unsigned int rawData_Bits_Count =0;
byte Mask = 1;
if (RAW_RPT_SPACE > 0) Rep_Temp = 2;
else Rep_Temp = 1;
irsend.enableIROut(hz); // khz
irsend.space(max(RAW_HDR_MARK,RAW_HDR_SPACE)); //Initial signal
for (int j = 0; j < Rep_Temp; j++) { // if Repeat
//Start Single
irsend.mark(RAW_HDR_MARK);
irsend.space(RAW_HDR_SPACE);
for (unsigned int i = 0; i < SendRAWData_Len; i++) {
for (Mask = B10000000; Mask > 0; Mask >>= 1) {
if (rawData_Bits_Count < SendRAWData_Bits) {
if (SendRAWData[i] & Mask) {
//Bit 1
irsend.mark(RAW_BIT_MARK);
irsend.space(RAW_ONE_SPACE);
}
else {
//Bit 0
irsend.mark(RAW_BIT_MARK);
irsend.space(RAW_ZERO_SPACE);
}
rawData_Bits_Count++;
}
}
}
if (Rep_Temp > 1) {
//Send Repeat Single
irsend.mark(RAW_RPT_MARK);
irsend.space(RAW_RPT_SPACE);
}
irsend.space(max(RAW_HDR_MARK,RAW_HDR_SPACE)); //Stop Single
}
}
//初始設定
void setup()
{
Serial.begin(9600); //與電腦序列埠通訊連接用
irrecv.enableIRIn(); //充許接收紅外線信號
pinMode(Recv_Enable_LED_Pin, OUTPUT); //初始 啟動指示燈 腳位
pinMode(LED_Buzzer_Pin, OUTPUT); //初始 動作指示燈及聲音 腳位
pinMode(13, OUTPUT); //初始 開發板指示燈 腳位
digitalWrite(Recv_Enable_LED_Pin, HIGH);//打開 啟動指示燈, 要關閉請改 LOW
digitalWrite(LED_Buzzer_Pin, LOW); //關閉 動作指示燈及聲音
digitalWrite(13, HIGH); //打開 主板指示燈, 要關閉請改 LOW
}
//主程式迴圈
void loop() {
if (irrecv.decode(&results)) { //是否偵測到紅外線信號
if (results.decode_type == NEC) { //判斷為哪個數字鍵, 只偵測 NEC 編碼
Serial.print("NEC Code : "); //提供序列埠監看
Serial.print(results.value, HEX);
IR_MAP_Index = IR_NUM_SIZE;
for (uint8_t i = 0; i < IR_NUM_SIZE; i++) {
if (results.value == IR_MAP[i].IR_CODE) {
IR_MAP_Index = IR_MAP[i].INDEX_NUM;
break;
}
}
Serial.print(" Map Index : ");
Serial.println(IR_MAP_Index, DEC);
if (IR_MAP_Index < IR_NUM_SIZE) { //是否有找到對應的數字鍵
delay(100); //收發間隔,穩定用
switch (IR_MAP_Index) { //保留每個數字對應的程式操作空間
case 0:
ST_LED_Buzzer (10); //蜂鳴器先叫, 以免和冷氣混在一起
RAWIRSend(rawData_0, sizeof(rawData_0) / sizeof(rawData_0[0]), rawData_Bits, khz);
break;
case 1:
ST_LED_Buzzer (1); //蜂鳴器先叫, 以免和冷氣混在一起
RAWIRSend(rawData_1, sizeof(rawData_1) / sizeof(rawData_1[0]), rawData_Bits, khz);
break;
case 2:
ST_LED_Buzzer (2); //蜂鳴器先叫, 以免和冷氣混在一起
RAWIRSend(rawData_2, sizeof(rawData_2) / sizeof(rawData_2[0]), rawData_Bits, khz);
break;
case 3:
ST_LED_Buzzer (3); //蜂鳴器先叫, 以免和冷氣混在一起
RAWIRSend(rawData_3, sizeof(rawData_3) / sizeof(rawData_3[0]), rawData_Bits, khz);
break;
case 4:
ST_LED_Buzzer (4); //蜂鳴器先叫, 以免和冷氣混在一起
RAWIRSend(rawData_4, sizeof(rawData_4) / sizeof(rawData_4[0]), rawData_Bits, khz);
break;
case 5:
ST_LED_Buzzer (5); //蜂鳴器先叫, 以免和冷氣混在一起
RAWIRSend(rawData_5, sizeof(rawData_5) / sizeof(rawData_5[0]), rawData_Bits, khz);
break;
case 6:
ST_LED_Buzzer (6); //蜂鳴器先叫, 以免和冷氣混在一起
RAWIRSend(rawData_6, sizeof(rawData_6) / sizeof(rawData_6[0]), rawData_Bits, khz);
break;
case 7:
ST_LED_Buzzer (7); //蜂鳴器先叫, 以免和冷氣混在一起
RAWIRSend(rawData_7, sizeof(rawData_7) / sizeof(rawData_7[0]), rawData_Bits, khz);
break;
case 8:
ST_LED_Buzzer (8); //蜂鳴器先叫, 以免和冷氣混在一起
RAWIRSend(rawData_8, sizeof(rawData_8) / sizeof(rawData_8[0]), rawData_Bits, khz);
break;
case 9:
ST_LED_Buzzer (9); //蜂鳴器先叫, 以免和冷氣混在一起
RAWIRSend(rawData_9, sizeof(rawData_9) / sizeof(rawData_9[0]), rawData_Bits, khz);
break;
}
irrecv.enableIRIn(); //IRRemote 在發射的時候會把接收功能停掉, 重開接收
} else {
irrecv.resume(); //沒收到定義的 0-9 信號, 重新接收
}
}
irrecv.resume(); //沒收到NEC信號, 重新接收
}
}
請將程式編譯上傳,再次提醒測試時請一定要將紅外線發射LED的正上方圓頂對準冷氣機紅外線接收器的位置,測試時按下上述中米家APP內創維遙控器的 0-9 數字鍵,順利的話冷氣機就會做出對應的動作了。
測試完成後就不需要再用到手機去遙控了,日後操作使用的方式為:
小愛同學 (或遠端操作) -> 連動實體小米萬用遙控器 -> 透過此電路轉譯成自家冷氣紅外線信號發送 -> 讓自家冷氣空調動作
剩下的就是依各人需求將這個加入米家APP的智慧連動了,這樣小愛同學也能輕鬆幫忙。
想做這個東西很久了,現在終於完成,實在是說不出的高興,補完了心中的一塊拼圖。
謝謝耐心看完這份小專題。
測試完成後就不需要再用到手機去遙控了,日後操作使用的方式為:
小愛同學 (或遠端操作) -> 連動實體小米萬用遙控器 -> 透過此電路轉譯成自家冷氣紅外線信號發送 -> 讓自家冷氣空調動作
剩下的就是依各人需求將這個加入米家APP的智慧連動了,這樣小愛同學也能輕鬆幫忙。
想做這個東西很久了,現在終於完成,實在是說不出的高興,補完了心中的一塊拼圖。
謝謝耐心看完這份小專題。
Xiaomi Mijia universal infrared ray remote control for Unsupported air conditioner.