本文紀錄網友回報的適用冷氣空調機型,另程式碼貼於下方,有問題可方便討論除錯修正。
適用機型:
1. 三菱電機:機型 GE25NA / 遙控器 KM10D
記憶型空調紅外線轉譯器 程式碼
24行:移除雙斜線,會進入除錯模式,可在Arduino IDE 監控視窗查看轉譯器訊號接收、發射狀況
25行:移除雙斜線,紅外線發射時主板會有閃燈及嗶聲,初期可監聽看是否穩定執行
26行:移除雙斜線,焊接的擴展板S1按鍵可重發行目前的紅外線信號,方便測試用(電子積木版勿開啟,會造成連續誤觸發)
程式下載 (密碼藍字 mygraphpaper.com ) :https://www.mygraphpaper.com/download/MPG_TR_Translator.rar
程式下載 (密碼藍字 mygraphpaper.com ) :https://www.mygraphpaper.com/download/MPG_TR_Translator.rar
/*
IRremote
An IR LED must be connected to Arduino PWM pin 3.
Copyright Ken Shirriff
http://arcfn.com
MPG_IR_Translator Ver.1.0 2019.8.11 By MGP.
Only for Air conditioner. https://www.mygraphpaper.com blog.
Do not reprint,please.
This site is only for personal use, pay attention to safety when using, please.
Disclaimer : This site will not bear any liability for any errors or omissions.
If you agree, use the contents of this site.
請勿轉載.
本網站內容及程式僅提供個人非營利使用, 使用時請注意安全.
免責聲明 : 本網站不會對任何錯誤或遺漏承擔任何賠償責任, 若您同意再使用本網站、程式內容。
請注意執行本程式前, 請使用同壓縮檔內檔案覆蓋 IRremoteInt.h , 或手動修改參數值如下:
Line 43 : #define RAWBUF 624 // 預設儲存信號長度為 101 個 , 但冷氣機編碼超過, 需加大
Line 52 : int rawlen; // 冷氣機編碼的大小超過 Byte 255計數, 改為 int
Line 98 : #define _GAP 30000 // 預設信號結束的空白時間 5000, 但太小導致取樣不到 RPT_SPACE 的間隔參數, 需加大
*/
//#define MPG_DEBUG //Arduino監控視窗除錯用, 若您的紅外線信號記錄不到可打開監看取樣值.
//#define MPG_SendHit //紅外線傳送時會發出提示音及閃燈, 但會延遲發射時間
//#define MPG_SOLDER //焊接版本可打開以使用擴展板S1測試按鍵, 電子積木版勿開會造成誤觸發.
//各家紅外線編碼不同, 靈敏度調整用, 非必要請勿修改
#define Tolerance_scope 4 //MARK, SPACE 取樣誤差判斷容忍範圍, 4X50us=200us
#define MARK_EXCESS_Factor 2 //MARK, SPACE MARK_EXCESS 參數取樣誤差修正 100us/2=50us
//--------------------------------------------
#include <IRremote.h>
#include <EEPROM.h>
#define recvPin 2 //紅外線接收在 Pin2; 發射內定在 Pin3
#define Pin9_SW 9 //Pin9 擴展板S1按鍵 ,發射當前紅外線信號測試用
#define Recv_Enable_LED_Pin 10 //Pin10 擴展板啟動指示燈
#define LED_Buzzer_Pin 11 //Pin11 動作指示燈及聲音, Pin13同動作
#define khz 38 //使用 38kHz 頻率發射 RAW
#define SMax 10 //MARK, SPACE 取樣排序陣列大小
//米家APP內創維機頂盒 遙控器數字 "0~9 確定" 的NEC編碼
#define IR_NUM_SIZE 11 //使用 "0~9 確定" ,共11個按鍵 (注意 變更這個值會初始化歸零 EEPROM, 清除已記錄儲存的紅外線按鍵信號)
#define N00 0x80BFE11E //數字盤 0
#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
#define N10 0x80BF738C //數字盤 確定
struct NUM_MAP { //將 "0~9 確定" 的紅外線編碼對應成數字 0-10 ,方便比對搜尋
unsigned long IR_CODE;
byte INDEX_NUM;
};
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}, {N10, 10}};
struct IR_RAW_type { //原生信號參數結構 (注意 變更這個結構大小會初始化歸零 EEPROM, 清除已記錄儲存的紅外線按鍵信號)
char RAW_TYPE; //紅外線編碼類別 UNKNOWN, NEC, SONY ...
unsigned int RAW_HDR_MARK ; //初始信號 RAW_HDR_MARK + RAW_HDR_SPACE
unsigned int RAW_HDR_SPACE ;
unsigned int RAW_BIT_MARK ; //0 與 1 的信號組合
unsigned int RAW_ONE_SPACE ; //1 = RAW_BIT_MARK + RAW_ONE_SPACE
unsigned int RAW_ZERO_SPACE ; //0 = RAW_BIT_MARK + RAW_ZERO_SPACE
unsigned int rawData_Bits ; //傳送位元數, 可能會過 255 (624/2=312)
unsigned int rawHole[8][3] ; //特殊長度的 位置,數值(MARK+SPACE), 如 PRT_MARK+RPT_SPACE
byte rawHole_Count ; //rawHole陣列大小
byte rawData_Count ; //rawData陣列大小
byte rawData[39] ; //可記錄至 39X16 = 624bit, 要記錄10組信號, EEPROM只有1024byte, 總結構大小不能超過 1024/10 = 102byte
};
struct IR_RAW_type IR_RAW;
bool EnterSetup = false; //是否進入設定模式
byte SetupIndex = 0; //目前的設定數字
byte IR_MAP_Index; //儲存找到對應的 0-9
IRrecv irrecv(recvPin); //設定接收用 Pin
IRsend irsend; //發射用
decode_results results; //儲存接收結果用
//以下為程式碼--------------------------------------------------------------------------------------------------------------------------
//控制 動作指示燈及聲音 Pin11、13
void ST_LED_Buzzer (int Send_Status) {
while ( Send_Status > 0 ) {
if (Send_Status >= 5) {
digitalWrite(LED_Buzzer_Pin, HIGH);
digitalWrite(13, HIGH);
delay(400);
digitalWrite(LED_Buzzer_Pin, LOW);
digitalWrite(13, LOW);
delay(400);
Send_Status = Send_Status - 5;
} else {
digitalWrite(LED_Buzzer_Pin, HIGH);
digitalWrite(13, HIGH);
delay(150);
digitalWrite(LED_Buzzer_Pin, LOW);
digitalWrite(13, LOW);
delay(150);
Send_Status = Send_Status - 1;
}
}
}
//發送SONY紅外線編碼陣列 (SONY編碼較特殊, 變動MARK)
void SONYIRSend(byte SendRAWData[], unsigned int SendRAWData_Bits) {
unsigned long SONYIRCode;
SONYIRCode = ( (((unsigned long)SendRAWData[0]) << 24) | (((unsigned long)SendRAWData[1]) << 16) | (((unsigned long)SendRAWData[2]) << 8) | (((unsigned long)SendRAWData[3])) );
irsend.sendSony(SONYIRCode , SendRAWData_Bits);
}
//發送紅外線編碼陣列, 位元由左至右發送, 資料共計 rawData_Bits 位元
//void RAWIRSend(發送陣列資料, 陣列大小, 發射位元數, 發射頻率)
void RAWIRSend(byte SendRAWData[], byte SendRAWData_Len, unsigned int SendRAWData_Bits, byte hz) {
unsigned int rawData_Bits_Count =0;
byte Mask = 1;
byte rawHole_ind = 0;
irsend.enableIROut(hz); // khz
irsend.space(max(IR_RAW.RAW_HDR_MARK,IR_RAW.RAW_HDR_SPACE)); //Initial signal
irsend.mark(IR_RAW.RAW_HDR_MARK);
irsend.space(IR_RAW.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 ((rawHole_ind < IR_RAW.rawHole_Count) && (rawData_Bits_Count == IR_RAW.rawHole[rawHole_ind][0])){
//Special length
irsend.mark(IR_RAW.rawHole[rawHole_ind][1]);
irsend.space(IR_RAW.rawHole[rawHole_ind][2]);
rawHole_ind ++ ;
} else {
if (SendRAWData[i] & Mask) {
//Bit 1
irsend.mark(IR_RAW.RAW_BIT_MARK);
irsend.space(IR_RAW.RAW_ONE_SPACE);
}
else {
//Bit 0
irsend.mark(IR_RAW.RAW_BIT_MARK);
irsend.space(IR_RAW.RAW_ZERO_SPACE);
}
}
rawData_Bits_Count++;
}
}
}
irsend.space(max(IR_RAW.RAW_HDR_MARK,IR_RAW.RAW_HDR_SPACE)); //Stop signal
}
//取樣陣列右移排大小
unsigned int Probability[SMax], Temporary[SMax]; //取樣陣列 數值, 次數
void Shift_Val(unsigned int Val){
byte i,j;
for(i = 0; i < SMax; i++) { //將較小數值向後移
if ((i>0) && (Val == Probability[i-1])) break;
if (Val > Probability[i]) {
for(j = SMax-1; j >= i+1; j--) {
Probability[j] = Probability[j-1];
Temporary[j] = Temporary[j-1];
}
Probability[i] = Val;
Temporary[j] = 0;
break;
}
}
for(i = 0; i<SMax; i++) { //記錄出現次數
if (Val == Probability[i]) {
Temporary[i]++;
break;
}
}
}
//統計原生訊號出現次數
//FindMaxCount(原始編碼訊號, 處理編碼訊號陣列起始位置, 是否需比對 MARK 值, 判斷是ONE_SPACE的判斷值)
unsigned int FindMaxCount(decode_results *results, int StartPos, unsigned int Check_RAW_BIT_MARK, unsigned int MAX_RAW_ZERO_SPACE) {
unsigned int Return_MAX_Value = 0; //回傳值
for (byte i=0 ; i < SMax ; i++) {
Probability[i]=0; //初始陣列
Temporary[i]=0;
}
for (int i = StartPos; i < results->rawlen; i+=2) { //results->rawlen 為int
if ((Check_RAW_BIT_MARK == 0) || (results->rawbuf[i-1] == Check_RAW_BIT_MARK)){ //是否需比對 MARK 值
if ((MAX_RAW_ZERO_SPACE == 0) || (results->rawbuf[i] > MAX_RAW_ZERO_SPACE)){ //判斷 Space 長的判斷值
Shift_Val(results->rawbuf[i]); //取樣陣列排大小
}
}
}
//從最小值找向連續的最大值, 出現最多次的
unsigned int Last_Count = 0;
for (int i = SMax-1; i>=0; i--) {
if ((Probability[i] >= Tolerance_scope) && (Temporary[i] > 0)) { //有信號
if (Return_MAX_Value == 0) {
Return_MAX_Value = Probability[i];
Last_Count = Temporary[i];
} else {
if ((Probability[i] - Probability[i+1]) <= Tolerance_scope) { //連續誤差訊號
if (Temporary[i] >= Last_Count) {
Return_MAX_Value = Probability[i];
Last_Count = Temporary[i];
}
} else {
break; //信號不連續
}
}
}
}
return Return_MAX_Value;
}
//取得原生訊號參數及重新編碼
void EncodeRAWCode (decode_results *results) {
IR_RAW.rawData_Count = 0;
IR_RAW.rawHole_Count = 0 ;
IR_RAW.RAW_TYPE = results->decode_type;
#ifdef MPG_DEBUG
Serial.println(F(""));
Serial.print(F("IR Struct Size (byte) : "));
Serial.println(sizeof(IR_RAW),DEC);
Serial.print(F("IR TYPE : "));
Serial.println(IR_RAW.RAW_TYPE,DEC); //4 SONY
#endif
//SONY使用40Khz, 且是改變 MARK的長度, SPACE不變, 直接使用內建函數解碼, 用38K可以接收但失敗率較大
//SONY發送為 unsigned long 4Byte, 拆解入 IR_RAW.rawData 存放
if (IR_RAW.RAW_TYPE == SONY) {
IR_RAW.RAW_HDR_MARK = 2400;
IR_RAW.RAW_HDR_SPACE = 600;
IR_RAW.RAW_BIT_MARK = 600;
IR_RAW.RAW_ZERO_SPACE = 600;
IR_RAW.RAW_ONE_SPACE = 1200;
IR_RAW.rawData_Bits = results->bits;
IR_RAW.rawData_Count = 4;
IR_RAW.rawData[0] = (byte)((results->value & 0xff000000) >> 24);
IR_RAW.rawData[1] = (byte)((results->value & 0x00ff0000) >> 16);
IR_RAW.rawData[2] = (byte)((results->value & 0x0000ff00) >> 8);
IR_RAW.rawData[3] = (byte)((results->value & 0x000000ff));
} else {
IR_RAW.rawData_Bits = (results->rawlen-3)/2; //扣除頭2尾1, 2個信號為 1bit
IR_RAW.RAW_HDR_MARK = results->rawbuf[1]; //第一個訊號[0]無效,[1]為RAW_HDR_MARK
IR_RAW.RAW_HDR_SPACE = results->rawbuf[2]; //[2]RAW_HDR_SPACE
#ifdef MPG_DEBUG
Serial.println(F(""));
Serial.println(F("// My Data Start , Mark & Ctrl-C Copy ---------------"));
Serial.println(F(""));
Serial.print(F("#define RAW_HDR_MARK "));
Serial.print(IR_RAW.RAW_HDR_MARK * USECPERTICK - MARK_EXCESS/MARK_EXCESS_Factor, DEC);
Serial.println(F(""));
Serial.print(F("#define RAW_HDR_SPACE "));
Serial.print(IR_RAW.RAW_HDR_SPACE * USECPERTICK + MARK_EXCESS/MARK_EXCESS_Factor, DEC);
#endif
//取得 BIT_MARK , 3開始的奇數位置為 RAW_BIT_MARK 值
IR_RAW.RAW_BIT_MARK = FindMaxCount(results , 3, 0, 0);
#ifdef MPG_DEBUG
Serial.println(F(""));
Serial.print(F("#define RAW_BIT_MARK "));
Serial.print(IR_RAW.RAW_BIT_MARK * USECPERTICK - MARK_EXCESS/MARK_EXCESS_Factor, DEC);
Serial.print(F(" // "));
for(byte i = 0; i < SMax; i++) {
if (Probability[i] > 0 ) {
Serial.print(F("( "));
Serial.print(Probability[i] * USECPERTICK, DEC);
Serial.print(F(" , "));
Serial.print(Temporary[i] , DEC);
Serial.print(F(" ), "));
}
}
Serial.print(F(" times."));
#endif
//取得 ZERO_SPACE , 最小值範圍中最多出現次數, 4開始的偶數位置為 RAW_ZERO_SPACE 值
IR_RAW.RAW_ZERO_SPACE = FindMaxCount(results , 4, IR_RAW.RAW_BIT_MARK, 0);
#ifdef MPG_DEBUG
Serial.println(F(""));
Serial.print(F("#define RAW_ZERO_SPACE "));
Serial.print(IR_RAW.RAW_ZERO_SPACE * USECPERTICK + MARK_EXCESS/MARK_EXCESS_Factor, DEC);
Serial.print(F(" // "));
for(byte i = 0; i < SMax; i++) {
if (Probability[i] > 0 ) {
Serial.print(F("( "));
Serial.print(Probability[i] * USECPERTICK, DEC);
Serial.print(F(" , "));
Serial.print(Temporary[i] , DEC);
Serial.print(F(" ), "));
}
}
Serial.print(F(" times. (in RAW_BIT_MARK)"));
#endif
//取得 ONE_SPACE , 最小值範圍中最多出現次數, 4開始的偶數位置為 RAW_ONE_SPACE 值
//ONE_SPACE 的下限值, 若仍太小可漸加大Tolerance_scope測試
unsigned int MAX_RAW_ZERO_SPACE = IR_RAW.RAW_ZERO_SPACE + Tolerance_scope;
IR_RAW.RAW_ONE_SPACE = FindMaxCount(results , 4, IR_RAW.RAW_BIT_MARK, MAX_RAW_ZERO_SPACE);
#ifdef MPG_DEBUG
Serial.println(F(""));
Serial.print(F("#define RAW_ONE_SPACE "));
Serial.print(IR_RAW.RAW_ONE_SPACE * USECPERTICK + MARK_EXCESS/MARK_EXCESS_Factor, DEC);
Serial.print(F(" // "));
for(byte i = 0; i < SMax; i++) {
if (Probability[i] > 0 ) {
Serial.print(F("( "));
Serial.print(Probability[i] * USECPERTICK, DEC);
Serial.print(F(" , "));
Serial.print(Temporary[i] , DEC);
Serial.print(F(" ), "));
}
}
Serial.print(F(" times. (in RAW_BIT_MARK)"));
#endif
//將 Mark+Space 的 0 1 編碼為 Byte 型態存放
byte rawTemp = 0; //暫存值
byte Bits_Count = 0; //目前 Bit 計數
MAX_RAW_ZERO_SPACE = (IR_RAW.RAW_ONE_SPACE + IR_RAW.RAW_ZERO_SPACE) / 2; //取中間值作 0 1 判斷
for (int i = 4; i < results->rawlen; i+=2) {
Bits_Count++;
rawTemp = rawTemp << 1;
if (results->rawbuf[i] > MAX_RAW_ZERO_SPACE) rawTemp = rawTemp | 1;
if (Bits_Count==8) { //完成 1 Byte 存放入rawData
IR_RAW.rawData[IR_RAW.rawData_Count] = rawTemp;
Bits_Count = 0;
IR_RAW.rawData_Count ++;
rawTemp = 0;
}
}
if (Bits_Count != 0) { //不足 1Byte 的補完
for (byte i = (Bits_Count+1); i <= 8; i++) {
rawTemp = rawTemp << 1;
}
IR_RAW.rawData[IR_RAW.rawData_Count] = rawTemp;
IR_RAW.rawData_Count ++;
}
//掃描特殊長度
byte Max_rawHole = sizeof(IR_RAW.rawHole)/sizeof(unsigned int)/3;
for (int i = 3; i < results->rawlen-1; i++) {
if(i%2==1){ //MARK
if (!((results->rawbuf[i]>=IR_RAW.RAW_BIT_MARK-Tolerance_scope) && (results->rawbuf[i]<=IR_RAW.RAW_BIT_MARK+Tolerance_scope))) {
if (IR_RAW.rawHole_Count >= Max_rawHole) {
results->overflow = true; //特殊編碼太多, 無空間可記錄, 報溢位錯誤
break;
}
IR_RAW.rawHole[IR_RAW.rawHole_Count][0] = (i-3)/2; //0起算
IR_RAW.rawHole[IR_RAW.rawHole_Count][1] = results->rawbuf[i] * USECPERTICK;
IR_RAW.rawHole[IR_RAW.rawHole_Count][2] = results->rawbuf[i+1] * USECPERTICK;
IR_RAW.rawHole_Count++;
i++;
}
} else { //SPACE
if (results->rawbuf[i]>(IR_RAW.RAW_ONE_SPACE + Tolerance_scope)) {
if (IR_RAW.rawHole_Count >= Max_rawHole) {
results->overflow = true; //特殊編碼太多, 無空間可記錄, 報溢位錯誤
break;
}
IR_RAW.rawHole[IR_RAW.rawHole_Count][0] = (i-4)/2; //0起算
IR_RAW.rawHole[IR_RAW.rawHole_Count][1] = results->rawbuf[i-1] * USECPERTICK;
IR_RAW.rawHole[IR_RAW.rawHole_Count][2] = results->rawbuf[i] * USECPERTICK;
IR_RAW.rawHole_Count++;
}
}
}
//做硬體取樣延遲誤差修正
IR_RAW.RAW_HDR_MARK = IR_RAW.RAW_HDR_MARK * USECPERTICK - MARK_EXCESS/MARK_EXCESS_Factor; //次數 X 間隔(50us) - 誤差
IR_RAW.RAW_HDR_SPACE = IR_RAW.RAW_HDR_SPACE * USECPERTICK + MARK_EXCESS/MARK_EXCESS_Factor;
IR_RAW.RAW_BIT_MARK = IR_RAW.RAW_BIT_MARK * USECPERTICK - MARK_EXCESS/MARK_EXCESS_Factor;
IR_RAW.RAW_ONE_SPACE = IR_RAW.RAW_ONE_SPACE * USECPERTICK + MARK_EXCESS/MARK_EXCESS_Factor;
IR_RAW.RAW_ZERO_SPACE = IR_RAW.RAW_ZERO_SPACE * USECPERTICK + MARK_EXCESS/MARK_EXCESS_Factor;
}
#ifdef MPG_DEBUG
Serial.println(F(""));
Serial.println(F(""));
Serial.print(F("int rawData_Bits = "));
Serial.print(IR_RAW.rawData_Bits, DEC);
Serial.print(F(" ;"));
Serial.println(F(""));
Serial.print(F("byte rawData["));
Serial.print(IR_RAW.rawData_Count, DEC);
Serial.print(F("] = { "));
for (byte i = 0; i < IR_RAW.rawData_Count; i++) {
Serial.print(F("0x"));
Serial.print(IR_RAW.rawData[i], HEX);
if ( i < IR_RAW.rawData_Count-1 ) Serial.print(F(", "));
}
Serial.println(F(" };"));
Serial.print(F("unsigned int rawHole["));
Serial.print(IR_RAW.rawHole_Count, DEC);
Serial.print(F("][3] = { "));
for (byte i = 0; i < IR_RAW.rawHole_Count; i++) {
Serial.print(F("{ "));
for (byte j = 0; j < 3; j++) {
Serial.print(IR_RAW.rawHole[i][j], DEC);
if ( j < 2 ) Serial.print(F(", "));
}
Serial.print(F(" } "));
if ( i < IR_RAW.rawHole_Count-1 ) Serial.print(F(", "));
}
Serial.println(F("};"));
Serial.println(F(""));
Serial.println(F("// My Data End --------------- By MGP."));
Serial.println(F(""));
#endif
}
//初始設定
void setup() {
Serial.begin(9600); //與電腦序列埠通訊連接用
irrecv.enableIRIn(); //充許接收紅外線信號
SetupIndex = 0;
IR_RAW.rawData_Count = 0;
if ((EEPROM.read(1022) != IR_NUM_SIZE) || (EEPROM.read(1023) != sizeof(IR_RAW))) { //第一次使用, 初始化歸零 EEPROM
Serial.println(F("Wait for initial EEPROM ..."));
for(int i =0; i<1022; i++) EEPROM.write(i,0);
EEPROM.write(1022,IR_NUM_SIZE); //標記 EEPROM 結構個數
EEPROM.write(1023,sizeof(IR_RAW)); //標記 EEPROM 結構大小
}
pinMode(Pin9_SW, INPUT); //初始 擴展板S1按鍵 腳位
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, LOW); //關閉 主板指示燈, 相容電子積木版用, 同 LED_Buzzer_Pin
ST_LED_Buzzer (1); //通知已啟動開始接收
Serial.println(F("MPG IR-Translator is Ready."));
}
//主程式迴圈
void loop() {
#ifdef MPG_SOLDER //焊接擴展版 S1按鈕
if (digitalRead(Pin9_SW) == LOW) {
Serial.println(sizeof(IR_RAW));
delay(100);
ST_LED_Buzzer (1);
RAWIRSend(IR_RAW.rawData, IR_RAW.rawData_Count, IR_RAW.rawData_Bits, khz);
#ifdef MPG_DEBUG
show_RAW_IR();
#endif
irrecv.enableIRIn();
delay(100);
}
#endif
if (irrecv.decode(&results)) { //是否偵測到紅外線信號
IR_MAP_Index = IR_NUM_SIZE; //預設IR_MAP_Index
if (results.decode_type == NEC) { //判斷為哪個數字鍵, 只偵測 NEC 編碼
#ifdef MPG_DEBUG
Serial.print(F("NEC Code : ")); //提供序列埠監看
Serial.print(results.value, HEX);
#endif
for (byte i = 0; i < IR_NUM_SIZE; i++) {
if (results.value == IR_MAP[i].IR_CODE) {
IR_MAP_Index = IR_MAP[i].INDEX_NUM;
break;
}
}
#ifdef MPG_DEBUG
Serial.print(F(" Map Index : "));
Serial.println(IR_MAP_Index, DEC);
#endif
if (IR_MAP_Index < IR_NUM_SIZE) { //是否有找到對應的數字鍵
delay(100); //收發間隔,穩定用
switch (IR_MAP_Index) { //不合併,保留每個數字對應的程式操作空間
case 0:
if(EnterSetup) {
ST_LED_Buzzer (10);
SetupIndex = IR_MAP_Index;
} else {
#ifdef MPG_SendHit
ST_LED_Buzzer (1); //蜂鳴器先叫, 以免和冷氣混在一起
#endif
EEPROM_readAnything(IR_MAP_Index*sizeof(IR_RAW), IR_RAW);
if (IR_RAW.RAW_TYPE == SONY) SONYIRSend(IR_RAW.rawData, IR_RAW.rawData_Bits);
else RAWIRSend(IR_RAW.rawData, IR_RAW.rawData_Count, IR_RAW.rawData_Bits, khz);
#ifdef MPG_DEBUG
show_RAW_IR();
#endif
}
break;
case 1:
if(EnterSetup) {
ST_LED_Buzzer (IR_MAP_Index);
SetupIndex = IR_MAP_Index;
} else {
#ifdef MPG_SendHit
ST_LED_Buzzer (1); //蜂鳴器先叫, 以免和冷氣混在一起
#endif
EEPROM_readAnything(IR_MAP_Index*sizeof(IR_RAW), IR_RAW);
if (IR_RAW.RAW_TYPE == SONY) SONYIRSend(IR_RAW.rawData, IR_RAW.rawData_Bits);
else RAWIRSend(IR_RAW.rawData, IR_RAW.rawData_Count, IR_RAW.rawData_Bits, khz);
#ifdef MPG_DEBUG
show_RAW_IR();
#endif
}
break;
case 2:
if(EnterSetup) {
ST_LED_Buzzer (IR_MAP_Index);
SetupIndex = IR_MAP_Index;
} else {
#ifdef MPG_SendHit
ST_LED_Buzzer (1); //蜂鳴器先叫, 以免和冷氣混在一起
#endif
EEPROM_readAnything(IR_MAP_Index*sizeof(IR_RAW), IR_RAW);
if (IR_RAW.RAW_TYPE == SONY) SONYIRSend(IR_RAW.rawData, IR_RAW.rawData_Bits);
else RAWIRSend(IR_RAW.rawData, IR_RAW.rawData_Count, IR_RAW.rawData_Bits, khz);
#ifdef MPG_DEBUG
show_RAW_IR();
#endif
}
break;
case 3:
if(EnterSetup) {
ST_LED_Buzzer (IR_MAP_Index);
SetupIndex = IR_MAP_Index;
} else {
#ifdef MPG_SendHit
ST_LED_Buzzer (1); //蜂鳴器先叫, 以免和冷氣混在一起
#endif
EEPROM_readAnything(IR_MAP_Index*sizeof(IR_RAW), IR_RAW);
if (IR_RAW.RAW_TYPE == SONY) SONYIRSend(IR_RAW.rawData, IR_RAW.rawData_Bits);
else RAWIRSend(IR_RAW.rawData, IR_RAW.rawData_Count, IR_RAW.rawData_Bits, khz);
#ifdef MPG_DEBUG
show_RAW_IR();
#endif
}
break;
case 4:
if(EnterSetup) {
ST_LED_Buzzer (IR_MAP_Index);
SetupIndex = IR_MAP_Index;
} else {
#ifdef MPG_SendHit
ST_LED_Buzzer (1); //蜂鳴器先叫, 以免和冷氣混在一起
#endif
EEPROM_readAnything(IR_MAP_Index*sizeof(IR_RAW), IR_RAW);
if (IR_RAW.RAW_TYPE == SONY) SONYIRSend(IR_RAW.rawData, IR_RAW.rawData_Bits);
else RAWIRSend(IR_RAW.rawData, IR_RAW.rawData_Count, IR_RAW.rawData_Bits, khz);
#ifdef MPG_DEBUG
show_RAW_IR();
#endif
}
break;
case 5:
if(EnterSetup) {
ST_LED_Buzzer (IR_MAP_Index);
SetupIndex = IR_MAP_Index;
} else {
#ifdef MPG_SendHit
ST_LED_Buzzer (1); //蜂鳴器先叫, 以免和冷氣混在一起
#endif
EEPROM_readAnything(IR_MAP_Index*sizeof(IR_RAW), IR_RAW);
if (IR_RAW.RAW_TYPE == SONY) SONYIRSend(IR_RAW.rawData, IR_RAW.rawData_Bits);
else RAWIRSend(IR_RAW.rawData, IR_RAW.rawData_Count, IR_RAW.rawData_Bits, khz);
#ifdef MPG_DEBUG
show_RAW_IR();
#endif
}
break;
case 6:
if(EnterSetup) {
ST_LED_Buzzer (IR_MAP_Index);
SetupIndex = IR_MAP_Index;
} else {
#ifdef MPG_SendHit
ST_LED_Buzzer (1); //蜂鳴器先叫, 以免和冷氣混在一起
#endif
EEPROM_readAnything(IR_MAP_Index*sizeof(IR_RAW), IR_RAW);
if (IR_RAW.RAW_TYPE == SONY) SONYIRSend(IR_RAW.rawData, IR_RAW.rawData_Bits);
else RAWIRSend(IR_RAW.rawData, IR_RAW.rawData_Count, IR_RAW.rawData_Bits, khz);
#ifdef MPG_DEBUG
show_RAW_IR();
#endif
}
break;
case 7:
if(EnterSetup) {
ST_LED_Buzzer (IR_MAP_Index);
SetupIndex = IR_MAP_Index;
} else {
#ifdef MPG_SendHit
ST_LED_Buzzer (1); //蜂鳴器先叫, 以免和冷氣混在一起
#endif
EEPROM_readAnything(IR_MAP_Index*sizeof(IR_RAW), IR_RAW);
if (IR_RAW.RAW_TYPE == SONY) SONYIRSend(IR_RAW.rawData, IR_RAW.rawData_Bits);
else RAWIRSend(IR_RAW.rawData, IR_RAW.rawData_Count, IR_RAW.rawData_Bits, khz);
#ifdef MPG_DEBUG
show_RAW_IR();
#endif
}
break;
case 8:
if(EnterSetup) {
ST_LED_Buzzer (IR_MAP_Index);
SetupIndex = IR_MAP_Index;
} else {
#ifdef MPG_SendHit
ST_LED_Buzzer (1); //蜂鳴器先叫, 以免和冷氣混在一起
#endif
EEPROM_readAnything(IR_MAP_Index*sizeof(IR_RAW), IR_RAW);
if (IR_RAW.RAW_TYPE == SONY) SONYIRSend(IR_RAW.rawData, IR_RAW.rawData_Bits);
else RAWIRSend(IR_RAW.rawData, IR_RAW.rawData_Count, IR_RAW.rawData_Bits, khz);
#ifdef MPG_DEBUG
show_RAW_IR();
#endif
}
break;
case 9:
if(EnterSetup) {
ST_LED_Buzzer (IR_MAP_Index);
SetupIndex = IR_MAP_Index;
} else {
#ifdef MPG_SendHit
ST_LED_Buzzer (1); //蜂鳴器先叫, 以免和冷氣混在一起
#endif
EEPROM_readAnything(IR_MAP_Index*sizeof(IR_RAW), IR_RAW);
if (IR_RAW.RAW_TYPE == SONY) SONYIRSend(IR_RAW.rawData, IR_RAW.rawData_Bits);
else RAWIRSend(IR_RAW.rawData, IR_RAW.rawData_Count, IR_RAW.rawData_Bits, khz);
#ifdef MPG_DEBUG
show_RAW_IR();
#endif
}
break;
case 10: //進入設定模式
if(EnterSetup) {
ST_LED_Buzzer (20);
EnterSetup = false;
} else {
ST_LED_Buzzer (15);
EnterSetup = true;
}
break;
}
irrecv.enableIRIn(); //IRRemote 在發射的時候會把接收功能停掉, 重開接收
}
}
if (IR_MAP_Index == IR_NUM_SIZE) { //非數字鍵
if(EnterSetup) {
if (results.overflow) { //位元數太多, 無法記錄
#ifdef MPG_DEBUG
dumpInfo(&results);
dumpRaw(&results);
show_RAW_IR();
#endif
} else {
#ifdef MPG_DEBUG
dumpInfo(&results);
dumpRaw(&results);
#endif
EncodeRAWCode(&results);
if ((IR_RAW.RAW_BIT_MARK >0) && (IR_RAW.RAW_ONE_SPACE >0) && (IR_RAW.RAW_ZERO_SPACE >0) && (results.overflow == false)) {
EEPROM_writeAnything(SetupIndex*sizeof(IR_RAW), IR_RAW);
ST_LED_Buzzer(2); //提示已記錄按鍵紅外線值
}
}
}
}
irrecv.resume(); //重新接收
delay(100); //穩定用
}
}
//EEPROM 函數
template <class T> int EEPROM_writeAnything(int ee, const T& value) {
const byte* p = (const byte*)(const void*)&value;
unsigned int i;
for (i = 0; i < sizeof(value); i++)
EEPROM.write(ee++, *p++);
return i;
}
template <class T> int EEPROM_readAnything(int ee, T& value) {
byte* p = (byte*)(void*)&value;
unsigned int i;
for (i = 0; i < sizeof(value); i++)
*p++ = EEPROM.read(ee++);
return i;
}
//------- 以下為除錯用函數
//顯示目前RAW結構參數
void show_RAW_IR(){
Serial.println(F(""));
Serial.println(F("// My Data Start , Mark & Ctrl-C Copy ---------------"));
Serial.println(F(""));
Serial.print(F("#define RAW_HDR_MARK "));
Serial.print(IR_RAW.RAW_HDR_MARK, DEC);
Serial.println(F(""));
Serial.print(F("#define RAW_HDR_SPACE "));
Serial.print(IR_RAW.RAW_HDR_SPACE, DEC);
Serial.println(F(""));
Serial.print(F("#define RAW_BIT_MARK "));
Serial.print(IR_RAW.RAW_BIT_MARK, DEC);
Serial.print(F(" // "));
Serial.print(Probability[0] * USECPERTICK, DEC);
Serial.print(F(" -> "));
Serial.print(Temporary[0] , DEC);
Serial.print(F(" times. "));
Serial.print(Probability[1] * USECPERTICK, DEC);
Serial.print(F(" -> "));
Serial.print(Temporary[1] , DEC);
Serial.print(F(" times. "));
Serial.print(Probability[2] * USECPERTICK, DEC);
Serial.print(F(" -> "));
Serial.print(Temporary[2] , DEC);
Serial.print(F(" times."));
Serial.println(F(""));
Serial.print(F("#define RAW_ONE_SPACE "));
Serial.print(IR_RAW.RAW_ONE_SPACE, DEC);
Serial.print(F(" // "));
Serial.print(Probability[0] * USECPERTICK, DEC);
Serial.print(F(" -> "));
Serial.print(Temporary[0] , DEC);
Serial.print(F(" times. "));
Serial.print(Probability[1] * USECPERTICK, DEC);
Serial.print(F(" -> "));
Serial.print(Temporary[1] , DEC);
Serial.print(F(" times. "));
Serial.print(Probability[2] * USECPERTICK, DEC);
Serial.print(F(" -> "));
Serial.print(Temporary[2] , DEC);
Serial.print(F(" times."));
Serial.println(F(""));
Serial.print(F("#define RAW_ZERO_SPACE "));
Serial.print(IR_RAW.RAW_ZERO_SPACE, DEC);
Serial.print(F(" // "));
Serial.print(Probability[0] * USECPERTICK, DEC);
Serial.print(F(" -> "));
Serial.print(Temporary[0] , DEC);
Serial.print(F(" times. "));
Serial.print(Probability[1] * USECPERTICK, DEC);
Serial.print(F(" -> "));
Serial.print(Temporary[1] , DEC);
Serial.print(F(" times. "));
Serial.print(Probability[2] * USECPERTICK, DEC);
Serial.print(F(" -> "));
Serial.print(Temporary[2] , DEC);
Serial.print(F(" times."));
Serial.println(F(""));
Serial.println(F(""));
Serial.print(F("int rawData_Bits = "));
Serial.print(IR_RAW.rawData_Bits, DEC);
Serial.print(F(" ;"));
Serial.println(F(""));
Serial.print(F("byte rawData["));
Serial.print(IR_RAW.rawData_Count, DEC);
Serial.print(F("] = { "));
for (byte i = 0; i < IR_RAW.rawData_Count; i++) {
Serial.print(F("0x"));
Serial.print(IR_RAW.rawData[i], HEX);
if ( i < IR_RAW.rawData_Count-1 ) Serial.print(F(", "));
}
Serial.println(F(" };"));
Serial.print(F("unsigned int rawHole["));
Serial.print(IR_RAW.rawHole_Count, DEC);
Serial.print(F("][3] = { "));
for (byte i = 0; i < IR_RAW.rawHole_Count; i++) {
Serial.print(F("{ "));
for (byte j = 0; j < 3; j++) {
Serial.print(IR_RAW.rawHole[i][j], DEC);
if ( j < 2 ) Serial.print(F(", "));
}
Serial.print(F(" } "));
if ( i < IR_RAW.rawHole_Count-1 ) Serial.print(F(", "));
}
Serial.println(F("};"));
Serial.println(F(""));
Serial.println(F("// My Data End --------------- By MGP."));
Serial.println(F(""));
Serial.println(F(""));
}
//------- 以下為原紅外線程式庫範例內函數
void encoding (decode_results *results)
{
switch (results->decode_type) {
default:
case UNKNOWN: Serial.print(F("UNKNOWN")); break ;
case NEC: Serial.print(F("NEC")); break ;
case SONY: Serial.print(F("SONY")); break ;
case RC5: Serial.print(F("RC5")); break ;
case RC6: Serial.print(F("RC6")); break ;
case DISH: Serial.print(F("DISH")); break ;
case SHARP: Serial.print(F("SHARP")); break ;
case JVC: Serial.print(F("JVC")); break ;
case SANYO: Serial.print(F("SANYO")); break ;
case MITSUBISHI: Serial.print(F("MITSUBISHI")); break ;
case SAMSUNG: Serial.print(F("SAMSUNG")); break ;
case LG: Serial.print(F("LG")); break ;
case WHYNTER: Serial.print(F("WHYNTER")); break ;
case AIWA_RC_T501: Serial.print(F("AIWA_RC_T501")); break ;
case PANASONIC: Serial.print(F("PANASONIC")); break ;
case DENON: Serial.print(F("Denon")); break ;
}
}
void ircode (decode_results *results)
{
// Panasonic has an Address
if (results->decode_type == PANASONIC) {
Serial.print(results->address, HEX);
Serial.print(F(":"));
}
// Print Code
Serial.print(results->value, HEX);
}
void dumpInfo(decode_results *results)
{
// Check if the buffer overflowed
if (results->overflow) {
Serial.println(F("IR code too long. Edit IRremoteInt.h and increase RAWBUF"));
return;
}
// Show Encoding standard
Serial.print(F("Encoding : "));
encoding(results);
Serial.println(F(""));
// Show Code & length
Serial.print(F("Code : "));
ircode(results);
Serial.print(F(" ("));
Serial.print(results->bits, DEC);
Serial.println(F(" bits)"));
}
//+=============================================================================
// Dump out the decode_results structure.
//
void dumpRaw (decode_results *results)
{
// Print Raw data
Serial.print("Timing[");
Serial.print(results->rawlen-1, DEC);
Serial.println("]: ");
for (int i = 1; i < results->rawlen; i++) {
unsigned long x = results->rawbuf[i] * USECPERTICK;
if (!(i & 1)) { // even
Serial.print("-");
if (x < 1000) Serial.print(" ") ;
if (x < 100) Serial.print(" ") ;
Serial.print(x, DEC);
} else { // odd
Serial.print(" ");
Serial.print("+");
if (x < 1000) Serial.print(" ") ;
if (x < 100) Serial.print(" ") ;
Serial.print(x, DEC);
if (i < results->rawlen-1) Serial.print(", "); //',' not needed for last one
}
if (!(i % 8)) Serial.println("");
}
Serial.println(""); // Newline
}
2019.8.14 補充
使用於本製作內容的小米萬能遙控器是一代,後續有二代增強版、搭配小愛同學的版本等,介面會有所不同,若無創維機上盒的選項,可用長虹電視代替,可將 45-55 行替換如下,再上傳至晶片
#define N00 0xBD827D //數字盤 0
#define N01 0xBDA857
#define N02 0xBD6897
#define N03 0xBDE817
#define N04 0xBD9867
#define N05 0xBD58A7
#define N06 0xBDD827
#define N07 0xBDB847
#define N08 0xBD7887
#define N09 0xBDF807
#define N10 0xBD50AF //數字盤 確定
或者可使用其它電視(請先將 實體小米萬能遙控器 對準 轉譯器接收模組)
A. 請將上方程式 24-25 行左方的雙斜線拿掉,將程式上傳至晶片進入除錯模式
B. 選取米家APP內萬能遙控器中任一牌電視測試(按任一鍵均可),若此電視遙控器是NEC編碼的,在Arduino IDE監控視窗內會出現編碼代號,若無請更換其它牌電視繼續測試
C. 找到的話請依序記錄 "0~9 確定" 共11個鍵的NEC編碼代號替換 45-55 行編碼代號 (0x 之後的16進位數字)
D. 加回 24-25 行的雙斜線,將程式上傳至晶片即完成
03設定篇 請改用新的電視數字鍵盤操作即可。