2017年10月23日 星期一

Arduino 433M 超再生無線模組測試

做完 nRF24L01 無線模組測試後, 我在零件箱裡又找出之前向露天賣家 XLAN 買的 433MHz 超再生無線收發模組, 也是一樣來沒測試過能不能用, 參考 :

#  [X-LAN] Arduino RF 433M 超再生模組 發射接收一對 $45
露天 XLAN 電子零件購買清單

剛好上週五向市圖借的 "Arduino 自造指南 (碁峰, 曾繁勛等譯)" 書已拿到, 其中的專案 47 "建構無線遠端控制" 就是使用這種無線射頻連結模組 (SparkFun 零件編號 wrl-10534 的 RF Link 模組). 今天就趁著上班前的空檔測試看看. 以下的測試還參考了下面這本書 :

# Arduino 互動設計專題與實戰 (碁峰, 柯博文)

此模組有分收發不同板子如下所示,  :




這種 RF Link 模組沒有錯誤檢查功能, 無法確認送出的資料是否正確接收. 注意, 接收板中間的兩個針腳是相同的 Data 腳, 接哪一個都可以. 另外, 兩塊板子上都有一個 ANT 天線孔, 可以用單心線繞原子筆捲成一個 2cm 的彈簧天線, 一端焊在 ANT 孔上才能讓傳送距離增加, 參考商品說明 :

 "使用前,發射與接收端都要接上天線,以達到最佳接收效果; 天線可以用單心線捲成螺旋狀,長度約2公分即可。"

沒有天線的話傳遞距離大約只有 40~50 公分而已, 加上天線空曠處可達 40 公尺 (書上說可達 100 公尺), 參考其規格 :

工作電壓(V):DC 5V
傳輸速率:<10KB/S (一般 4KB/S)
發射功率:25mW
工作電流:4mA
調製方式:調幅
工作溫度: -10 ℃ ~ 70 ℃
接收靈敏度(dBm):-105DB
工作頻率(MHz):433.92MHz
實測距離可以達40公尺(視實際環境有無干擾而定)


使用此模組傳遞資料須使用 VirtualWire 函式庫, 可從下列網址下載最新的 1.27 版, 將 ZIP 檔解壓縮後放在 Arduino 安裝目錄的 libraries 子附錄下即可 :

http://www.airspayce.com/mikem/arduino/VirtualWire/VirtualWire-1.27.zip

VirtualWire 函式庫各函式用法說明可參考 :

https://www.pjrc.com/teensy/td_libs_VirtualWire.html


在 "Arduino 自造指南" 專案 47 中, 傳送板與接收板的 Data 腳都接到 Arduino 的 D8 腳, 傳送板 Arduino 的 D2, D3 各接上一個按鈕開關與上拉電阻; 接收板 Arduino 的 D6, D7 各接一組 LED 與 560 歐姆電阻串聯電路以顯示收到的按鈕狀態訊號. 專案 47 的範例程式如下 :

傳送端程式 :

#include <VirtualWire.h>

uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen=VW_MAX_MESSAGE_LEN;

const char *on2="a";
const char *off2="b";
const char *on3="c";
const char *off3="d";

void setup() {
  Serial.begin(9600);
  vw_set_ptt_inverted(true);
  vw_setup(300);
  vw_set_tx_pin(8);
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  }

void loop() {
  if (digitalRead(2)==HIGH) {
    vw_send((uint8_t *)on2, strlen(on2));
    vw_wait_tx();
    Serial.println("a");
    delay(200);
    }
  if (digitalRead(2)==LOW) {
    vw_send((uint8_t *)off2, strlen(off2));
    vw_wait_tx();
    Serial.println("b");
    delay(200);
    }
  if (digitalRead(3)==HIGH) {
    vw_send((uint8_t *)on3, strlen(on3));
    vw_wait_tx();
    Serial.println("c");
    delay(200);
    }
  if (digitalRead(3)==LOW) {
    vw_send((uint8_t *)off3, strlen(off3));
    vw_wait_tx();
    Serial.println("d");
    delay(200);
    } 
  }

接收端程式 :

#include <VirtualWire.h> 4 6

uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen=VW_MAX_MESSAGE_LEN;

const char *on2="a";
const char *off2="b";
const char *on3="c";
const char *off3="d";

void setup() {
  Serial.begin(9600);
  vw_set_ptt_inverted(true);
  vw_setup(300);
  vw_set_rx_pin(8);
  vw_rx_start();
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  }

void loop() {
  if (vw_get_message(buf, &buflen)) {
    switch(buf[0]) {
      case 'a':
        digitalWrite(6, HIGH);
        Serial.println("a");
      break;
      case 'b':
        digitalWrite(6, LOW);     
        Serial.println("b");
      break;
      case 'c':
        digitalWrite(7, HIGH);
        Serial.println("c");
      break;
      case 'd':
        digitalWrite(7, LOW);
        Serial.println("d");
      break;
      }
    }
  }

但是我沒有使用上面這組程式來進行測試, 因為我覺得它的範例比較複雜, Arduino 的 DIO 腳本身就有內建上拉電阻了, 不需要外加. 我的測試方法很簡單, 就是在接收端用兩個全域變數紀錄 LED 現在的狀態, 當接收到傳送的送來的信號時就將狀態反轉, 這樣方便一個人做距離測試時檢查狀態是否有改變 (增加測試距離時記住原先狀態, 按下按鈕後再跑過去檢查). 我將其改編為如下的測試 1 :


測試 1 : 接收端 LED 顯示傳送端按鈕的 toggle 狀態

傳送板程式 :

#include <VirtualWire.h>

const char *sendA="A";   //按鈕 A 要傳送的資訊
const char *sendB="B";   //按鈕 B 要傳送的資訊
int buttonA=2;
int buttonB=3;
int TXpin=8;   //傳送板 Data 腳接 Arduino D8 腳

void setup() {
  Serial.begin(9600);
  vw_set_ptt_inverted(true);  //設定RF Link 模組 "push to talk" 極性
  vw_setup(300);   //設定速率 300bps
  vw_set_tx_pin(TXpin);   //設定 Data 傳送腳 (D8)
  pinMode(buttonA, INPUT_PULLUP);    //開啟上拉電阻
  pinMode(buttonB, INPUT_PULLUP);    //開啟上拉電阻
  }

void loop() {
  if (digitalRead(buttonA)==LOW) {   //若按鈕 B 被按下
    vw_send((uint8_t *) sendA, strlen(sendA));    //傳送字元 'A' 給接收板
    vw_wait_tx();
    Serial.println("Send 'A'");
    delay(200);
    }
  if (digitalRead(buttonB)==LOW) {    //傳送字元 'B' 給接收板
    vw_send((uint8_t *) sendB, strlen(sendB));
    vw_wait_tx();
    Serial.println("Send 'B'");
    delay(200);
    } 
  }


接收板程式 :


#include <VirtualWire.h> 4 6

uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen=VW_MAX_MESSAGE_LEN;

boolean LEDA_ON=false;  //LED 預設熄滅
boolean LEDB_ON=false;  //LED 預設熄滅
int LEDA=6;
int LEDB=7;
int RXpin=8;   //接收板 Data 腳接 Arduino D8 腳

void setup() {
  Serial.begin(9600);
  vw_set_ptt_inverted(true);  //設定RF Link 模組 "push to talk" 極性
  vw_setup(300);  //設定速率 300bps (須與傳送端一致)
  vw_set_rx_pin(RXpin);   //設定接收板接 Arduino 哪一隻腳
  vw_rx_start();  //RF Link 模組開始接收資料
  pinMode(LEDA, OUTPUT);
  pinMode(LEDB, OUTPUT);
  Serial.println("Receiving...");
  }

void loop() {
  if (vw_get_message(buf, &buflen)) {
    if (buf[0]=='A') {
      Serial.println("Receive 'A'");
      LEDA_ON = !LEDA_ON;
      if (LEDA_ON) {digitalWrite(LEDA, HIGH);}  //狀態為 ON 就點亮 LED
      else {digitalWrite(LEDA, LOW);}  //狀態為 OFF 就點亮 LED
      }
    if (buf[0]=='B') {
      Serial.println("Receive 'B'");
      LEDB_ON = !LEDB_ON;
      if (LEDB_ON) {digitalWrite(LEDB, HIGH);}  //狀態為 ON 就點亮 LED
      else {digitalWrite(LEDB, LOW);}  //狀態為 OFF 就點亮 LED
      }
    }
  }




可見按鈕按下時, 對應的遠端 LED 就會交替明滅 (雖然這個遠端只有 50 公分不到).

傳送端序列埠監控視窗輸出訊息如下 :

Send 'A'
Send 'B'
Send 'A'
Send 'B'
Send 'B'
Send 'B'
Send 'A'
Send 'A'

接收端序列埠監控視窗輸出訊息如下 :

Receiving...
Receive 'A'
Receive 'B'
Receive 'A'
Receive 'B'
Receive 'B'
Receive 'B'
Receive 'A'
Receive 'A'

2017-10-24 補充 :

今天用原子筆捲了兩個彈簧天線焊在收發板上測試, 結果距離有比較遠, 從 30~40 公分變成 5~6 公尺, 這 ..... 對我而言簡直沒有任何實用性, 說好的 40 公尺呢? 失望. 我認為不要把時間浪費在這種無用的模組上.

2015-10-25 補充 :

今天在柯博文寫的 "Arduino 互動設計專題與實戰" 的 13.7 節找到此 433MHz RF 收發器的介紹, 提到發射距離 20~200 公尺 ("不同電壓不同距離"), 原來此種模組可工作於 3.5V~12V 電壓, 所以 200 公尺或許是在 12V 下於空曠地方才有這樣的傳送距離. 難怪這種便宜的模組常用在玩具車, 遙控飛機, 鐵捲門, 防盜器, 汽機車遙控車門啟閉等應用裡, 且常用 12V 的乾電池 (小圓筒形或方形).

我將其範例改編為如下測試 2 :

測試 2 : 傳送板持續送出 'Hello' 給接收板

傳送板程式 :

#include <VirtualWire.h>

int TXpin=8;   //傳送板 Data 腳接 Arduino D8 腳

void setup() {
  Serial.begin(9600);
  vw_set_ptt_inverted(true);
  vw_setup(2000);
  vw_set_tx_pin(TXpin);
  }

void loop() {
  const char *msg="Hello";
  digitalWrite(13, HIGH);
  vw_send((uint8_t *)msg, strlen(msg));
  vw_wait_tx();
  digitalWrite(13, LOW);
  delay(200);
  }

接收板程式 : 

#include <VirtualWire.h> 4 6

uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen=VW_MAX_MESSAGE_LEN;

int RXpin=8;   //接收板 Data 腳接 Arduino D8 腳

void setup() {
  Serial.begin(9600);
  vw_set_ptt_inverted(true);
  vw_setup(2000);
  vw_set_rx_pin(RXpin);
  vw_rx_start();
  Serial.println("Receiving...");
  }

void loop() {
  if (vw_get_message(buf, &buflen)) {
    int i;
    digitalWrite(13, HIGH);
    Serial.println("Got: ");
    for (int i=0; i<buflen; i++) {
      Serial.print(buf[i], HEX);
      Serial.print(" ");
      }
    Serial.println("");
    digitalWrite(13, LOW);
    }
  }




傳送板的 Arduino 內建  LED 13 閃爍表示它不斷送出 "Hello" 字串, 而接收板若有收到資料的話, Arduino 的板上 LED 13 也會不斷閃爍, 若超出收訊範圍即停止閃爍.

接收板序列埠監控視窗輸出 :

Receiving...
Got:
48 65 6C 6C 6F
Got:
48 65 6C 6C 6F
Got:
48 65 6C 6C 6F
Got:
48 65 6C 6C 6F
.......

收到的這些 16 進位 bytes 值其實就是 "Hello" 的 ASCII 編碼, 參考 :

https://zh.wikipedia.org/wiki/ASCII
http://maniacbug.github.io/RF24/starping_8pde-example.html

10 則留言 :

匿名 提到...

若要長距離 有什麼模組 好入門的??

小狐狸事務所 提到...

LoRa, 參考 :
http://yhhuang1966.blogspot.tw/2017/10/lora_16.html

大頭 提到...

如果要改成按著才會亮要怎麼改呢?

小狐狸事務所 提到...

Hi! 就去掉 LED 狀態判斷就可以了. 持續收到就輸出 HIGH, 否則 LOW.

大頭 提到...

請問線要怎樣接呢?

大頭 提到...

請問可以用line教學嗎

小狐狸事務所 提到...

接線方式文中有說明, 只是很忙沒時間畫圖而已. 433M 傳輸距離太短, 建議用 LoRa 模組.

大頭 提到...

請問433版其他線要接哪裡呢?

匿名 提到...

不好意思 想請問這個模組的封包結構是怎麼組成的

小狐狸事務所 提到...

Hi, 您是指底層 byte 結構嗎? 這我沒研究, 從 buffer 中讀出來就是 user data 的 ASCII 編碼了.