Arduino筆記(99):LoRa-01 SX1278遠距離傳送與接收AHT10溫濕度

剛完成 nRF24L01 無線傳送溫濕度,遠端則在接收後呈現在 LCD 屏幕上的實作,剛好最近在淘X買了兩個由安信可製造的 LoRa-01 SX1278,也來瞭解一下 LoRa 的基本知識,以及如何使用 Arduino 連接後,進行資料的傳送與接收。本實作繼續使用AHT10的溫濕度感測器,將測得的值傳送給另一個 Lo-Ra接收設備,在 LCD1602 顯示溫濕度值。

[LoRa技術]

LoRa(Long Range)是Semtech開發的LPWAN(Low-Power Wide-Area Network,低功率廣域網路)協定,是依chirp展頻(CSS)技術衍生的展頻調變技術為基礎[1]。是由法國格勒諾布爾的Cycleo公司所發展,後來被LoRa Alliance的創立成員Semtech所收購。[維基百科]

LoRa技術可用於將雙向信息傳輸到長距離而不會消耗很多功率,通常可達到15-20km的距離,根據 thethingsnetwork.org 聲稱它可以使用 25mW 的功率傳輸距離達到 766 公里,前提是節點與網關之間沒有障礙的最佳通訊品質時測得的。LoRa使用世界各地都可使用的未許可頻率。這些是最廣泛使用的頻率:
  • 歐洲868 MHz
  • 北美915 MHz
  • 亞洲433 MHz頻段
這些頻段是開放使用的,任何人都可以自由使用它們,而無需付費或無需獲得許可。通常在工廠的環境當中,要做到物聯網的通訊,需要克服距離與電力問題,在現場部署數百個 Sensor 節點,要將資料傳輸到後端伺服機處理,需要這些節點的感測器具備無線的功能,且應該使用小電池工作。目前無線射頻的解決方案,可以將數據發送到遠距離,但是需要更多的功率,因此無法通過電池供電,而藍芽無線傳輸可以使用很少的功率工作,但是不能將數據發送到遠距離。這就是為何LoRa會有需求的原因。

我購買的 LoRa 擴頻無線模組,是安信可採用 SX1278 開發的遠程調制解調器,用於超長距離擴頻通信,抗干擾性強,能夠最大限度降低電流消耗。引出的接腳跟 NRF24L01 模組一樣,如果想要遠距離傳輸數據,可以直接替換NRF24L01,只要更改一下程式即可,不需要更改硬體設計。
LoRa本身也有缺點,為了以低功耗傳輸,其頻寬帶很小,最大帶寬約為5.5 kbps,這意味著只能發送少量數據。無法通過此技術發送音頻或視頻,它僅適用於傳輸較少的信息(例如傳感器值)。

SX1278特點:
  • LoRa擴頻調製技術
  • 超遠通信距離15KM
  • +20dBm-100mW電壓變化時恆定的射頻功率輸出
  • 高靈敏度:低至-148dBm
  • 半雙工SPI通訊
  • 可編程比特率高達300kbps
  • 支持FSK、GFSK、MSK、GMSK、LoRa§及OOK調製方式
  • 127dB的RSSI動態範圍
  • 自動射頻信號檢測,CAD模式和超高速AFC
  • 帶有CRC、高達256字節的數據包引擎
  • 小體積雙列郵票孔貼片封裝
  • 帶屏蔽殼
  • 彈簧天線(Ra-01)或IPEX外接天線(Ra-02)

[安裝程式庫Library]

除了硬體的連接外,還需要 arduino-LoRa 程式庫,才能驅動或管理這個無線射頻模組的接收與發射。本實作需要安裝以下程式庫:
如為下載程式庫(Library)安裝方法請參考另一篇文章:Arduino筆記:安裝 Arduino IDE 程式庫(Library)

[材料]

  • Arduino UNO  x2
  • LoRa-01 SX1278 x2
  • LCD1602液晶顯示器
  • I2C/介面 LCD1602轉接板 PCF8574
  • 麵包板 x2
  • AHT10 溫濕度感測器 x1
  • 排線 n 條


[arduino-LoRa 程式庫]

arduino-LoRa 程式庫控制 SX1728 LoRa 物件的方法如下:
◾ begin(frequency);
以指定的頻率進行 Lora 物件初始化。frequency 頻率以 Hz 為單位,可使用433E6(亞洲),868E6(歐洲) 915E6(北美)三種頻率。
◾ beginPacket(implicitHeader);
開始循序發送封包。implicitHeader(選項)true 啟用隱式頭模式,false啟用顯式頭模式(預設)
◾ endPacket();
結束循序發送封包。
◾ available();
回傳可以讀取的 Byte 數。
◾ parsePacket(size);
檢查是否已經收到封包。size為選項,假使大於 0 ,啟用 implicit header 模式,並使用指定的 size 字節,預設為 explicit header 模式。
回傳值為封包的長度,或傳回 0 表示沒有收到封包。
◾ packetRssi();
回傳最後接收封包的平均 RSSI(Received Signal Strength Indicator),接收信號强度指示。

[接線圖]

LoRa模組原有16個引腳,其中有 6 個是 GPIO 引腳使用,範圍從 DIO0 到 DIO5,4 個由接地引腳使用。該模組工作在 3.3V 電壓,因此 LoRa 上的 3.3V 腳連接到Arduino UNO 板上的3.3v 引腳。修改後的 SPI 模組,接腳跟 nRF24L01 一樣,如下圖:
如無法根據上圖連接引腳,可以使用下表來確保正確連接。

發射端:
Arduino UnoLoRa SX1278模組AHT10
GNDGNDGND
3.3V3.3-
5V-VCC
D2G0/DIO0-
D9RST-
D10EN/NSS-
D11MOSI-
D12MISO-
D13SCK-
A5-SCL
A4-SDA

註:由於找不到 LoRa-01 SPI介面的 Fritzing圖,改以 16 Pin 引腳示意。

接收端:
Arduino UnonRF24L01LCD1602
GNDGNDGND
3.3V3.3-
5V-VCC
D2G0/DIO0-
D9RST-
D10EN/NSS-
D11MOSI-
D12MISO-
D13SCK-
SCL-SCL
SDA-SDA


註:由於找不到 LoRa-01 SPI介面的 Fritzing圖,改以 16 Pin 引腳示意。

[傳送端程式]

連接 Arduino 和 LoRa SPI 模組連接後,從 AHT10 傳感器讀取得的溫濕度值,透過 LoRa 送到遠端的 Lora,每秒傳送一筆資料。
#include <SPI.h>
#include <LoRa.h>
#include <AHT10.h>

AHT10 myAHT10(AHT10_ADDRESS_0X38);
float temp = 0;
float humidity = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("LoRa Sender");
  if (!LoRa.begin(433E6)) 
  {
    Serial.println("Starting LoRa failed!");
    while(1);
  }
  while (myAHT10.begin() != true)  {
    Serial.println(F("AHT10 not connected...")); 
    delay(5000);
  }
  Serial.println(F("AHT10 initial OK"));    
}

void loop() {
  // 讀取 AHT10 的溫濕度
  temp = myAHT10.readTemperature();    //讀取 AHT10 溫度
  humidity = myAHT10.readHumidity();   //讀取 AHT10 濕度
    
  Serial.print("Temperatura: ");
  Serial.println(temp);
  Serial.print("   Humidity: ");
  Serial.println(humidity);

  String msg = String(temp)+" "+String(humidity);

  Serial.print("Sending message :");
  Serial.println(msg);
  
  LoRa.beginPacket();
  LoRa.print(msg);
  LoRa.endPacket();
  delay(1000);   // 每1秒鐘送一次資料
}

[接收端程式]

從 Ro-Ra 接收到資料後,由 Arduino UNO 顯示在 LCD 1602 屏幕上。
#include <SPI.h>
#include <LoRa.h>
#include <string.h>
#include <LiquidCrystal_I2C.h>   // 引用 LiquidCrystal_I2C Library
LiquidCrystal_I2C lcd(0x27,16,2);  // 設定 LCD 位址為 0x27,有 16 個字元 2 列

void setup() {
  Serial.begin(9600);
  Serial.println("LoRa Receiver");
  if (!LoRa.begin(433E6)) {
    Serial.println("Starting LoRa failed!");
    while (1);
  }
  lcd.init();                // 初始化 lcd 
  lcd.backlight();            // 設定背板為亮    
}

void loop() {
  String str="";
  String tempc="";
  String humidity="";    
  
  int packetSize = LoRa.parsePacket();
  if (packetSize) {
    Serial.print("Received packet '");
    // 讀取封包
    while (LoRa.available())  {
      str=str+((char)LoRa.read());    
    }
    tempc = str.substring(0,5);
    humidity = str.substring(6,12);
    Serial.print(str);
    Serial.print("   ");
//    Serial.print(tempc);
//    Serial.print("   ");
//    Serial.print(humidity);
    Serial.print("' with RSSI ");
    Serial.println(LoRa.packetRssi());
        
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Temp");
    lcd.setCursor(9, 0);
    lcd.print(tempc);
    lcd.print(" C");
    lcd.setCursor(0, 1);
    lcd.print("Humidity");
    lcd.setCursor(9, 1);
    lcd.print(humidity);
    lcd.print(" %");
  }
//  delay(1000);     
}

[實作結果]


在接收端,原本每次讀取不到資料後,會 delay 1秒鐘,但結果呈現接收頻率不規則的情況,有時會多達10幾秒才收到 1 筆資料,。將 delay(1000); 這行當作註解後,接收資料變得比較順利,但仍有會遺失封包的情況。以下是傳送端送出資料的串列埠視窗:

以下是傳送端送出資料的串列埠視窗:

[參考資料]

6 留言

  1. 請問實測距離可以多遠?

    回覆刪除
    回覆
    1. 我沒實測最遠的距離,測試的情境僅10多公尺,收發正常,改天找機會再測試最遠距離。

      刪除
  2. Arduino Uno 邏輯電壓是5V,Ra-01邏輯運作電壓是1.8~3.7V,恐怕Ra-01已經內傷了!
    兩個之間最好要加上邏輯電位轉換器。

    回覆刪除
    回覆
    1. Arduino也有提供3.3V的電壓,我接的是3.3V的Pin腳,謝謝提醒與說明。

      刪除
    2. 您可能會錯意了!
      SPI那一些 I/O(NSS、RST、MISO、DIO0..等等),從Arduino送出來的邏輯1是5V,不過Ra-01的邏輯1的運作電壓只有到3.7V,我才說有可能Ra-01已經內傷。
      所以我認為文章後面寫接收端接收不到的問題,有很大的機會是Ra-01已經故障了,訊號送的怪怪的!

      刪除
  3. 您好,我依您的程式發現接收端不能單獨顯示溫溼度,應該改成下面方式,可正常顯示
    感謝您網站讓我受益良多

    int packetSize = LoRa.parsePacket();
    if (packetSize) {
    Serial.print("Received packet '");
    // 讀取封包
    while (LoRa.available()) {
    String LoRaData = LoRa.readString();
    Serial.print(LoRaData);
    tempc = LoRaData.substring(0,5);
    humidity = LoRaData.substring(6,12);
    }

    Serial.print(" ");
    Serial.print(tempc);
    Serial.print(" ");
    Serial.print(humidity);
    Serial.print("' with RSSI ");
    Serial.println(LoRa.packetRssi());

    回覆刪除

張貼留言

較新的 較舊