Prism

2019年7月16日 星期二

冷氣機紅外線遙控訊號連動 - 06 紅外線訊號轉譯程式

< 本文請勿轉載,謝謝 ! >

※ 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 行中的語法

其它就不用修改了,測試正常後可再依自己的需求去修改程式內容,上面已有提過程式基本流程為
    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的智慧連動了,這樣小愛同學也能輕鬆幫忙。

想做這個東西很久了,現在終於完成,實在是說不出的高興,補完了心中的一塊拼圖。

謝謝耐心看完這份小專題。



Xiaomi Mijia universal infrared ray remote control for Unsupported air conditioner.


1 則留言: