# LoRa Soil Sensor Relay

Firmware cho mang cam bien dat dung STM32F103, DS3231, E22 LoRa va ESP32. Du lieu duoc truyen noi tiep ve ESP32, sau do day len Firebase.

This project uses STM32F103, DS3231, E22 LoRa, and ESP32 to relay soil-sensor data to Firebase.

---

# Huong dan tieng Viet

## 1. Kien truc he thong

Cac chuoi duoc ho tro:

```text
2 node: 1002 -> 1001 -> ESP32
3 node: 1003 -> 1002 -> 1001 -> ESP32
4 node: 1004 -> 1003 -> 1002 -> 1001 -> ESP32
```

Time-sync chay theo chieu nguoc lai va ACK quay ve ESP32:

```text
ESP32 -> 1001 -> 1002 -> ... -> node cuoi
ESP32 <- 1001 <- 1002 <- ... <- ACK node cuoi
```

## 2. Ket noi phan cung

### STM32 va ST-Link

| ST-Link | STM32F103 |
|---|---|
| SWDIO | PA13 |
| SWCLK | PA14 |
| GND | GND |
| 3.3V | 3.3V |
| NRST | NRST (khuyen nghi) |

Dat `BOOT0 = GND` de chay firmware tu Flash.

### DS3231

- Noi `INT/SQW` vao `PA0`.
- Keo len 3.3 V bang dien tro 4.7 kohm.
- DS3231 phai co pin backup de giu gio khi mat nguon chinh.

### E22 tren ESP32

```text
E22 TXD -> ESP32 GPIO16 (RX)
E22 RXD -> ESP32 GPIO17 (TX)
E22 M0  -> ESP32 GPIO4
E22 M1  -> ESP32 GPIO5
E22 AUX -> ESP32 GPIO22
E22 GND -> ESP32 GND
```

E22-400T30D can nguon rieng on dinh, du dong cho luc phat 30 dBm. Khong nen cap truc tiep tu chan 3.3 V yeu cua ESP32.

## 3. Cau hinh tat ca E22

Tat ca module phai cung cau hinh:

```text
Model/bang tan : E22-400T30D / 400 MHz
UART            : 9600, 8N1
Air rate        : 2.4 kbps
Channel         : 25 (435.125 MHz)
NET ID          : 0
Address         : 200 (0x00C8)
Packet size     : 240 bytes
Power           : 30 dBm
Transmission    : Normal/Transparent
Relay/LBT/RSSI  : Disable
WOR cycle       : 2000 ms
Key             : 0
```

Raw configuration:

```text
00 C8 00 62 00 19 03 00 00
```

Dung `RF_Setting(E22-E9X(SL)) V3.5(EN).exe`, nhan **Set Param**, sau do nhan **Get** de doc lai va xac nhan.

## 4. Chon so node trong chuoi

Dat cung mot gia tri trong:

- `Core/Src/main.c`
- `lora_recv/lora_recv.ino`

```c
#define RELAY_CHAIN_NODE_COUNT 2U
```

Gia tri:

```text
2U: 1002 -> 1001 -> ESP32
3U: 1003 -> 1002 -> 1001 -> ESP32
4U: 1004 -> 1003 -> 1002 -> 1001 -> ESP32
```

Hai firmware phai co cung `RELAY_CHAIN_NODE_COUNT`.

## 5. Dat ID cho tung STM32

ID duoc luu trong Flash tai `0x0800FC00`. Thuc hien rieng cho tung board.

Mo `Core/Inc/flash.h`, dat ID can ghi va bat che do ghi bat buoc:

```c
#define FLASH_DEFAULT_DEVICE_ID 1001UL
#define FLASH_FORCE_DEFAULT_DEVICE_ID_ON_BOOT 1U
```

ID hop le:

```text
Node 1: 1001
Node 2: 1002
Node 3: 1003
Node 4: 1004
```

Build, nap va reset board mot lan. Sau do tat che do ghi bat buoc:

```c
#define FLASH_FORCE_DEFAULT_DEVICE_ID_ON_BOOT 0U
```

Build va nap lai firmware van hanh. Kiem tra trong Live Expressions:

```c
deviceId
relay_parent_id
```

Vi du node 1002:

```text
deviceId       = 1002
relay_parent_id = 1001
```

Khong de `FLASH_FORCE_DEFAULT_DEVICE_ID_ON_BOOT = 1U` trong firmware van hanh, vi moi lan reset se ghi lai ID mac dinh.

## 6. Nap firmware STM32

1. Mo project bang STM32CubeIDE.
2. Ket noi ST-Link theo bang SWD.
3. Chon **Project > Build Project**.
4. Chon **Run As > STM32 C/C++ Application** hoac nhan nut Run.
5. Cho den khi hien `Download verified successfully`.
6. Reset STM32 de chay.

Nap cung firmware relay cho cac STM32, nhung moi board phai co ID da luu rieng.

## 7. Nap firmware ESP32

Mo `lora_recv/lora_recv.ino` bang Arduino IDE va chon:

```text
Board            : DOIT ESP32 DEVKIT V1
Port             : dung COM cua ESP32
Upload Speed     : 115200 (nen dung khi nap khong on dinh)
Flash Mode       : DIO
Flash Frequency  : 40 MHz
```

Dien Wi-Fi va Firebase cua he thong trong source, sau do nhan Upload. Neu bi ket tai `Connecting...`, giu nut BOOT cho den khi bat dau `Writing at...`.

Serial Monitor cua ESP32 dung `115200 baud`. UART noi voi E22 dung `9600 baud`.

## 8. Dat gio DS3231 thu cong (tuy chon)

Thong thuong khong can dat gio thu cong. ESP32 lay gio NTP va gui xuong cac node.

Neu can khoi tao RTC bang tay, trong `Core/Src/main.c` dat:

```c
#define DS3231_SET_TIME_ON_BOOT 1U
```

Cap nhat `DS3231_DateTime_t` thanh ngay gio hien tai, build va nap. Sau khi DS3231 da duoc ghi, phai tra lai:

```c
#define DS3231_SET_TIME_ON_BOOT 0U
```

Sau do build va nap lai. Neu de `1U`, moi lan reset se ghi lai thoi gian co dinh trong source.

## 9. Dong bo gio tu ESP32 khi khoi tao lan dau

Cach khoi dong/reset khuyen nghi:

1. Giu `DS3231_SET_TIME_ON_BOOT = 0U`.
2. Bat hoac reset ESP32 va cho log `NTP sync requested`.
3. Reset node xa ESP32 nhat truoc.
4. Reset lan luot cac node ve phia ESP32, moi node cach nhau khoang 1-3 giay.
5. Cho log:

```text
TIME_SYNC END-TO-END ACK via node=1001
```

Thu tu reset:

```text
2 node: 1002 -> 1001
3 node: 1003 -> 1002 -> 1001
4 node: 1004 -> 1003 -> 1002 -> 1001
```

Khong reset tung node cach nhau qua lau. Time-sync khong duoc luu trong relay queue: neu node gan nhan time-sync khi node ke tiep dang ngu, goi khong the di het chuoi va ESP32 se chua nhan duoc END-TO-END ACK. Goi du lieu sensor van co the den ESP32 sau do vi du lieu co queue/retry rieng; dieu nay khong thay the ACK dong bo thoi gian.

Khi STM32 reset, no khong ngu ngay. Node se:

1. Bat nguon E22 va cam bien.
2. Doc du lieu.
3. Nghe goi time-sync.
4. Ghi epoch vao DS3231.
5. Chuyen tiep time-sync va ACK.
6. Gui du lieu, tat nguon E22/cam bien va vao STOP mode.

`via node=1001` nghia la node 1001 la hop cuoi gui ACK ve ESP32. END-TO-END ACK xac nhan time-sync da den node cuoi cua chuoi va cac DS3231 tren duong truyen da ghi thanh cong.

Node cuoi gui ACK time-sync 3 lan, cach nhau 500 ms. Cac node trung gian forward tung ACK ve ESP32; ESP32 chi chap nhan va in ACK dau tien cua cung mot `seq`. Co che nay tot hon ban cu chi gui mot ACK: no chiu duoc mot hoac hai goi ACK bi mat. Tuy nhien, ACK lap van khong the di qua mot node dang ngu, nen thu tu reset va khoang thoi gian cac node cung thuc van quan trong.

## 10. Chu ky test va chu ky van hanh

Dat giong nhau tren STM32 va ESP32:

```c
#define APP_RELAY_TEST_MODE 1U
```

Test mode hien tai dung moc 5 phut:

```text
hh:00, hh:05, hh:10, hh:15, ...
```

Khong dung chu ky 1 phut vi pipeline co slot node 1002 khoang 60 giay va node 1001 khoang 85 giay.

Khi chay that 4 gio, doi ca hai firmware thanh:

```c
#define APP_RELAY_TEST_MODE 0U
```

Cac moc danh thuc:

```text
00:00, 04:00, 08:00, 12:00, 16:00, 20:00
```

Cac node thuc gan cung luc, nhung gui va ngu khac luc do slot LoRa va nhiem vu relay.

## 11. Scan va doi ID cac sensor RS485

Dia chi mac dinh trong `Core/Src/SoilSensor.c`:

```c
#define SOIL_NPK_SENSOR_ADDR 0x01U
#define SOIL_ENV_SENSOR_ADDR 0x02U
#define SHT_SENSOR_ADDR      0x03U
```

NPK va ENV dung 9600 baud; SHT dung 4800 baud. Thanh ghi dia chi la `0x07D0`.

### Quet dia chi

Bat tool trong `Core/Src/main.c` va tat tat ca co doi ID:

```c
// Core/Src/main.c
#define APP_RS485_ID_TOOL_MODE 1U

// Core/Src/SoilSensor.c
#define SOIL_NPK_CHANGE_ID   0U
#define SOIL_ENV_CHANGE_ID   0U
#define SHT_SENSOR_CHANGE_ID 0U
```

Nap firmware, reset STM32 va theo doi:

```c
soil_scan_done
soil_scan_9600_count
soil_scan_9600_addresses
soil_scan_9600_values
soil_scan_4800_count
soil_scan_4800_addresses
soil_scan_4800_values
```

Khi `soil_scan_done == 1`, mang 9600 baud chua NPK/ENV va mang 4800 baud chua SHT. Tool quet tu `0x01` den `0x0A`.

### Doi ID

Chi noi **mot sensor** vao RS485 khi doi ID. Neu nhieu sensor trung dia chi cu, chung co the cung nhan lenh hoac phan hoi xung dot. Chi bat mot co doi ID tai mot thoi diem.

```c
// NPK: 0x03 -> 0x01
#define SOIL_NPK_CHANGE_ID    1U
#define SOIL_NPK_OLD_ADDRESS 0x03U
#define SOIL_NPK_NEW_ADDRESS 0x01U
#define SOIL_ENV_CHANGE_ID    0U
#define SHT_SENSOR_CHANGE_ID  0U
```

```c
// ENV: 0x01 -> 0x02
#define SOIL_NPK_CHANGE_ID    0U
#define SOIL_ENV_CHANGE_ID    1U
#define SOIL_ENV_OLD_ADDRESS 0x01U
#define SOIL_ENV_NEW_ADDRESS 0x02U
#define SHT_SENSOR_CHANGE_ID  0U
```

```c
// SHT: 0x01 -> 0x03
#define SOIL_NPK_CHANGE_ID      0U
#define SOIL_ENV_CHANGE_ID      0U
#define SHT_SENSOR_CHANGE_ID    1U
#define SHT_SENSOR_OLD_ADDRESS 0x01U
#define SHT_SENSOR_NEW_ADDRESS 0x03U
```

Build, nap va reset STM32. `soil_id_change_status == 0` la thanh cong. Tool se tu scan lai de xac nhan. Theo doi them `soil_id_change_sensor`, `soil_id_change_old_address` va `soil_id_change_new_address`.

Sau khi doi xong, tat tool va tat tat ca co doi ID, roi nap lai firmware:

```c
#define APP_RS485_ID_TOOL_MODE 0U
#define SOIL_NPK_CHANGE_ID     0U
#define SOIL_ENV_CHANGE_ID     0U
#define SHT_SENSOR_CHANGE_ID   0U
```

Neu dung dia chi khac `0x01`, `0x02`, `0x03`, phai cap nhat ca ba macro `*_SENSOR_ADDR` de che do doc binh thuong truy van dung sensor.

## 12. Cac che do tool phai tat khi van hanh

STM32:

```c
#define APP_E22_CONFIG_READ_MODE 0U
#define APP_RS485_ID_TOOL_MODE   0U
#define DS3231_SET_TIME_ON_BOOT  0U
```

ESP32:

```cpp
#define E22_CONFIGURE_ON_BOOT 0U
```

## 13. Debug nhanh

Node 1002 nen theo doi:

```c
deviceId
relay_parent_id
lora_dma_rx_count
lora_packet_count
lora_crc_fail_count
relay_time_sync_count
relay_time_sync_last_epoch
relay_time_sync_last_seq
relay_time_sync_last_status
relay_time_sync_ack_forward_count
relay_forward_count
ack_count
ack_timeout_count
relay_queue_count
relay_cycle_stage
```

`relay_time_sync_last_status`:

```text
0: ghi DS3231 thanh cong
1: epoch/chuyen doi thoi gian khong hop le
2: loi ghi DS3231 qua I2C
```

`Status = 0` trong goi soil data nghia la cac buoc doc cam bien tra ve thanh cong. Gia tri cam bien bang 0 van co the la gia tri thuc hoac can kiem tra lai dau do.

---

# English Guide

## 1. System architecture

Supported relay chains:

```text
2 nodes: 1002 -> 1001 -> ESP32
3 nodes: 1003 -> 1002 -> 1001 -> ESP32
4 nodes: 1004 -> 1003 -> 1002 -> 1001 -> ESP32
```

Time synchronization travels down the chain, and its ACK returns to the ESP32:

```text
ESP32 -> 1001 -> 1002 -> ... -> final node
ESP32 <- 1001 <- 1002 <- ... <- final-node ACK
```

## 2. Hardware connections

### STM32 and ST-Link

| ST-Link | STM32F103 |
|---|---|
| SWDIO | PA13 |
| SWCLK | PA14 |
| GND | GND |
| 3.3V | 3.3V |
| NRST | NRST (recommended) |

Keep `BOOT0 = GND` to boot from Flash.

### DS3231

- Connect `INT/SQW` to `PA0`.
- Add a 4.7 kohm pull-up to 3.3 V.
- Install the RTC backup battery.

### E22 on ESP32

```text
E22 TXD -> ESP32 GPIO16 (RX)
E22 RXD -> ESP32 GPIO17 (TX)
E22 M0  -> ESP32 GPIO4
E22 M1  -> ESP32 GPIO5
E22 AUX -> ESP32 GPIO22
E22 GND -> ESP32 GND
```

Use a stable external supply for the E22-400T30D. A 30 dBm module should not be powered from a weak ESP32 3.3 V rail.

## 3. Configure all E22 modules

All modules must use matching settings:

```text
Model/band      : E22-400T30D / 400 MHz
UART            : 9600, 8N1
Air rate        : 2.4 kbps
Channel         : 25 (435.125 MHz)
NET ID          : 0
Address         : 200 (0x00C8)
Packet size     : 240 bytes
Power           : 30 dBm
Transmission    : Normal/Transparent
Relay/LBT/RSSI  : Disabled
WOR cycle       : 2000 ms
Key             : 0
```

Raw configuration:

```text
00 C8 00 62 00 19 03 00 00
```

Use `RF_Setting(E22-E9X(SL)) V3.5(EN).exe`. Click **Set Param**, then **Get** and verify the values.

## 4. Select the chain length

Set the same value in `Core/Src/main.c` and `lora_recv/lora_recv.ino`:

```c
#define RELAY_CHAIN_NODE_COUNT 2U
```

```text
2U: 1002 -> 1001 -> ESP32
3U: 1003 -> 1002 -> 1001 -> ESP32
4U: 1004 -> 1003 -> 1002 -> 1001 -> ESP32
```

Both firmware images must use the same chain length.

## 5. Assign each STM32 node ID

The ID is stored in Flash at `0x0800FC00`. Configure each board separately.

Edit `Core/Inc/flash.h`:

```c
#define FLASH_DEFAULT_DEVICE_ID 1001UL
#define FLASH_FORCE_DEFAULT_DEVICE_ID_ON_BOOT 1U
```

Valid IDs are `1001`, `1002`, `1003`, and `1004`. Build, flash, and reset the board once. Then disable forced writing:

```c
#define FLASH_FORCE_DEFAULT_DEVICE_ID_ON_BOOT 0U
```

Build and flash the normal firmware again. Verify with Live Expressions:

```c
deviceId
relay_parent_id
```

Do not leave force mode enabled, because every reset would overwrite the stored node ID.

## 6. Flash STM32 firmware

1. Open the project in STM32CubeIDE.
2. Connect ST-Link through SWD.
3. Select **Project > Build Project**.
4. Select **Run As > STM32 C/C++ Application**.
5. Wait for `Download verified successfully`.
6. Reset the STM32.

All STM32 boards use the same relay logic, but each board must retain its own ID.

## 7. Flash ESP32 firmware

Open `lora_recv/lora_recv.ino` in Arduino IDE and select:

```text
Board            : DOIT ESP32 DEVKIT V1
Port             : the ESP32 COM port
Upload Speed     : 115200 when upload stability is poor
Flash Mode       : DIO
Flash Frequency  : 40 MHz
```

Configure the system Wi-Fi and Firebase values in the source, then upload. If it remains at `Connecting...`, hold BOOT until `Writing at...` starts.

Use `115200 baud` for the ESP32 Serial Monitor. The E22 UART runs at `9600 baud`.

## 8. Set DS3231 time manually (optional)

Manual initialization is normally unnecessary because the ESP32 obtains NTP time and forwards it to the nodes.

To initialize an RTC manually, set in `Core/Src/main.c`:

```c
#define DS3231_SET_TIME_ON_BOOT 1U
```

Update the `DS3231_DateTime_t` structure, build, and flash. After the RTC is written, restore:

```c
#define DS3231_SET_TIME_ON_BOOT 0U
```

Build and flash again. Leaving it enabled writes the fixed source time after every reset.

## 9. First-time NTP synchronization

Recommended startup/reset procedure:

1. Keep `DS3231_SET_TIME_ON_BOOT = 0U`.
2. Start or reset the ESP32 and wait for `NTP sync requested`.
3. Reset the node farthest from the ESP32 first.
4. Reset the remaining nodes toward the ESP32, about 1-3 seconds apart.
5. Wait for:

```text
TIME_SYNC END-TO-END ACK via node=1001
```

Reset order:

```text
2 nodes: 1002 -> 1001
3 nodes: 1003 -> 1002 -> 1001
4 nodes: 1004 -> 1003 -> 1002 -> 1001
```

Do not reset individual nodes too far apart. Time sync is not stored in the relay data queue: if a near node receives time sync while its next node is asleep, the packet cannot cross the complete chain and the ESP32 receives no end-to-end ACK. Sensor data may still arrive later because it uses a separate queue/retry path; receiving sensor data does not replace the time-sync ACK.

After an STM32 reset, the node remains awake long enough to power the E22 and sensors, read data, receive time sync, update the DS3231, forward packets, and wait for ACKs before entering STOP mode.

`via node=1001` means node 1001 delivered the final ACK hop to the ESP32. The end-to-end ACK confirms that time sync reached the final configured node and the RTC updates along the path succeeded.

The final node transmits each time-sync ACK three times at 500 ms intervals. Intermediate nodes forward each ACK toward the ESP32, while the ESP32 accepts and logs only the first ACK for the same `seq`. This is more reliable than the previous single-ACK implementation because one or two lost ACK packets can be tolerated. Repetition still cannot cross a sleeping relay, so reset order and overlapping awake windows remain important.

## 10. Test and production schedules

Use the same setting on STM32 and ESP32:

```c
#define APP_RELAY_TEST_MODE 1U
```

Test mode uses aligned 5-minute boundaries:

```text
hh:00, hh:05, hh:10, hh:15, ...
```

Do not use a one-minute test period because the current relay pipeline includes approximately 60-second and 85-second transmit slots.

For the four-hour production schedule, set both firmware images to:

```c
#define APP_RELAY_TEST_MODE 0U
```

Wake boundaries become:

```text
00:00, 04:00, 08:00, 12:00, 16:00, 20:00
```

Nodes wake near the same boundary but transmit and return to sleep at different times because they use separate LoRa slots and relay duties.

## 11. Scan and change RS485 sensor IDs

Default addresses in `Core/Src/SoilSensor.c`:

```c
#define SOIL_NPK_SENSOR_ADDR 0x01U
#define SOIL_ENV_SENSOR_ADDR 0x02U
#define SHT_SENSOR_ADDR      0x03U
```

NPK and ENV use 9600 baud; SHT uses 4800 baud. The address register is `0x07D0`.

### Scan addresses

Enable the tool in `Core/Src/main.c` and disable every change flag:

```c
// Core/Src/main.c
#define APP_RS485_ID_TOOL_MODE 1U

// Core/Src/SoilSensor.c
#define SOIL_NPK_CHANGE_ID   0U
#define SOIL_ENV_CHANGE_ID   0U
#define SHT_SENSOR_CHANGE_ID 0U
```

Flash the firmware, reset the STM32, and monitor:

```c
soil_scan_done
soil_scan_9600_count
soil_scan_9600_addresses
soil_scan_9600_values
soil_scan_4800_count
soil_scan_4800_addresses
soil_scan_4800_values
```

When `soil_scan_done == 1`, the 9600-baud array contains NPK/ENV results and the 4800-baud array contains SHT results. The tool scans `0x01` through `0x0A`.

### Change an ID

Connect only **one sensor** to RS485 while changing its ID. Sensors sharing the old address may all accept the command or cause response collisions. Enable exactly one change flag at a time.

```c
// NPK: 0x03 -> 0x01
#define SOIL_NPK_CHANGE_ID    1U
#define SOIL_NPK_OLD_ADDRESS 0x03U
#define SOIL_NPK_NEW_ADDRESS 0x01U
#define SOIL_ENV_CHANGE_ID    0U
#define SHT_SENSOR_CHANGE_ID  0U
```

```c
// ENV: 0x01 -> 0x02
#define SOIL_NPK_CHANGE_ID    0U
#define SOIL_ENV_CHANGE_ID    1U
#define SOIL_ENV_OLD_ADDRESS 0x01U
#define SOIL_ENV_NEW_ADDRESS 0x02U
#define SHT_SENSOR_CHANGE_ID  0U
```

```c
// SHT: 0x01 -> 0x03
#define SOIL_NPK_CHANGE_ID      0U
#define SOIL_ENV_CHANGE_ID      0U
#define SHT_SENSOR_CHANGE_ID    1U
#define SHT_SENSOR_OLD_ADDRESS 0x01U
#define SHT_SENSOR_NEW_ADDRESS 0x03U
```

Build, flash, and reset the STM32. `soil_id_change_status == 0` means success. The tool scans again for verification. Also monitor `soil_id_change_sensor`, `soil_id_change_old_address`, and `soil_id_change_new_address`.

Afterward, disable the tool and all change flags, then flash the normal firmware again:

```c
#define APP_RS485_ID_TOOL_MODE 0U
#define SOIL_NPK_CHANGE_ID     0U
#define SOIL_ENV_CHANGE_ID     0U
#define SHT_SENSOR_CHANGE_ID   0U
```

When using addresses other than `0x01`, `0x02`, and `0x03`, also update all three `*_SENSOR_ADDR` macros so normal acquisition queries the correct sensors.

## 12. Tool modes that must be disabled in production

STM32:

```c
#define APP_E22_CONFIG_READ_MODE 0U
#define APP_RS485_ID_TOOL_MODE   0U
#define DS3231_SET_TIME_ON_BOOT  0U
```

ESP32:

```cpp
#define E22_CONFIGURE_ON_BOOT 0U
```

## 13. Quick diagnostics

Useful STM32 Live Expressions:

```c
deviceId
relay_parent_id
lora_dma_rx_count
lora_packet_count
lora_crc_fail_count
relay_time_sync_count
relay_time_sync_last_epoch
relay_time_sync_last_seq
relay_time_sync_last_status
relay_time_sync_ack_forward_count
relay_forward_count
ack_count
ack_timeout_count
relay_queue_count
relay_cycle_stage
```

`relay_time_sync_last_status` values:

```text
0: DS3231 update succeeded
1: invalid epoch/time conversion
2: DS3231 I2C write failed
```

A soil packet `Status = 0` means the sensor read operations returned successfully. A zero measurement may still be a real sensor value or indicate that the probe requires further checking.
