Community project

Giám Sát Mực Nước AWD

ESP32
Photo of Giám Sát Mực Nước AWD
Generated with AI

Naruto Uzumaki

Published October 5, 2026

The AWD Water Level Monitoring Station is a solar-powered remote sensor system built around an ESP32 that tracks water depth, soil moisture, and temperature using LoRa wireless transmission. The station combines an AJ-SR04M waterproof ultrasonic sensor for water level measurement, a capacitive soil moisture sensor, and a DS18B20 temperature sensor to gather environmental data from hard-to-reach locations.

This guide provides a complete parts list, wiring diagram, and step-by-step assembly instructions for building a weatherproof monitoring station. The included firmware handles sensor calibration, multi-sample averaging, LoRa transmission on the 868 MHz band, and deep sleep power management to extend battery life. Builders will learn how to integrate solar charging, configure the ultrasonic distance sensor for water depth calculation, and deploy a low-power IoT device for long-term environmental monitoring.

Wiring diagram

Wiring diagram for Giám Sát Mực Nước AWD

Gather all the parts

QtyComponent
1

18650 Li-ion Cell

3.7 V 2500 mAh

18650 lithium-ion cell, nominal 3.7 V, ~2500 mAh. Common for higher-capacity portable / battery-bank style projects; needs a holder and protection / charger circuit.

1

6 V Solar Panel

6 V, 3 W

Tấm pin mặt trời nạp lại pin của trạm khi đặt ngoài ruộng.

1

CN3065 Solar Li-ion Charger Module

CN3065

Mạch sạc pin lithium một cell từ tấm pin mặt trời.

1

5 V Boost Converter Module

5 V, 1 A

Mạch nâng điện áp pin lithium thành 5 V ổn định cho ESP32 và cảm biến.

1

Ống PVC AWD đục lỗ

PVC Ø90 mm, dài 1 m

Ống nhựa đứng bảo vệ đầu dò và cho nước ruộng đi vào để đo mực nước ổn định.

1

Hộp nhựa ABS kín nước

ABS IP65, 200 × 150 × 100 mm

Hộp nhựa bảo vệ ESP32, radio LoRa, bộ sạc và các đầu nối khỏi mưa và bùn.

1

AJ-SR04M Waterproof Ultrasonic Sensor

A waterproof ultrasonic sensor that measures the distance from the top of the AWD tube to the water surface.

1

Ai-Thinker Ra-01H LoRa Module

A 3.3 V long-range LoRa radio module for transmitting each field measurement to a compatible base station.

1

DS18B20

Digital temperature sensor using OneWire protocol

1

4.7 kΩ pull-up resistor

A small resistor that keeps the DS18B20 data wire at a readable idle level.

1

Gravity: IP65 Capacitive Soil Moisture Sensor

Capacitive soil moisture sensor with IP65 waterproof and corrosion-resistant construction. Compatible with Arduino, ESP32, and Raspberry Pi.

Assemble it in 6 steps

1. Lắp ống đo mực nước

Dùng pvc_awdtube là ống PVC Ø90 mm dài khoảng 1 m. Khoan các lỗ nhỏ ở nửa dưới để nước ruộng đi vào ống, nhưng không khoan gần đầu dò. Cắm ống thẳng đứng xuống nền ruộng, để miệng ống cao hơn mực nước cao nhất dự kiến. Gắn đầu dò kín nước của water_level_sensor ở nắp ống, mặt đầu dò hướng thẳng xuống nước.

  • Đặt ống tại vị trí đại diện cho ruộng, tránh sát bờ hoặc chỗ nước chảy mạnh.
  • Đo khoảng cách từ mặt đầu dò đến mặt đất trong ống. Firmware đang dùng 30 cm; sửa hằng SENSOR_TO_SOIL_CM nếu số đo thực tế khác.
  • Không để bùn, rơm hoặc lá bịt các lỗ của ống; nước vào ống chậm sẽ làm mực nước đo được không đúng.

2. Nối đầu dò mực nước

Đưa cáp của water_level_sensor vào abs_enclosure qua đầu siết cáp. Nối VCC → 3V3 (nguồn), GND → GND (ground), TRIG → GPIO25 (ra lệnh đo), ECHO → GPIO26 (tín hiệu phản hồi). AJ-SR04M được cấp 3,3 V nên ECHO cũng an toàn cho ESP32, không dùng mạch điện trở hạ áp.

  • Để AJ-SR04M ở chế độ xung mặc định để firmware hiện tại đo bằng chân TRIG và ECHO.
  • Bọc mối nối bằng ống co nhiệt trước khi cho vào hộp.
  • Không cấp 5 V cho AJ-SR04M khi ECHO được nối trực tiếp với GPIO26; ECHO 5 V có thể làm hỏng ESP32.

3. Lắp cảm biến nhiệt độ và ẩm đất

Đặt temperature_sensor ở vị trí cần theo dõi nhiệt độ, tránh để dây bị dập. Nối VCC → 3V3 (nguồn), GND → GND (ground), DATA → GPIO27 (tín hiệu). Nối temperature_pullup: END_A → 3V3 (nguồn), END_B → chân DATA của temperature_sensor (giữ tín hiệu ổn định). Cắm đầu dò soil_moisture_sensor vào đất tại vùng rễ: VCC → 3V3 (nguồn), GND → GND (ground), AOUT → GPIO34 (tín hiệu đo độ ẩm).

  • Cảm biến điện dung chịu ruộng tốt hơn loại hai thanh kim loại, nhưng vẫn cần hiệu chuẩn một mẫu đất khô và một mẫu đất bão hòa.
  • Giữ phần mạch điện tử của cảm biến ẩm đất ở trên mặt đất và che mưa; chỉ cắm phần đầu dò vào đất.
  • Không nhúng mối nối hoặc phần mạch của cảm biến vào nước ruộng; nước có thể gây chập và làm số liệu sai.

4. Nối mô-đun LoRa Ra-01H

Đặt lora_radio trong abs_enclosure. Nối 3.3V → 3V3 (nguồn), GND → GND (ground), SCK → GPIO18 (xung), MISO → GPIO19 (dữ liệu về), MOSI → GPIO23 (dữ liệu đi), NSS → GPIO4 (chọn mô-đun), RESET → GPIO32 (khởi động lại), DIO0 → GPIO33 (báo trạng thái). Hàn hoặc gắn đúng anten cho dải 868 MHz vào chân ANT trước khi cấp nguồn.

  • Ra-01H hoạt động trong dải 803–930 MHz; firmware đặt 868 MHz. Bộ thu phải dùng cùng tần số, thông số LoRa và anten 868 MHz.
  • Đặt anten thẳng đứng, cách dây nguồn và tấm pin mặt trời ít nhất 10 cm.
  • Chỉ cấp 3,3 V cho Ra-01H; cấp 5 V có thể làm hỏng mô-đun.
  • Không phát khi chưa gắn anten; điều đó có thể làm hỏng phần phát vô tuyến.
  • Hãy xác nhận 868 MHz được phép sử dụng tại địa phương trước khi triển khai hàng loạt.

5. Lắp nguồn năng lượng mặt trời

Trong abs_enclosure, nối solar_panel: PV+ → VIN của solar_charger (nguồn), PV− → VSS của solar_charger (ground). Nối solar_charger: VBAT → +V của battery_18650 (sạc pin), GND → GND của battery_18650 (ground). Nối battery_18650: +V → IN+ của boost_converter (nguồn), GND → IN− của boost_converter (ground). Nối boost_converter: OUT+ → VIN/5V của ESP32 (nguồn), OUT− → GND của ESP32 (ground).

  • Cố định pin để không lăn, không bị vật kim loại chạm cả hai đầu.
  • Đặt tấm pin ở nơi ít bị lúa hoặc cây che bóng.
  • Trước khi nối ESP32, dùng đồng hồ đo chỉnh OUT+ của boost_converter đúng 5,0 V; điện áp cao hơn có thể làm hỏng bo mạch.
  • Không hàn trực tiếp vào viên pin 18650 rời; dùng khay pin hoặc pack pin có mạch bảo vệ.

6. Đóng hộp và đưa trạm vào hoạt động

Đặt ESP32, solar_charger, boost_converter và lora_radio trong abs_enclosure. Siết chặt đầu siết cáp, bọc mọi mối nối hở và đóng nắp hộp. Gắn hộp cao hơn mặt nước, nghiêng nhẹ để nước mưa không đọng trên nắp. Dán mã trạm ở ngoài hộp để ghép đúng bản ghi LoRa với đúng thửa ruộng.

  • Trạm đo, gửi gói dữ liệu rồi ngủ 15 phút để giảm hao pin.
  • Hiệu chuẩn SOIL_DRY_RAW và SOIL_WET_RAW trong firmware từ mẫu đất thực tế trước khi nhân rộng.
  • Không đóng hộp khi còn dây trần hoặc chỗ nối hở; hơi ẩm có thể gây chập mạch và làm trạm dừng hoạt động.

Review all connections

1. Connections between "solar_panel" and "ESP32"

Functionsolar_panelESP32
powerPV+ → CN3065 Solar Li-ion Charger Module VINEXT
groundPV- → CN3065 Solar Li-ion Charger Module VSSEXT

2. Connections between "solar_charger" and "ESP32"

Functionsolar_chargerESP32
powerVBAT → 18650 Li-ion Cell +VEXT
groundGND → 18650 Li-ion Cell GNDEXT

3. Connections between "boost_converter" and "ESP32"

Functionboost_converterESP32
powerIN+ → 18650 Li-ion Cell +VEXT
groundIN- → 18650 Li-ion Cell GNDEXT
powerOUT+VIN
groundOUT-GND

4. Connections between "water_level_sensor" and "ESP32"

Functionwater_level_sensorESP32
powerVCC3V3
groundGNDGND
digitalTRIGGPIO 14
digitalECHOGPIO 21

5. Connections between "lora_radio" and "ESP32"

Functionlora_radioESP32
power3.3V3V3
groundGNDGND
dataANT → Ăng-ten 868 MHz phù hợp Ra-01HEXT
spiMOSIGPIO 11
spiMISOGPIO 13
spiSCKGPIO 12
spiNSSGPIO 10
digitalRESETGPIO 38
digitalDIO0GPIO 39

6. Connections between "temperature_sensor" and "ESP32"

Functiontemperature_sensorESP32
powerVCC3V3
groundGNDGND
dataDATAGPIO 4

7. Connections between "soil_moisture_sensor" and "ESP32"

Functionsoil_moisture_sensorESP32
powerVCC3V3
groundGNDGND
analogAOUTGPIO 1

8. Connections between "temperature_pullup" and "ESP32"

Functiontemperature_pullupESP32
powerEND_A3V3
dataEND_B → DS18B20 DATAEXT

Deploy the firmware

#include <Arduino.h>
#include <SPI.h>
#include <LoRa.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <esp_sleep.h>

// Forward declarations
float readDistanceCm();
int readSoilRaw();
int soilPercentFromRaw(int raw);
float readTemperatureC();
bool sendMeasurement(float waterDepthCm, float distanceCm, float temperatureC,
                     int soilRaw, int soilPercent);

constexpr int TRIG_PIN = 14;
constexpr int ECHO_PIN = 21;
constexpr int LORA_SCK_PIN = 12;
constexpr int LORA_MISO_PIN = 13;
constexpr int LORA_MOSI_PIN = 11;
constexpr int LORA_NSS_PIN = 10;
constexpr int LORA_RESET_PIN = 38;
constexpr int LORA_DIO0_PIN = 39;
constexpr int DS18B20_PIN = 4;
constexpr int SOIL_MOISTURE_PIN = 1;

constexpr long LORA_FREQUENCY_HZ = 868000000L;
constexpr float SENSOR_TO_SOIL_CM = 30.0f;
constexpr uint64_t SLEEP_MINUTES = 15;
constexpr uint8_t SENSOR_SAMPLES = 5;
constexpr uint8_t SOIL_SAMPLES = 10;
// Calibrate these values with one installed sensor in dry and saturated soil.
constexpr int SOIL_DRY_RAW = 3000;
constexpr int SOIL_WET_RAW = 1400;

OneWire oneWire(DS18B20_PIN);
DallasTemperature ds18b20(&oneWire);

float readDistanceCm() {
  float total = 0.0f;
  uint8_t validSamples = 0;

  for (uint8_t i = 0; i < SENSOR_SAMPLES; ++i) {
    digitalWrite(TRIG_PIN, LOW);
    delayMicroseconds(3);
    digitalWrite(TRIG_PIN, HIGH);
    delayMicroseconds(10);
    digitalWrite(TRIG_PIN, LOW);

    const unsigned long duration = pulseIn(ECHO_PIN, HIGH, 30000);
    if (duration > 0) {
      const float distanceCm = duration * 0.0343f / 2.0f;
      if (distanceCm >= 2.0f && distanceCm <= 400.0f) {
        total += distanceCm;
        ++validSamples;
      }
    }
    delay(60);
  }

  return validSamples ? total / validSamples : -1.0f;
}

int readSoilRaw() {
  uint32_t total = 0;
  for (uint8_t i = 0; i < SOIL_SAMPLES; ++i) {
    total += analogRead(SOIL_MOISTURE_PIN);
    delay(10);
  }
  return total / SOIL_SAMPLES;
}

int soilPercentFromRaw(int raw) {
  return constrain(map(raw, SOIL_DRY_RAW, SOIL_WET_RAW, 0, 100), 0, 100);
}

float readTemperatureC() {
  ds18b20.requestTemperatures();
  const float temperatureC = ds18b20.getTempCByIndex(0);
  return temperatureC == DEVICE_DISCONNECTED_C ? -127.0f : temperatureC;
}

bool sendMeasurement(float waterDepthCm, float distanceCm, float temperatureC,
                     int soilRaw, int soilPercent) {
  SPI.begin(LORA_SCK_PIN, LORA_MISO_PIN, LORA_MOSI_PIN, LORA_NSS_PIN);
  LoRa.setPins(LORA_NSS_PIN, LORA_RESET_PIN, LORA_DIO0_PIN);

  if (!LoRa.begin(LORA_FREQUENCY_HZ)) {
    Serial.println("LoRa radio not detected");
    return false;
  }

  LoRa.setTxPower(17);
  LoRa.beginPacket();
  LoRa.print("AWD,depth_cm=");
  LoRa.print(waterDepthCm, 1);
  LoRa.print(",distance_cm=");
  LoRa.print(distanceCm, 1);
  LoRa.print(",temp_c=");
  LoRa.print(temperatureC, 1);
  LoRa.print(",soil_raw=");
  LoRa.print(soilRaw);
  LoRa.print(",soil_pct=");
  LoRa.print(soilPercent);
  const bool sent = LoRa.endPacket() == 1;
  LoRa.sleep();
  return sent;
}

void setup() {
  Serial.begin(115200);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  pinMode(SOIL_MOISTURE_PIN, INPUT);
  analogSetPinAttenuation(SOIL_MOISTURE_PIN, ADC_11db);
  digitalWrite(TRIG_PIN, LOW);
  delay(250);

  ds18b20.begin();
  const float distanceCm = readDistanceCm();
  float waterDepthCm = -1.0f;
  if (distanceCm >= 0.0f) {
    waterDepthCm = SENSOR_TO_SOIL_CM - distanceCm;
    if (waterDepthCm < 0.0f) waterDepthCm = 0.0f;
  }

  const float temperatureC = readTemperatureC();
  const int soilRaw = readSoilRaw();
  const int soilPercent = soilPercentFromRaw(soilRaw);
  const bool sent = sendMeasurement(waterDepthCm, distanceCm, temperatureC,
                                    soilRaw, soilPercent);
  Serial.printf("Depth %.1f cm, temperature %.1f C, soil raw %d, soil %d%%, sent %s\n",
                waterDepthCm, temperatureC, soilRaw, soilPercent,
                sent ? "yes" : "no");

  esp_sleep_enable_timer_wakeup(SLEEP_MINUTES * 60ULL * 1000000ULL);
  delay(100);
  esp_deep_sleep_start();
}

void loop() {
  // The ESP32 restarts from setup() after each timed deep-sleep wake-up.
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik