# 「[Avionic 101](https://hackmd.io/@Jackiempty/avionic-101-list)」:LoRa應用
本文將會帶你了解 **LoRa 模組** 以及兩種不同的使用方式。
1. **AT 指令型模組**(內建 MCU,UART 控制)
2. **裸 LoRa 模組**(晶片級,需要 MCU 控制 SPI)
首先還是需要先了解什麼是LoRa你才知道為什麼要用他。
## 什麼是LoRa
LoRa(Long Range) 是一種專為 低功耗廣域網路(LPWAN, Low Power Wide Area Network) 設計的無線調變技術。由 Semtech 公司開發,LoRa 的核心特色在於:
* 遠距離傳輸(可達數公里)
* 低功耗運作(電池可持續數年)
* 低資料傳輸率(適合小量數據)
* 低成本部署
* 使用不須申請許可的次GHz射頻頻段,例如433 MHz、868 MHz(歐洲)、915 MHz(澳大利亞及北美洲)及923 MHz(亞洲),**台灣則是可以使用920 - 925 MHz**。
### LoRa 的特性  
| 特性            | 說明 |
|----------------|------|
| *長距離傳輸* | 幾公里到十公里(依環境、天線、功率而定) |
| *低功耗*     | 適合電池供電裝置 |
| *低速率*     | 傳輸速率約 0.3 kbps ~ 50 kbps,不適合傳大檔 |
| *穿透能力強* | 比 Wi-Fi、藍牙更能穿透牆壁、建築物 |
| *Mesh / Point-to-Point / Gateway 支援* | 可用於點對點或 LoRaWAN 網路 |
### 為什麼要用 LoRa
1. *長距離點對點控制*
   - 比如遙控火箭點火、農田感測、山區設備控制
2. *低功耗應用*
   - 電池裝置可運作數月甚至數年
3. *不需電信費用*
   - 用免執照頻段,不必申請基地台或付月租
4. *抗干擾能力強*
   - 在雜訊環境下仍能傳輸成功
5. *可結合 LoRaWAN*
   - 建構大範圍感測網路
### 什麼是 LoRaWAN
LoRaWAN(Long Range Wide Area Network)是一種基於 LoRa 調變技術的高層網路通訊協定,專為低功耗廣域網路(LPWAN)設計。它規範了設備之間如何進行資料交換、網路如何管理節點,以及如何確保資料安全與可靠性。
主要特點包括:  
- **分層架構**:由終端節點(Sensors/Actuators)、閘道器(Gateways)、網路伺服器(Network Servers)與應用伺服器(Application Servers)組成  
- **節點管理**:支援節點的註冊、認證與管理  
- **安全性**:提供端對端加密與資料完整性驗證  
- **省電設計**:支援非同步上行通訊與長休眠模式,適合電池供電的物聯網裝置  
- **擴充性強**:可支持數千甚至數萬個終端節點,適合大型感測網路部署  
簡而言之,LoRa 是物理層的無線技術,而 LoRaWAN 是建立在 LoRa 之上的完整網路協定,讓多個 LoRa 裝置可以安全、有效率地組成大型物聯網網路。
---
簡單介紹完LoRa的特點以及應用,接下來會開始說明 **AT指令模組** 跟 **裸LoRa模組** 在使用上有什麼差別。
## LoRa 模組的兩種型態
| 類型 | 說明 | 例子 | 優點 | 缺點 |
|------|------|------|------|------|
| **AT 指令模組** | 模組內建 MCU,封裝好 LoRa 協定,透過 UART 下 AT 指令即可使用 | RYLR998、E32 系列 | 簡單易用,不需懂 LoRa 細節 | 彈性不足、可調參數少 |
| **裸 LoRa 模組** | 僅有 RF 晶片(如 SX1278),需 MCU 透過 SPI 控制 | SX1276 / SX1278 / SX1262 | 可完全掌握 LoRa 參數,靈活客製化 | 程式設計較複雜,需要使用 SPI |
## LoRa 模組互動流程與設定
在使用 LoRa 模組時,無論是搭配 **ESP32** 、 **Arduino** 或是其他 MCU 來搭配,關鍵在於**透過 UART 發送 AT 指令**來設定參數,讓兩個甚至以上的模組能夠在無限的環境中互相溝通。
### 基本概念
- **每個 LoRa 模組都須有一個獨立的位址(ADDRESS)** 用來識別節點,可以當作是電話號碼。
- **同一個 LoRa 網路必須有相同的網路ID(NETWORKID)** 才能收發彼此的資料。
- **通訊前必須先設定參數(位址、網路ID、頻率、功率)**,確保雙方一致。
### 兩個 LoRa 節點互動示例
假設有兩個 LoRa 模組:
- **模組 A**:位址 `1`(ADDRESS=1)
- **模組 B**:位址 `2`(ADDRESS=2)
- **共用**:
  - 網路 ID = 18
  - 頻率通道 = 12(對應 915 MHz)
  - 擴頻因子 SF = 7
  - 頻寬 = 1(125 kHz)
  - 功率 = 4
   
#### 1、設定模組 A
```text
AT+ADDRESS=1
AT+NETWORKID=18
AT+PARAMETER=12,7,1,4
```
#### 2、設定模組 B
```text
AT+ADDRESS=2
AT+NETWORKID=18
AT+PARAMETER=12,7,1,4
```
#### 3、發送訊息
- A → B 發送"Hello"
```text
AT+SEND=2,5,Hello
```
#### 4、接收訊息
- 如果 B 收到訊息,可能會自動回覆:
```text
+RCV=1,5,Hello,-45,25
```
- 1 → 發送方位址
- 5 → 資料長度
- Hello → 訊息內容
- -45 → RSSI (訊號強度,dBm)
- 25 → SNR (訊號雜訊比,dB)
---
### 常用 AT 指令
| 指令 | 功能 | 範例 | 範例解釋 |
|------|------|------|----------|
| AT | 測試模組是否正常回應 | AT | 模組會回覆 `OK`,表示 UART 通訊正常、模組可用。 |
| AT+ADDRESS=<addr> | 設定模組的位址(1~65535,一般建議 1~255) | AT+ADDRESS=1 | 設定本機 LoRa 模組的通訊位址為 `1`,收發訊息時會用來識別來源與目標。 |
| AT+NETWORKID=<id> | 設定 LoRa 網路 ID(0~65535,需相同才能通訊) | AT+NETWORKID=18 | 將本機加入 ID 為 18 的 LoRa 網路,只有相同 Network ID 的模組能互通。 |
| AT+PARAMETER=<freq>,<sf>,<bw>,<power> | 設定無線參數 | AT+PARAMETER=12,7,1,4 | 12`=頻率通道(通常對應到實際頻率,模組手冊會列對應表,例如 12`=915MHz)、`7`=擴頻因子(SF7,數字越大傳得越遠但越慢)、`1`=頻寬設定、`4`=發射功率等級。 |
| AT+SEND=<addr>,<len>,<data> | 發送資料 | AT+SEND=2,5,Hello | 傳送 Hello`(長度=5字元)到位址 2` 的 LoRa 模組,必須指定字串長度,否則模組不會正確處理。 |
| AT+RECV | 讀取接收到的資料(部分模組自動回覆) | AT+RECV | 查詢是否有新封包,模組可能回覆 `+RCV=1,5,Hello,-45,25`(代表來自位址 1,資料長度 5,內容 Hello,RSSI -45 dBm,SNR 25 dB)。 |
    
#### 常見錯誤與排查  
- **指令無回應**:檢查 UART 接線、波特率設定是否一致  
- **傳送失敗**:確認頻率、Network ID、Address 是否匹配  
- **指令格式錯誤**:注意大小寫與逗號、等號等符號  
- **使用順序提醒**:記得先設定模組地址、網路 ID、參數後再傳送資料  
#### 指令使用流程建議  
1. 連線模組,測試 `AT` 指令是否有回應  
2. 設定 `ADDRESS`、`NETWORKID`、`PARAMETER`  
3. 傳送資料 `AT+SEND`  
4. 查收資料 `AT+RECV`  
#### 傳送資料長度限制與字元處理  
- 字串長度需正確指定(否則模組可能無法正常解析)  
- 避免特殊不可見字元,若要傳送二進位資料,需使用模組支援的特殊格式  
#### 進階指令設定(視模組支援)  
- 重試次數、回應確認設定  
- 加密功能設定  
    
---
    
### 接線方式
| ESP32 腳位 | LoRa 模組腳位 | 說明 |
|------------|--------------|------|
| GPIO16     | RX           | ESP32 TX → LoRa RX(交叉接) |
| GPIO17     | TX           | ESP32 RX ← LoRa TX(交叉接) |
| 3.3V       | VCC          | 電源(不可用 5V) |
| GND        | GND          | 接地共用 |
    
    
##  什麼是 HardwareSerial
- HardwareSerial 是 Arduino Core 提供的類別,用來操作 MCU 內建的 *硬體 UART*
- ESP32 有 **3 組硬體 UART**:
  - **UART0**(`Serial`) → 預設連 USB 供調試用
  - **UART1**(`HardwareSerial(1)`) → 可自訂 TX/RX 腳位
  - **UART2**(`HardwareSerial(2)`) → 可自訂 TX/RX 腳位
- 硬體 UART 透過 *TX / RX 腳* 傳輸數位訊號(3.3V 邏輯)
## 為什麼用 HardwareSerial 控 LoRa 模組
- LoRa 模組(如 RYLR998、E32 系列)內建 UART 介面
- UART 用來:
  1. 傳送 AT 指令給 LoRa 模組
  2. 接收 LoRa 模組的回覆或無線接收到的資料
- 你在程式中呼叫 LoRaSerial.print() 或 `.println()`,就能把資料透過 UART 發送到 LoRa 模組
- HardwareSerial → 只是 ESP32 與 LoRa 模組的「有線數據通道」
- LoRa 模組的 MCU 收到 UART 傳來的 AT 指令 / 資料
- 模組 MCU 再用 RF(無線電)依指定頻率與設定去發射
## HardwareSerial 常用指令
| 指令 | 說明 | 範例 |
|------|------|------|
| begin(baud, config, rxPin, txPin) | 初始化 UART 並設定鮑率、資料格式、腳位 | `LoRaSerial.begin(115200, SERIAL_8N1, 16, 17);` |
| end() | 關閉 UART | `LoRaSerial.end();` |
| available() | 回傳可讀取的字元數(>0 代表有資料) | `if (LoRaSerial.available()) { ... }` |
| read() | 讀取一個字元(回傳 int,無資料時為 -1) | `char c = LoRaSerial.read();` |
| readString() | 讀取整個接收到的字串(直到超時) | `String data = LoRaSerial.readString();` |
| readStringUntil(char) | 讀到指定字元為止(常用換行符 \n) | `String line = LoRaSerial.readStringUntil('\n');` |
| peek() | 查看下一個可讀字元但不移除它 | `char c = LoRaSerial.peek(); `|
| flush() | 等待傳輸緩衝區的資料全部送出 | `LoRaSerial.flush()`; |
| print(data) | 傳送資料(不加換行) | `LoRaSerial.print("Hello")`; |
| println(data) | 傳送資料並自動換行(加 \r\n) | `LoRaSerial.println("Hello");` |
## 基本程式範例
```cpp
#include <HardwareSerial.h>
HardwareSerial LoRaSerial(1);
void setup() {
  Serial.begin(115200); 
  LoRaSerial.begin(115200, SERIAL_8N1, 16, 17);
  Serial.println("初始化完成,測試 LoRa AT 指令...");
  LoRaSerial.println("AT");
}
void loop() {
  if (LoRaSerial.available()) {
    String msg = LoRaSerial.readStringUntil('\n');
    Serial.println("LoRa 回覆: " + msg);
  }
}
```
### 程式說明
```cpp
#include <HardwareSerial.h>
HardwareSerial LoRaSerial(1);
```
- 引入程式庫跟建立 LoRaSerial 物件,指定使用 UART1。
```cpp
LoRaSerial.begin(115200, SERIAL_8N1, 16, 17);
```
- 初始化 UART1,鮑率設為 115200。
- SERIAL_8N1 → 8 位資料、無同位元(N)、1 停止位(常見的 UART 設定)。
- 16, 17 → 指定 UART1 的 RX 腳位為 GPIO16、TX 腳位為 GPIO17。
```cpp
LoRaSerial.println("AT");
```
- 送出 AT 指令`"AT\r\n"`給 LoRa 模組,測試是否連通。
- LoRa 模組若正常,應回覆 "OK" 或其他對應字串。
```cpp
void loop() {
  if (LoRaSerial.available()) {
    String msg = LoRaSerial.readStringUntil('\n');
    Serial.println("LoRa 回覆: " + msg);
  }
}
```
- `LoRaSerial.available()` → 檢查 UART1 有沒有資料可讀(LoRa 模組的回覆)。
- `readStringUntil('\n')` → 一直讀到換行符(\n)為止,並回傳成 String。
-  `Serial.println()` 把這個回覆印到 USB 序列監視器上。