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 則留言:

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

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

    回覆刪除
  3. 您好,

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

    麻煩您了,謝謝

    回覆刪除
  4. 您好,

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

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

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

    回覆刪除
  5. 你是說 void showIRProtocol(decode_results *results) 裏的 decode_results *results 這行是什麼意思嗎?

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

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

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

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

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

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

    回覆刪除
  7. Coopermaa你好~~

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

    麻煩您回復了 謝謝~

    回覆刪除
  8. 你好,

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

    回覆刪除
  9. 謝謝!!! 原來是沒有顯示~~~:))

    回覆刪除
  10. 不好意思我想請問一下
    我的遙控器最一開始接收的訊號是:
    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);
    }
    這樣也無法測試...

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

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

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

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

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

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


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

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

    我有重新測試了你說的加上"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個不一樣的值了??!

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

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

    回覆刪除
  14. 不好意思捏...恕我愚笨...是先用你網站上面的程式碼先測試的...但就有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)
    則會出現我之前說的那三個而已!!

    回覆刪除
  15. 試把 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^

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

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

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

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

    以上...過來人XDDD


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

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

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

    回覆刪除
  18. @Sven Wang
    有你的分享,蓬蓽生輝,感謝!

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

    回覆刪除
  19. TO Sven Wang, Coopermaa:
    恩恩!!! 我明白了!!!
    真的很謝謝你們!!! :))

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

    真的很謝謝你們喔~

    回覆刪除
  20. 你發紅外線發射器接在哪一支腳位?
    可以貼一下接線照片嗎?

    回覆刪除
  21. 可以可以!!!
    就是...怎麼把照片PO上來啊??!

    回覆刪除
  22. 放到 dropbox/public 資料夾
    然後再貼上檔案連結就行啦~~

    回覆刪除
  23. 作者已經移除這則留言。

    回覆刪除
  24. 不好意思...現在才回

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

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

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

    回覆刪除

注意:只有此網誌的成員可以留言。