2011年3月30日 星期三

1.2 顯示紅外線協定種類

實驗目的

練習用 IRRemote 函式庫接收紅外線訊號。在這個實驗裏,你將利用 decode_results 結構來判斷並顯示遙控器的紅外線協定種類,如 NEC, Sony SIRC, Philips RC5 和 Philips RC6 等紅外線協定(Infrared Protocol)。

前提

在做這個實驗前,我假設:

  1. 你已經看過「IRRemote 紅外線遙控教學」一文,而且
  2. 也已經把 IRRemote 函式庫安裝起來了
材料
接線
  • 照下表把紅外線接收器跟 Arduino 連接起來:
紅外線接收器接腳 Arduino 接腳
GND(-) 接到 GND
Vcc(+ 或 V+) 接到 +5V 電源
Vout(或 OUT) 接到 pin 2。你可以接到其它 Digital pin,但程式要配合修改

image_thumb2[3] 
▲ 有些紅外線接收器 GND 和 Vcc 是相反的,接線時請查閱你的 datasheet

decode_results 結構

從前一篇中,你已經知道 decode_results 是用來存放紅外線訊號解碼結果的結構。decode_results 結構它的定義如下:

下表是 decode_results 結構各個欄位的說明:

decode_results 欄位名稱 說明
decode_type 協定種類,包括 NEC, SONY, RC5, RC6, DISH 以及 SHARP 等紅外線協定,若解析不出則為 UNKNOWN。
value 解到的編碼數值
bits 編碼的位元數(即資料長度)
rawbuf 紅外線訊號原始波形資料
rawlen rawbuf 的記錄總筆數

我們已經看過 value 和 bits 欄位。rawbuf, rawlen 是紅外線訊號的原始波形資料與記錄總筆數,這兩個欄位是比較深入的東西,一般而言,這兩個欄位的機會非常少,所以目前你可以先暫時忽略不管。

這次實驗的目的是判斷並顯示紅外線協定的種類,因此程式將會用到 decode_type 欄位。

程式

底下的程式(Example1.2.pde)示範如何用 decode_type 欄位來判斷並顯示紅外線協定種類:

程式碼沒有太大的變動,只是增加一個 showIRProtocol() 函式來顯示紅外線協定的種類而已,相信你一定看得懂。

示範照片

隨手拿起一個電視遙控器按幾個鈕測試:

image_thumb1
▲ 接受紅外線訊號

打開 Serial Monitor 觀察解碼結果:

 image
▲ 顯示解碼結果以及遙控器所用的紅外線協定

延伸閱讀

25 意見:

Kai 提到...

上述那些紅外線接收器不知上哪兒買, 小弟用紅外線接收LED 也是一樣效果.

coopermaa 提到...

我是在光華商場買的,我用的那款是 FM-8038TM2
其它的我沒用過,也許你可以到 mouser 或露天拍賣找一下
ex:
這個, 這個, 還有這個

chenanco 提到...

您好,

我想請問 decode_results *results 這行是什麼意思
另外, results->value 是不是等於 results.value ?

麻煩您了,謝謝

chenanco 提到...

您好,

再請問 volatile unsigned int *rawbuf 這行程式中
開頭的volatile有什麼意思?以及*rawbuf為何要加上"*"

再一個問題,const int irReceiverPin = 2 這行當中,
const是什麼意思?

問了這麼多真不好意思,麻煩您了,謝謝

coopermaa 提到...

你是說 void showIRProtocol(decode_results *results) 裏的 decode_results *results 這行是什麼意思嗎?

Mm.. 這行是宣告 decode_results 型別的變數,變數名稱叫做 results,而因為 results 變數前面有一個星號,所以 results 它是一個指標變數。

results->value; // 這個是用指標存取結構欄位 (field) 的寫法
results.value; // 這個則是一般變數存取結構欄位 (field) 的寫法

coopermaa 提到...

volatile 這個不好用文字解釋呢,請你先看一下這篇教學:
http://blog.csdn.net/c_bg44/article/details/1538235
簡單來說,volatile 是告訴編譯器不要對某個變數的存取做程式碼最佳化,每當程式存取某個變數時,就老老實實的記憶體裏存取,而非利用 CPU 暫存器,避免產生狀態不一致的情形。

rawbuf 前面加上星號,代表它是一個指標
也就是說,真正用來存放 IR 原始資料的 buffer 是在另一個地方,所以這邊才會使用指標。

const 是常數的意思,所以 const int irReceiverPin = 2 這行便是宣告一個整數常數,整數常數叫做 irReceiverPin,然後它的數值是 2。因為是用 const 宣告成常數,所以之後程式不能改變 irReceiverPin 的內容。

不會啊,對我來說,算是溫故知新啊。:)

Sven Wang 提到...

Coopermaa你好~~

請問7位數字跟32位元是否有關係呢?
為什麼不是8位數字代表32位元呢?
我的想法是16^8=2^32
但為什麼你顯示的是七位數字呢?

麻煩您回復了 謝謝~

coopermaa 提到...

你好,

你是說為什麼 "4FB48B7" 這是 32 位元的數值嗎? 是這樣的, "4FB48B7" 是 16 進位 hex code,每一位數字是 4 個位元,"4FB48B7" 總共有 7 位,所以是 7 * 4 = 28 位元,不過,其實還有第一位數,只是因為第一位是剛好是 0,所以 Serial.println() 沒把 0 顯示出來,事實上還是 32 位元的

Sven Wang 提到...

謝謝!!! 原來是沒有顯示~~~:))

Emily Ke 提到...

不好意思我想請問一下
我的遙控器最一開始接收的訊號是:
Protocol: NEC, irCode: 20DF10EF, bits: 32
Protocol: NEC, irCode: FFFFFFFF, bits: 0
Protocol: NEC, irCode: FFFFFFFF, bits: 0

所以我在測試發射紅外線的訊號這邊是打:
if (buttonState == HIGH) {
// 發射紅外線訊號
irsend.sendNEC(0x20DF10EF, 32);
}
但是這樣測試開關卻測試不出來!

那我又打說:
if (buttonState == HIGH) {
// 發射紅外線訊號
irsend.sendNEC(0x20DF10EF, 32);
irsend.sendNEC(0xFFFFFFFF, 0);
}
這樣也無法測試...

我想請問一下,這樣打是錯在哪?!應該怎麼修正比較好?!

coopermaa 提到...

if (buttonState == HIGH) {
// 發射紅外線訊號
irsend.sendNEC(0x20DF10EF, 32);
}

這樣寫應該沒錯。你測試的是什麼開關?

也許同一個按鍵可以多解碼幾次,看看解到的碼是不是每次都一樣。

BTW, irCode 為 FFFFFFFF 是 NEC Protocol 的 repeat code,你可以加個判斷式把它過瀘掉,像這樣:

if (irrecv.decode(&results)) { // 解碼成功,收到一組紅外線訊號
if (results.value != -1) {
// 印到 Serial port
    ...
}
}


Emily Ke 提到...

恩恩~真的很謝謝你!!!
但是不好意思...想再請問一下

我的是電視遙控器,算是那種通用的電視遙控器
而我測的是開電視開關的那個按鈕

我有重新測試了你說的加上"if (results.value != -1)",但是這樣似乎反而測不出是NEC的遙控器,他的輸出是:
Protocol: Unknown encoding, irCode: EAB7BACD, bits: 32
Protocol: Unknown encoding, irCode: B853798A, bits: 32
Protocol: Unknown encoding, irCode: EDB7BF84, bits: 32
Protocol: Unknown encoding, irCode: 97307A58, bits: 32
反而有四種不一的訊號了...

而...若是設定不要=0的訊號,為什麼我打
if (irrecv.decode(&results) && results.value != 0)
這個出來的結果和原本的沒甚麼改變!
但若是更改為你說的那個
if (results.value != -1)後,反而會沒有0的值?!但是蹦出4個不一樣的值了??!

不好意思...我不大明白,提出了這麼多的疑問...

coopermaa 提到...

同一顆 Power 鈕反而解出四種不一樣的訊號? 怪~~
可以貼一下你完整的 code 嗎?

Emily Ke 提到...

不好意思捏...恕我愚笨...是先用你網站上面的程式碼先測試的...但就有bug了!

所以是這樣的:
#include

const int irReceiverPin = 2;
IRrecv irrecv(irReceiverPin);
decode_results results;

void setup()
{
Serial.begin(9600);
irrecv.enableIRIn(); }

void showIRProtocol(decode_results *results)
{
Serial.print("Protocol: ");

switch(results->decode_type) {
case NEC:
Serial.print("NEC");
break;
case SONY:
Serial.print("SONY");
break;
case RC5:
Serial.print("RC5");
break;
case RC6:
Serial.print("RC6");
break;
default:
Serial.print("Unknown encoding");
}

Serial.print(", irCode: ");
Serial.print(results->value, HEX);
Serial.print(", bits: ");
Serial.println(results->bits);
}

void loop()
{
if (irrecv.decode(&results)) {
if (results.value != -1) {
showIRProtocol(&results);
irrecv.resume();
}
}
}
這個是一開始測試接收紅外線的種類的!!!

但測出來就有不同的四個了!
若是沒有加if (results.value != -1)
則會出現我之前說的那三個而已!!

coopermaa 提到...

試把 loop 函式:

void loop()
{
if (irrecv.decode(&results)) {
if (results.value != -1) {
showIRProtocol(&results);
irrecv.resume();
}
}
}

改成這樣:

void loop()
{
if (irrecv.decode(&results)) {
if (results.value != -1) {
showIRProtocol(&results);
}
irrecv.resume(); // 搬到外面來
}
}

也許會撥雲見日喔!^o^

Sven Wang 提到...

回應Emily:
先前我也使用Cooper的紅外線接收程式接收電視遙控器發射出來的訊號, 也會有亂碼的問題.

經過我多方測試,"只要直接對著紅外線接受器發射紅外線"就可避免亂碼的產生.

我歸納如下,如果是自己做紅外線發射器的話,通常會有兩個原因產生亂碼:
1.紅外線發射電流不足,導致訊號不清晰==>加入電晶體放大電流就可以改善問題
2.各種紅外線LED燈規格不同,能夠通過的peak電流也不同,發射角度也不同,多少都會影響,所以建議配置能通過大電流的紅外線LED燈,並且對著接受器直接發射,減少亂碼的產生.

最後...如果還是配不出電晶體跟找不到適當的紅外線LED, 就直接解焊電視遙控器上面的燈還有電晶體跟周邊電阻,量測這些規格在進而製作自己的版本吧!!

以上...過來人XDDD


建議先把接收端的程式跟電路都弄好,然後用電視遙控器測試ok後,再進行紅外線發射器的製作這樣可以釐清是硬體問題還是程式問題~~ :))

Sven Wang 提到...

BTW,有個重點~~
先前測試的時候通常設定電晶體跟紅外線LED燈的工作點都不會設置在他們的continuous(連續輸出)範圍
因為continuous範圍的電流限制都太小了
其實紅外線發射也不是一直持續大電流的供應
所以建立在高電流的工作區是沒問題的
也就是說盡量操爆紅外線LED燈跟電晶體
要不然無法發揮他們的效能!!

但還是先建議在continuous的電流限制下先做測試,如果接受狀況不好,在進行加大電流測試八!!

coopermaa 提到...

@Sven Wang
有你的分享,蓬蓽生輝,感謝!

這兩天我又回過頭玩了一下紅外線 (相關記錄可參考 http://bit.ly/PKw6MS)
在做紅外線解碼的時候,發現燈光也會影響解碼
我在房間裏測試,好幾次只是手或身體動了一下擋到光線,結果接收器就有反應,解到一個 Unknown 的碼....
不知道是不是燈光顏色的關係,因為我房間燈光比較偏黃光,感覺好像比較容易讓紅外線接收器產生反應

Emily Ke 提到...

TO Sven Wang, Coopermaa:
恩恩!!! 我明白了!!!
真的很謝謝你們!!! :))

那麼我現在程式碼他的訊號我有照你們說的,而沒有產生亂碼了!
但就是接了線路想發射紅外線,用arduino板子開電視...反而不行,也有可能是因為電流的問題嗎?!
是不是我同樣的加上電晶體就會改善了?!

真的很謝謝你們喔~

coopermaa 提到...

你發紅外線發射器接在哪一支腳位?
可以貼一下接線照片嗎?

Emily Ke 提到...

可以可以!!!
就是...怎麼把照片PO上來啊??!

coopermaa 提到...

放到 dropbox/public 資料夾
然後再貼上檔案連結就行啦~~

Emily Ke 提到...
作者已經移除這則留言。
Emily Ke 提到...

不好意思...現在才回

https://www.dropbox.com/s/jax54nfbd745mkk/IMAG1092.jpg?m
https://www.dropbox.com/s/qxqcbflaicdpel9/IMAG1093.jpg?m

2張圖都是一樣的!!!
請問這樣你看的到嗎?!

coopermaa 提到...

這太考驗我的眼力啦!
發射器是接 pin 3 還是 pin 2?
另外你的程式是只有處理發射功能,還是有發射同時有接收功能呢?