嘿!您似乎在 United States,您想使用我们的 English 网站吗?
Switch to English site
Skip to main content

應用LoRa的Raspberry Pi溫度濕度感測器(繁體)

作者 Dan 羅傑瑞
難度 容易
所需時間 2小時

 

LoRa是物聯網領域中最新的產品之一。 LoRa,也稱為遠程(Long Range),是一種廣域網路通信協定,常用于遙感應用程式。 與其他通信協議(例如WiFi和藍牙)不同,LoRa可以在高達10公里的範圍內進行收發。 但是它唯一的缺點是速度可能很慢。 因此使其不適用於諸如圖像傳輸等的快速傳輸應用。 儘管如此,它仍然是非常方便的工具,尤其是用於發送基本資料(例如溫度、濕度、壓力、風速等)的工具。

在構建LoRa網路時,我們希望使用兩個Raspberry Pi作為主要計算模組。 在此設置中,一個Raspberry Pi被用作用戶端,另一個被用作伺服器。 因此,要將該項目所需的材料分為兩部分:

材料和零件

用戶端:

  1. 樹莓派4
  2. LoRa + GPS HAT(Dragino)
  3. BME280模組
  4. 帶有Raspbian作業系統的16GB(或32GB)SD卡
  5. 行動電源
  6. 7英寸Raspberry Pi觸控式螢幕顯示器
  7. 母針連接器

伺服器:

  1. 樹莓派4
  2. LoRa + GPS hat(Dragino)
  3. USB電源
  4. 帶有Raspbian作業系統的16GB(或32GB)SD卡

0125_d9a5ae27bc6d8f7617efaa76f206ec14b036a460.png

在用戶端模組中包含顯示器的目的是讓我們查看其是否能夠正確傳輸資料。 該設備將由行動電源供電,以便我們可以輕鬆將其帶到外部進行測試。

硬體準備

用戶端:

1. 將LoRa + GPS hat連接到Raspberry Pi。
2. 根據hat製造商的不同,按以下步驟焊接Raspberry Pi的GPIO8和GPIO7引腳:
0221_e0d066802c41a161cbd3fe83c8b673de8fed441c.png

3. 除此之外,由於我們要將一些感測器連接到Raspberry Pi,我們還希望將一些公頭針腳焊接到LoRa + GPS hat上,如下所示:

0310_ca8237a7bba0ffada604b0a84ce15a6bb7ba35aa.jpg

4. 將Raspberry Pi連接到7英寸觸控式螢幕LCD。 具體來說,我們想將LCD 5V和GND連接到LoRa + GPS hat。

0424_818fa0c23db107478cd7a048a46ba71dce18f431.png

5. 接下來,通過我們剛剛焊接的引腳連接BME280。

0520_87c7312fe963ebfd4dbc71012a725a114312a9b4.png

請注意,我們將SD0連接到3.3V,這將BME280 I2C位址設置為0x77,稍後將使用。

6. 現在,立即使用USB電源為其供電,以首先下載程式。

伺服器:

  1. 將LoRa + GPS hat連接到Raspberry Pi。
  2. 基於不同hat製造商焊接Raspberry Pi的GPIO8和GPIO7引腳。 (與用戶端中的步驟2相同)
  3. 使用USB電源為其供電

軟體

作為執行這些步驟的先決條件,請參閱以下配置:

0618_062f9d256f1d9b09cf5fa79bc835724de3f999e8.png

在這些設置中,我們要啟用多個介面,例如SSH、VNC、SPI和I2C。 SSH或VNC用於遠程控制Raspberry Pi來下載程式。這意味著您的Raspberry Pi應該已經連接到WiFi。此外,我們的LoRa + GPS hat使用SPI,而BME280使用I2C。

庫+程式準備: 

1. 通過VNC或SSH連接到您的Raspberry Pi用戶端。
2. 啟用之前顯示的介面(SPI和I2C)。
3. 在終端安裝所需的庫,如下所示:

sudo pip3 install --ignore-installed spidev
sudo pip3 install adafruit-circuitpython-bme280
sudo pip3 install board
sudo pip3 install adafruit-blinka

4. 克隆git存儲庫以測試開發板(歸功於meyeranalytics)

sudo git clone https://github.com/rpsreal/pySX127x

5. 克隆我們的git庫

sudo git clone https://github.com/danrustia11/RPiLoRa

6. 對Raspberry Pi伺服器重複步驟1-5

測試

用戶端:

1. 在Raspberry Pi終端上鍵入以下內容來測試您的電路板:

cd pySX127x
sudo ./lora_util.py

0721_c3542eb6444ebafbf514c100e290106dcde1338d.png

這些資訊顯示了主機板的當前配置。不用擔心,我們稍後將在程式中修復這些問題。如果看不到這些資訊,則說明您的開發板可能存在問題,未啟用SPI或無焊接LoRa + GPS hat的GPIO8和GPIO7。

2. 在Raspberry Pi用戶端上,通過在終端中輸入以下行來測試BME280是否正確連接:

sudo i2cdetect –y 1

您將看到以下輸出:

0818_d7c70ecb022568c8c734cb7731543064ba791622.png

如果輸出結果不匹配,這意味著您可能沒有在Raspberry Pi中啟用I2C,或者沒有將BME280的SDO引腳連接到3.3V。

3. 通過輸入以下命令來運行我們的用戶端程式:

cd RPiLoRa
python3 LORA_TH_CLIENT.py

如果沒有問題,您應該從終端輸出中看到“ START”。這意味著它已經在偵聽來自伺服器端的資料請求。

伺服器:

  1. 與用戶端的步驟1相同,通過運行測試程式來確保開發板可以正常工作。
  2. 通過輸入以下命令來運行我們的伺服器程式:
cd RPiLoRa
python3 LORA_TH_SERVER.py

最後,您應該在兩個不同的終端上看到以下輸出:0916_332d9074b4d6cf7a45e9c6ef9d498c5a76208d1b.png

簡而言之,伺服器向用戶端廣播資料請求包,例如“ ENVI”。然後,用戶端偵聽資料請求,並將其節點號、溫度和濕度資料發送回伺服器。之後,伺服器回復“ ACK”以表示它已經接收到資料。簡化的流程圖如下所示:

1032_655f7492bbbdcfa093c8fc6b659f16c9d78e0f2d.png

但是它是如何工作的呢?要考慮的要點是兩個LoRa板的頻率模式。在示例程式中,我們將電路板的頻率設置為915 MHz。可以將其設置為433 MHz、868 MHz或915 MHz,具體取決於您所在的國家/地區。工程師應仔細遵守此頻率要求範圍。但是由於我們只是在測試程式,因此使用915 MHz。
經過測試,此設置可與我們位於建築物10層的伺服器設備配合使用,而將用戶端設備帶離伺服器設備的位置只有五個街區。根據Google Map,該地點已經在500m左右。這是考慮到伺服器設備在房間內,對信號有很多干擾。但是,誰能想像出您可以無需使用GSM或WiFi之類的東西發送資料包。您也可以遵循示例代碼,將伺服器Raspberry Pi連接到Internet並通過網站存儲資料,以使其更加有用。

程式

在這個項目中,我們有兩個主要檔:LORA_TH_CLIENT.py和LORA_TH_SERVER.py
以下是兩個文件的簡短討論:

LORA_TH_CLIENT.py

# Import libraries

#!/usr/bin/env python3

import time
from SX127x.LoRa import *
from SX127x.board_config import BOARD

import board
import busio
import adafruit_bme280

# 設置介面和節點

# 為BME280設置i2c
i2c = busio.I2C(board.SCL, board.SDA)
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c)

# 設置節點
node = 1

# 初始化LoRa板
BOARD.setup()
BOARD.reset()

# 宣吿LoRa類別

import array
class mylora(LoRa):
    def __init__(self, verbose=False):
        super(mylora, self).__init__(verbose)
        self.set_mode(MODE.SLEEP)
        self.set_dio_mapping([0] * 6)

# 每當我們的LoRa板收到一些有效載荷時,就會調用此函數:

    def on_rx_done(self):
        BOARD.led_on()
        
        # 偵聽來自伺服器的請求
        self.clear_irq_flags(RxDone=1)
        payload = self.read_payload(nocheck=True )# Receive INF
       
        # 將有效載荷轉換為可讀字串
        mens=bytes(payload).decode("utf-8",'ignore')
        
        # 列印RX數據
        print("RX: " + mens)
        
        BOARD.led_off()

# 如果收到 ENVI,回復TH數據:

        if mens=="ENVI":
            time.sleep(2)

            # 從BME280獲取TH數據
            t = bme280.temperature
            h = bme280.humidity
            t = round(t, 2)
            h = round(h, 2)
            t = str(t)
            h = str(h)
            
            # 用「;」作為分隔符號創建TX消息
            TX_message = str(node) + ";" + t + ";" + h + ";"
            print("TX: " + TX_message)
            
            # 將TX消息轉換為bytes
            b = list()
            b.extend(TX_message.encode())
 
            # 發送TX消息到伺服器
            self.write_payload(b)
            self.set_mode(MODE.TX)
            
        if mens=="ACK":
            print("The server received the last message you sent.")
            
        # 將用戶端設置回接收器模式
        time.sleep(2)
        self.reset_ptr_rx()
        self.set_mode(MODE.RXCONT)

  # 其他中斷函數:

def on_tx_done(self):
        print("\nTxDone")
        print(self.get_irq_flags())

    def on_cad_done(self):
        print("\non_CadDone")
        print(self.get_irq_flags())

    def on_rx_timeout(self):
        print("\non_RxTimeout")
        print(self.get_irq_flags())

    def on_valid_header(self):
        print("\non_ValidHeader")
        print(self.get_irq_flags())

    def on_payload_crc_error(self):
        print("\non_PayloadCrcError")
        print(self.get_irq_flags())

    def on_fhss_change_channel(self):
        print("\non_FhssChangeChannel")
        print(self.get_irq_flags())

    def start(self):          
        while True:

         # 將用戶端設置為接收器模式以偵聽伺服器資料請求

            self.reset_ptr_rx()
            self.set_mode(MODE.RXCONT) 
            while True:
                pass;
            

lora = mylora(verbose=False)

# 設置我們的LoRa hat的配置:

# 慢速+遠端模式
# f = 915 MHz
# Bw = 125 kHz
# Cr = 4/8
# Sf = 4096chips/symbol
# CRC on. 13 dBm
lora.set_freq(915.0)  
lora.set_pa_config(pa_select=1, max_power=21, output_power=15)
lora.set_bw(BW.BW125)
lora.set_coding_rate(CODING_RATE.CR4_8)
lora.set_spreading_factor(12)
lora.set_rx_crc(True)
lora.set_low_data_rate_optim(True)


# 中程範圍默認
# Defaults:
# f = 434.0MHz
# Bw = 125 kHz
# Cr = 4/5
# Sf = 128chips/symbol
# CRC on 13 dBm
# lora.set_pa_config(pa_select=1)

assert(lora.get_agc_auto_on() == 1)

# 啟動程式:

try:
    print("START")
    lora.start()
except KeyboardInterrupt:
    sys.stdout.flush()
    print("Exit")
    sys.stderr.write("KeyboardInterrupt\n")
finally:
    sys.stdout.flush()
    print("Exit")
    lora.set_mode(MODE.SLEEP)
BOARD.teardown()

 

LORA_TH_SERVER.py

# Import libraries

#!/usr/bin/env python3

import time
from SX127x.LoRa import *
from SX127x.board_config import BOARD

BOARD.setup()
BOARD.reset()

# 宣佈LoRa類別

class mylora(LoRa):
    def __init__(self, verbose=False):
        super(mylora, self).__init__(verbose)
        self.set_mode(MODE.SLEEP)
        self.set_dio_mapping([0] * 6)
        self.var=0

# 在收到有效載荷時調用:

    def on_rx_done(self):
        BOARD.led_on()
        
        # 將rx標誌設置為1
        self.clear_irq_flags(RxDone=1)
        
        # 獲取有效載荷並解碼
        payload = self.read_payload(nocheck=True)
        payload_conv = bytes(payload).decode("utf-8",'ignore')
        
        # 列印有效載荷
        print ("RX: " + payload_conv)

     # 從發送方提取節點號、t和h

        s = payload_conv.split(";")
        if len(s) >= 2:
            node = s[0]
            t = s[1]
            h = s[2]
            print("Node: " + node)
            print("Temperature: " + t)
            print("Humidity: " + h)
        
        
        BOARD.led_off()
        
        # 用戶端準備所需要的強制等待時間
        time.sleep(2)

        # 發送確認給用戶端:

        ACK_message = "ACK"
        b = list()
        b.extend(ACK_message.encode())
        print ("TX: " + ACK_message)
        self.write_payload(b)
        
        # 將後臺伺服器設置為TX模式
        self.set_mode(MODE.TX)
        self.var=1

    def on_tx_done(self):
        print("\nTxDone")
        print(self.get_irq_flags())

    def on_cad_done(self):
        print("\non_CadDone")
        print(self.get_irq_flags())

    def on_rx_timeout(self):
        print("\non_RxTimeout")
        print(self.get_irq_flags())

    def on_valid_header(self):
        print("\non_ValidHeader")
        print(self.get_irq_flags())

    def on_payload_crc_error(self):
        print("\non_PayloadCrcError")
        print(self.get_irq_flags())

    def on_fhss_change_channel(self):
        print("\non_FhssChangeChannel")
        print(self.get_irq_flags())

  # 啟動數據請求例程:

    def start(self):          
        while True:
            while (self.var==0):
                
                # 向用戶端索取ENVI數據
                TX_message = "ENVI"
                b = list()
                b.extend(TX_message.encode())
                print ("TX: " + TX_message)
                self.write_payload(b)
                self.set_mode(MODE.TX)
                time.sleep(3)
                
                # 將伺服器設置為接收器模式
                self.reset_ptr_rx()
                self.set_mode(MODE.RXCONT) 
            
                start_time = time.time()
                while (time.time() - start_time < 5): # 等到收到資料後再發送
                    pass;
            
            # 將伺服器設置為接收器模式
            self.var=0
            self.reset_ptr_rx()
            self.set_mode(MODE.RXCONT) 
            time.sleep(5)

# 設置LoRa hat的模式:

lora = mylora(verbose=False)

# 慢速+遠端模式
# f = 915 MHz
# Bw = 125 kHz
# Cr = 4/8
# Sf = 4096chips/symbol
# CRC on. 13 dBm
lora.set_freq(915.0)
lora.set_pa_config(pa_select=1, max_power=21, output_power=15)
lora.set_bw(BW.BW125)
lora.set_coding_rate(CODING_RATE.CR4_8)
lora.set_spreading_factor(12)
lora.set_rx_crc(True)
lora.set_low_data_rate_optim(True)


# 中範圍預設值  Defaults:
# f = 434.0MHz
# Bw = 125 kHz
# Cr = 4/5
# Sf = 128chips/symbol
# CRC on 13 dBm
# lora.set_pa_config(pa_select=1)

# 啟動伺服器

assert(lora.get_agc_auto_on() == 1)
try:
    print("START SERVER")
    lora.start()
except KeyboardInterrupt:
    sys.stdout.flush()
    print("Exit")
    sys.stderr.write("KeyboardInterrupt\n")
finally:
    sys.stdout.flush()
    print("Exit")
    lora.set_mode(MODE.SLEEP)
    BOARD.teardown()

 

參考資料:

https://github.com/rpsreal/pySX127x

https://wiki.dragino.com/index.php?title=Lora/GPS_HAT

https://pypi.org/project/pyLoRa/

CAVEDU Education is devoted into robotics education and maker movement since 2008, and is intensively active in teaching fundamental knowledge and skills. We had published many books for readers in all ages, topics including Deep Learning, edge computing, App Inventor, IoT and robotics. Please check CAVEDU's website for more information: http://www.cavedu.com, http://www.appinventor.tw
DesignSpark Electrical Logolinkedin