2014年11月8日 星期六

[ Wireless-RF@Arduino ] RGB LED 混色 - Arduino 的 Machester ( 曼徹斯特 ) 無線傳輸

本篇網頁中所使用到的零件可到下面商品網址訂購:


常常私底下被問到無線傳輸的一些問題,雖然不知道最後發問者是否解決了問題,但是總覺得是不是部落格關於無線模組的使用說明或是範例沒有能夠起到輔助的作用 !!!

從無線傳送端到無線接收端:
在傳送端的程式,先將所需要傳送的數據準備好;數據經過軟體計算之後產生編碼資料;編碼資料經過無線模組發送。
接收端的無線模組收到傳送端送來的資料之後,接收端開始解碼資料。解碼之後就是從傳送端送過來的未經編碼前的資料,這也是我們所要的。
ASK-433 或是 RF433MHz 兩個硬體雖然不同,但是因為硬體調制模式 ( Modulation Mode ) 是一樣的,所以傳送與接收端互換之後都還是可以正常接收資料的,而且只要資料編解碼是一致的,就能得到所需要的資料。



對比其他使用 UART 發送資料的無線模組,即使頻率 ( 433MHz ) 與調制方式相同,要使用其他無線模組傳送以及接收資料,就必須知道它的編解碼格式,要不然是無法與其溝通的,例如控制無線開關,或是使用 RF433 模擬 HT12E 編碼控制使用 HT12D 的電路,這些都是解出編碼之後再使用軟體模擬傳送控制碼到遠端。

所以總結來說:無線編碼很重要!好的無線編碼讓資料傳輸不容易被解碼,而且也能起到輔助軟體接收的功能。
Any ASK transmission method must first sent a capture signal of 01010........ When the receiver has adjusted its AGC ( Auto Gain Control ) to the required level for the transmission the actual data transmission can occur.
mchr3k, @ line 127 - 12
在接下來的內容,我將會使用兩片 Arduino 板展示如何使用既有的函式庫控制遠端 RGB LED 的顏色,而紅 (R) 綠 (G) 藍 (B) 三種顏色都可以使用傳送端的可變電阻做調整。

而在進入主題之前,先離題看一下我對於 Serial 與無線通訊間的測試分享 !

Note:如果您有購買賣場裡面的無線發射接收模組套件,網頁中的程式碼與函式庫已放在雲端硬碟中可自行下載 !

在網路 Google 一下關於無線發射模組的資料,VirtualWire、Hardware Serial SoftwareSerial 是最常用來使用在編解碼無線發射接收模組的函式庫。International Journal of Computer Science and Mobile Computing 期刊中 Vol. 3, Issue. 4, April 2014, pg.340 – 345 所刊載的一篇文章On New Approach in Using 433MHz Radio Modules 摘要說道:
Abstract— Cheap radio modules such as 433MHz Rx/Tx pairs are very popular in hobby projects and readily available in the local market. For reliable data transmission and reception these modules need initial burst training pulses for sync and some encoding scheme for reducing effect of noise. Present approaches make use of VirtualWire library and Manchester library for using these modules with Arduino board. This gives transmission of 3-4 digit sensor values and 5-12 characters text string. In present paper we demonstrate it is possible to send even longer text string and integer data without using these two libraries and using another protocol viz. software serial only. Transmission of long text strings is demonstrated with reliable results.
文中比較了 VirtualWire Manchester 函式庫的優缺點,最後選用 SoftwareSerial 函式庫重複傳送與接收一段 50 個英文字的訊息並且得到一個可靠的測試結果,而且不需要裝置對無線傳遞的訊號進行額外的錯誤偵測 ( Error Detetion ) 或校正 ( Correct ) 處理,聽起來的確很好 !

有些電子模組 ( ex. GPS 或 GSM ... 等 ) 是使用 UART 輸出所有的數據資料。若照文章中的結論,就可以直接將無線發射模組的 DATA 接腳接在電子模組的 TXD 接腳上,並且在另一端直接使用無線接收模組接收其資料就可以了。而程式設計師所要做的,就只是分析接收過來的資料,不需額外再去考慮其他解碼的問題,看起來可以減輕很多寫程式的時間 !!!

不過實際在文章中,只看到輸出的畫面,有點不真實!所以在進入到我們真正要談論的東西之前,要先來做一下實際的測試:
實際將 GPS 輸出的資料分別接上賣場兩款 433 無線發射模組發送無線資料,而在接收端使用 USB 轉 TTL線和撰寫 Arduino ( Hardware Serial SoftwareSerial 函式庫 ) 程式碼接收 GPS 無線資料,藉此來驗證期刊中所說的。
左邊:文中稱為 RF433;右邊:文中稱為 ASK433


無線串列通訊接收 GPS 資料測試:

上面期刊中的文章,只是簡單的提到使用一些簡單的無線發射接收模組搭配 Arduino SoftwareSerial 就可以可靠的接收來自 GPS 發送過來的無線資料且得到很好的結果,個人抱持懷疑的態度 !!!

沒有經過處理而直接傳送的無線訊號很容易被干擾,因此才會出現很多無線編碼的方式。不過這部分我不是專家!我只是提供實際測試之後的結果,幫助使用者在使用賣場的無線模組時要如何去選用以及應用。

測試分為下面三種方式:
  1. GPS 直接接無線發射模組,並使用 USB 轉 TTL 線接無線接收模組接收 GPS 資料。
  2. GPS 直接接無線發射模組,使用 SoftwareSerial 函式庫接收來自 GPS 的無線資料。
    使用測試裝置 3 和 4 交互測試
  3. GPS 直接接無線發射模組,使用 Hardware Serial 函式庫接收來自 GPS 的無線資料。
    使用測試裝置 3 和 4 交互測試
1. 無線發射與接收測試接線圖

2, 3. Arduino 無線接收端接線圖

ps. 接線圖中只有使用 RF433 做表示。實際測試時,發射端可以直接做更換,若是使用 ASK433 做為接收端時,CS 接腳要記得接地,這是與 RF433 接線不同的地方。


測試目的:
無線發射接收模組使用不同的測試方式,接收來自 GPS 的無線資料 !!!


測試條件:
  1. 發射與接收模組相距的距離:> 4 米
  2. 無線發射與接收模組都是使用 +5V 電源供應。
  3. GPS 必須在窗邊或是室外以接收衛星訊號。
  4. 無線發射與接收模組在測試時盡量不要移動,避免訊號遺失。
  5. Arduino IDE 1.5.8 ( 已內建 SoftwareSerial 函式庫 )

測試裝置:
1.)  2  x  pcs  [ USB 轉 TTL 線 ]
2.)  1  x  pcs  [ GPS module with UART(TTL) output ]
3.)  1  x  set  [ RF433MHz 無線發射接收模組 ]
4.)  1  x  set  [ ASK-433MHz 無線發射接收模組 ]


測試結果:

下面測試中,無線發射端的接線都是相同的:使用 USB 轉 TTL 線接 GPS,GPS TXD 再與無線發射模組 DATA 連接。

接收端:USB 轉 TTL 線接無線接收模組
TXRF433ASK433RF433ASK433
RXRF433ASK433ASK433RF433
RESULT可接受(1)亂碼(2)亂碼(3)50%亂碼(4)
Note: 先插上 USB 轉 TTL 線之後再插上無線發射或是接收模組,不然系統驅動會失敗!!!

接收端測試程式碼:
 1 #include <SoftwareSerial.h>
 2 
 3 #define _txpin 2
 4 #define _rdpin 3
 5 
 6 // Configure the Library in UART Mode
 7 SoftwareSerial _recvrf433( _rxpin, _txpin );
 8 
 9 void setup(){
10  Serial.begin(9600);
11  _recvrf433.begin(9600);
12 }
13 
14 void loop(){
15  if( _recvrf433.available() )
16  {
17   Serial.write( _recvrf433.read() );
18  }
19 }

(1)
(2)
(3)
(4)


接收端:Arduino 與無線接收模組連接,使用 SoftwareSerial 函式庫接收資料
TXRF433ASK433RF433ASK433
RXRF433ASK433ASK433RF433
RESULT(a)可接受(b)亂碼(c)亂碼(d)50%亂碼

接收端測試程式碼:
 1 void setup() {
 2  Serial.begin(9600);
 3 }
 4 
 5 void loop() {
 6  if( Serial.available() )
 7  {
 8   Serial.write( Serial.read() );
 9  }
10 }

(a)
(b)
(c)
(d)


接收端:Arduino 與無線接收模組連接,使用 Hardware Serial 函式庫接收資料
TXRF433ASK433RF433ASK433
RXRF433ASK433ASK433RF433
RESULT可接受亂碼亂碼50%亂碼


結論:

看到這測試出來的結果,應該就可以知道為什麼不建議使用 Serial 的方式傳送與接收無線數據,因為在接收端容易受到雜訊的干擾!

翻到之前的 "[ wireless-RF ] Arduino 之間或與 Raspberry Pi 之間的 RF 433MHz 通訊" 這篇網頁仔細想想 ! 當初使用 VirtualWire 函式庫,重複傳送 "hello!" 字串來測試賣場裡的無線發射模組在不同樓層間的傳輸能力,接收端一直是處在開電的情況之下,而在上下樓層時,發射端電源是關閉直到找到電源插座才開始發送訊號,而此同時發射端並不會莫名收到一些亂碼 !

可是在上面的三個測試中,使用 Serial 方式接收無線訊號非常容易收到莫名的訊號,除了在條件 (1) 和 (a) 發射端開啟時會好很多,其餘的時候都是一直接收到亂碼。

另外一個問題是,在條件 (1) 和 (a) 測試時,接收端與發射端的位置必須固定,若雙方有移動的情形發生,接收端就會發生資料收不到或是亂碼的情況出現。所以在期刊這篇文章中的結論部分所說的,的確 ! SoftwareSerial 函式庫可以用來傳送長字串,但是是在理想的條件下!實際使用 GPS 來傳送無線資料測試也發現,並不是所有的 433MHz 無線模組都適用。而且容易在接收的過程中收到亂碼,去除這些亂碼也必須額外撰寫程式 !!!

上面這部分的測試,看看知道就好!實際使用上,為了更可靠的數據傳輸、增加抗雜訊與干擾的影響,應該選擇一個適用於現有項目上專用在無線通訊的函式庫。


VirtaulWire 與 Manchester 函示庫比較:

下面針對之前介紹過的 VirtualWire 函式庫,以及用在開頭展示影片中的 Manchester 函式庫簡單比較一下其優缺點

VirtualWire library:
  • VirtualWire 函式庫對於時鐘頻率 ( clock frequency ) 與環境溫度非常敏感,所以會造成資料丟失的問題。
  • 只能傳送非常短的字串與整數資料,這也是此函式庫的限制。

Manchester library:

Manchester ( 曼徹斯特 ) 碼的編解碼方式:WiKi
簡單的說,就是將 Clock 與 Data 做 XOR 計算,而得出傳送的字元 ( bit ) 是 1還是 0。
  • 使用這函式庫的最大優點,就是即使在有雜訊的條件下也能可靠的傳遞。
  • 另外一優點,也是我選用來做網頁中範例的原因:可以指定傳輸 ID。意謂著可以同時由不同的發射端傳送資料,但是只需要一個接收端接收資料。
  • 上面的優點也是缺點,現有的 Manchester 函式庫最多只能接收到 16 個不同 ID,也就是 16 個不同無線發射端。
  • 缺點與  VirtualWire 一樣,傳送的資料量少,只能傳送數字資料,不適合傳送長字串。
利用 Manchester 函式庫可以很容易的根據不同的無線發射端或是感測器類型,給予不同的 ID作為接收端處理的辨識。


RGB LED 接線電路圖:

RGB LED 遠端遙控接線電路圖
實際接線圖 ( 左邊:接收電路;右邊:發射電路 )

手邊剛好各有一片 Ardino Duemilanoce 以及 Arduino UNO 板子,兩片板子分別做為無線發射端 ( 左邊 ) 以及無線接收端 ( 右邊 )。

發射端需要三個 10 K 的可變電阻以及一片無線發射模組。由於 RF433 與 ASK433 的無線發射模組兩片的接腳代號都相同,因此可直接更換使用。

接收端需要一顆共陰級的 RGB LED ( 或三顆不同顏色的 LED ) 以及三根 220 歐姆的電阻分別接到 D5、D6 和 D9 這三根具有 PWM 輸出功能的接腳上,作為調亮度之用。由於 RF433 與 ASK433 其中的一根接腳不同,因此若是要做更換為 ASK433 無線接收模組,請就虛線部分接線。


RGB LED 發射與接收端程式碼:

發射端主要是負責處理 RGB LED 亮度值的傳送。亮度值由可變電阻調整並轉換為 0 - 255 之間的值 ( line 34 - 36 ),R、G、B 三個值分別對應到指定的 ID 號碼 1、2、3 ( line 23 ) 並先經過編碼再經 Manchester 編碼傳送出去 ( line 43 );為了不頻繁傳送變化值,因此設定了每個顏色前後變化之間必須超過誤差值 ( line 22 ) 才會被傳送 ( line 41 )。

Manchester 函式庫傳送與接收的接腳可以單獨設定,並同時可以設定傳輸的速度 ( line 27 ),由於在 Manchester.h 已預先宣告一個全局的實體 man,所以可以直接調用。

調用傳送的函數之前 ( man.transmit() ),我需要對 R、G 和 B 的亮度值加入 ID 的編碼 ( man.encodeMessage( ID, data ) ) 再傳送出去。如果想知道哪一個亮度值被傳送出去,可以把 line 11 前面的雙斜線取消掉,line 45 - 47 的程式就會被啟動。

Manchester_rgbled_tx.ino, line 8 - 51 
 8 
 9 #include <Manchester.h>
10 
11 #define DEBUG
12 
13 #define _txpin   2
14 #define _ledpin   13
15 #define _potpinred   A0 // A0
16 #define _potpingrn   A1 // A1
17 #define _potpinblu   A2 // A2
18 
19 uint8_t bufidx = 0;
20 uint8_t _previousColor[3]  = {0};
21 uint8_t _currentColor[3]  = {0};
22 const uint8_t _tolcolor[3] = { 5, 5, 5 };
23 const uint8_t _colorid[3] = { 1, 2, 3 };
24 
25 void setup() {
26  Serial.begin( 9600 );
27  man.setupTransmit( _txpin, MAN_1200 );
28 
29  pinMode( _ledpin, OUTPUT );
30 }
31 
32 void loop() {
33 
34  _currentColor[0] = ( 255 - map(analogRead(_potpinred), 0, 1024, 0, 255) );// RED
35  _currentColor[1] = ( 255 - map(analogRead(_potpingrn), 0, 1024, 0, 255) );// GREEN
36  _currentColor[2] = ( 255 - map(analogRead(_potpinblu), 0, 1024, 0, 255) ); // BLUE
37 
38  for( int i = 0; i < 3; i++ )
39  {
40    if( abs(_currentColor[i] - _previousColor[i]) > _tolcolor[i] )
41    {
42      _previousColor[i] = _currentColor[i];
43      man.transmit(man.encodeMessage(_colorid[i], _currentColor[i]) );
44      #ifdef DEBUG
45        char buf[25];
46        sprintf(buf, "Update id%2d=%3d", _colorid[i], _currentColor[i]);
47        Serial.println(buf);
48      #endif
49    }
50  }
51 }


接收端的程式負責解碼接收到的資料,並將資料解碼取出 iddata ( line 47 )。程式設計師可以根據取出的 id 號碼,輸出 data 到 RGB LED 連接的接腳上改變亮度 (  line 50 - 70 )。

Manchester 函式庫傳送與接收的接腳可以單獨設定,並同時可以設定傳輸的速度 ( line 23 ),由於在 Manchester.h 已預先宣告一個全局的實體 man,所以可以直接調用。要接收資料之前,必須先在 setup() 函式中下開始接收的指令 ( line 24 ),然後在 loop() 函式裡使用下面的程式碼作為樣板,在 "處理接收到的資料" 處輸入處理的程式碼 ( 也就是 Manchester_rgbled_tx.inoline 48 - 76 )。

 if (man.receiveComplete()) { // 已經接收到一些東西
  uint16_t m = man.getMessage();
  man.beginReceive(); // 在接收資料之後,開始下一輪的訊息接收
  if (man.decodeMessage(m, id, data) ) {
   // 處理接收到的資料
  }
 }

line 47 的判斷是將接收到的資料解碼,分離出 id 與所夾帶的 data。根據 id 就可以知道要改變 RGB 哪一個部分的亮度 ( line 50 - 70 ) ,而且當正確接收到資料時會開啟 Arduino UNO 板上的 D13 LED ( line 48 ),亮度改變後關閉 D13 LED ( line 74 )。

如果需要接收拾得更詳細的資料,就將 line 11 前面的雙斜線取消掉,程式執行解碼動作時就會輸出解碼的資料。

line 43line 77 兩個 delay 是必須的,但是可以斟酌調整裡面的數字,但請注意 ! 不當的調整會造成 Arduino 當掉,當掉的情形就是無法正常接收資料。

Manchester_rgbled_tx.ino, line 8 - 78 
 8 
 9 #include <Manchester.h>
10 
11 #define DEBUG
12 
13 #define _rxpin  3
14 
15 #define _ledred  5
16 #define _ledgrn  9
17 #define _ledblu  6
18 
19 #define _ledpin  13
20 
21 void setup() {
22     Serial.begin(9600);
23  man.setupReceive( _rxpin, MAN_1200 );
24    man.beginReceive();
25 
26    pinMode( _ledred, OUTPUT );
27    pinMode( _ledgrn, OUTPUT );
28    pinMode( _ledblu, OUTPUT );
29    pinMode( _ledpin, OUTPUT );
30    digitalWrite( _ledred, LOW );
31    digitalWrite( _ledgrn, LOW );
32    digitalWrite( _ledblu, LOW );
33    digitalWrite( _ledpin, LOW );
34 }
35 
36 uint8_t bufidx = 0;
37 uint8_t data;
38 uint8_t id;
39 char buf[25];
40 
41 void loop() {
42  delay(10);
43 
44  if (man.receiveComplete()) { //received something
45    uint16_t m = man.getMessage();
46    man.beginReceive(); //start listening for next message right after you retrieve the message
47    if (man.decodeMessage(m, id, data) ) {
48      digitalWrite( _ledpin, HIGH );
49      if( data < 5 ) data = 0;
50      switch(id)
51      {
52        case 1: // RED LED
53          analogWrite( _ledred, data );
54          #ifdef DEBUG
55            sprintf( buf, "%2d, red color=%3d", id, data );
56          #endif
57        break;
58        case 2: // GREEN LED
59          analogWrite( _ledgrn, data );
60          #ifdef DEBUG
61            sprintf( buf, "%2d, green color=%3d", id, data );
62          #endif
63        break;
64        case 3: // BLUE LED
65          analogWrite( _ledblu, data );
66          #ifdef DEBUG
67            sprintf( buf, "%2d, blue color=%3d", id, data );
68          #endif
69        break;
70      }
71      #ifdef DEBUG
72        Serial.println( buf );
73      #endif
74      digitalWrite( _ledpin, LOW );
75      }
76    }
77    delay(100);
78 }

發射端與接收端程式執行時的輸出如下圖所示,左邊是發射端,右邊是接收端。
無線發射端與接收端的輸出數據

測試結果:

下面的影片是實際使用 RF433 測試的情形,若是更換為 ASK433 做測試,則傳送與接收的距離更遠且效果更好,手邊有的可以試試 !


實際操作的時候,因為怕數據太頻繁發送給接收端,因此必須前後值差在 5 才會輸出,所以輸出的值會因為讀取時的時間差可能會傳送到非調整的值 ( 而是讀取的值 ),所以在調整 LED 燈滅掉的時候會需要多調整幾次 !


結論:

這篇文章其實是因為需要一個可以用在 ATtiny45 的無線通訊的 Arduino 函式庫,因此因緣際會下找到並測試過可以用才寫了這篇網頁。VirtualWire 函式庫在使用上有一些限制,因此並不適用在我另一個要做的項目上。在試用過 Machester 函式庫之後發現到,雖然 id 數目只有 16 個,但卻可以使用在多個小型無線發射裝置上,而接收端卻只需要一個,這對我來說很方便 ! 而實際應用的例子也經由上面的例子證實可行。

Machester 函式庫的程式碼不多,因此若對於此函示庫有興趣的話,可以對編碼部分的程式碼進行一些修改,讓 id 部分可以由 4-bit 變成 8-bit ( 甚至更多 ),那 id 的數目就會增加而且一次也可夾帶更多的資料,有時間或有興趣的可以動手改看看 !!!

就到這裡了,更多的應用就看各位了 !!!



<< 部落格相關文章 >>

沒有留言:

張貼留言

留言屬名為"Unknown"或"不明"的用戶,大多這樣的留言都會直接被刪除掉,不會得到任何回覆!

發問問題,請描述清楚你(妳)的問題,別人回答前不會想去 "猜" 問題是什麼?

不知道怎麼發問,請看 [公告] 部落格提問須知 - 如何問問題 !