สร้างสถานีตรวจวัดอากาศ ESP32 แบบครบวงจร: วัดอุณหภูมิ ความชื้น ความดัน ฝน และความเร็วลม
เรียนรู้วิธีสร้างสถานีตรวจวัดอากาศ IoT ด้วย ESP32 และเซนเซอร์หลากหลายประเภท พร้อมระบบบันทึกข้อมูล SD Card และ Dashboard แบบ Real-time
📑 สารบัญ
🌤️ ภาพรวมโปรเจกต์
สถานีตรวจวัดอากาศแบบ DIY เป็นโปรเจกต์ IoT ที่ได้รับความนิยมอย่างมากในปี 2026 ด้วย ESP32 ที่มีความสามารถด้านการเชื่อมต่อ Wi-Fi และ Bluetooth ทำให้เราสามารถสร้างสถานีตรวจวัดอากาศที่ส่งข้อมูลแบบ Real-time ได้อย่างง่ายดายและประหยัด
✨ จุดเด่นของโปรเจกต์นี้:
- วัดอุณหภูมิ ความชื้อ และความดันอากาศด้วย BME280
- ตรวจจับฝนด้วย Rain Sensor Module
- วัดความเร็วลมด้วย Anemometer (เสริม)
- บันทึกข้อมูลลง SD Card แบบต่อเนื่อง
- แสดงผลบน Web Dashboard แบบ Real-time
- เชื่อมต่อกับ Home Assistant ผ่าน ESPHome
- ใช้งานได้ทั้งแบบ Standalone และ Cloud-connected
🎯 เหมาะสำหรับ:
- เกษตรกรที่ต้องการติดตามสภาพอากาศในไร่นา
- คนรักการทำสวนที่ต้องการเฝ้าสภาพอากาศ
- นักพัฒนา IoT ที่ต้องการเรียนรู้การทำงานกับเซนเซอร์หลายตัว
- ผู้ที่สนใจ Smart Home และ Home Automation
🔧 อุปกรณ์ที่ต้องการ
| อุปกรณ์ | จำนวน | ราคาโดยประมาณ | หมายเหตุ |
|---|---|---|---|
| ESP32 Development Board | 1 | 150-250 บาท | ESP32 DevKitC หรือ ESP32-WROOM |
| BME280 Sensor | 1 | 80-150 บาท | วัดอุณหภูมิ ความชื้อ ความดัน |
| Rain Sensor Module | 1 | 30-60 บาท | ตรวจจับน้ำฝน |
| Micro SD Card Module | 1 | 40-80 บาท | พร้อม SD Card 4GB+ |
| OLED Display 0.96" (Optional) | 1 | 60-100 บาท | แสดงผลข้อมูลในตัวเครื่อง |
| Anemometer (Optional) | 1 | 200-500 บาท | วัดความเร็วลม |
| Jumper Wires | 1 ชุด | 30-50 บาท | Male-to-Female แนะนำ |
| Breadboard | 1 | 30-50 บาท | ขนาด 400-800 pins |
| USB Cable | 1 | 20-40 บาท | Micro USB สำหรับ ESP32 |
💡 คำแนะนำ:
ใช้ BME280 แทน DHT11/DHT22 เพราะ:
- ความแม่นยำสูงกว่ามาก
- วัดได้ทั้งอุณหภูมิ ความชื้อ และความดันในตัวเดียว
- ใช้พลังงานต่ำกว่า
- สื่อสารผ่าน I2C ง่ายต่อการเชื่อมต่อ
⚡ การเชื่อมต่อวงจร
BME280 Sensor (I2C)
| BME280 Pin | ESP32 Pin |
|---|---|
| VIN (3.3V) | 3.3V |
| GND | GND |
| SCL | GPIO 22 (I2C SCL) |
| SDA | GPIO 21 (I2C SDA) |
Rain Sensor Module
| Rain Sensor Pin | ESP32 Pin |
|---|---|
| VCC | 5V (หรือ 3.3V ตาม Module) |
| GND | GND |
| AO (Analog Out) | GPIO 34 (ADC1_CH6) |
Micro SD Card Module (SPI)
| SD Card Module | ESP32 Pin |
|---|---|
| CS | GPIO 5 |
| SCK | GPIO 18 |
| MOSI | GPIO 23 |
| MISO | GPIO 19 |
| VCC | 5V หรือ 3.3V |
⚠️ ข้อควรระวัง:
- ตรวจสอบระดับแรงดันของ Rain Sensor ก่อนเชื่อมต่อ (บางตัวใช้ 5V)
- GPIO 34 เป็น Input Only ไม่สามารถใช้เป็น Output ได้
- SD Card Module บางตัวต้องใช้ 3.3V เท่านั้น
- ใช้ Resistor 10KΩ Pull-up สำหรับ I2C ถ้า BME280 ไม่มี Built-in
💻 โค้ด Arduino
ก่อนเริ่มใช้งาน ติดตั้ง Library ที่จำเป็นก่อน:
- Adafruit BME280 Library - สำหรับ BME280
- Adafruit Unified Sensor - Sensor abstraction layer
- SD Library - สำหรับ SD Card (มีในตัว)
/*
* ESP32 Weather Station - สถานีตรวจวัดอากาศ
* วัดอุณหภูมิ ความชื้อ ความดัน ฝน และบันทึกข้อมูล
*
* Hardware: ESP32 + BME280 + Rain Sensor + SD Card
* Author: CynoIoT
* Updated: March 2026
*/
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Adafruit_Sensor.h>
#include "FS.h"
#include "SD.h"
#include "SPI.h"
// --- Pin Definitions ---
#define BME280_SDA 21
#define BME280_SCL 22
#define RAIN_SENSOR_PIN 34 // ADC1_CH6
#define SD_CS_PIN 5
#define SD_SCK_PIN 18
#define SD_MISO_PIN 19
#define SD_MOSI_PIN 23
// --- Sensor Objects ---
Adafruit_BME280 bme;
// --- Variables ---
float temperature = 0.0;
float humidity = 0.0;
float pressure = 0.0;
int rainValue = 0;
bool isRaining = false;
unsigned long lastReadTime = 0;
const long readInterval = 5000; // อ่านค่าทุก 5 วินาที
unsigned long lastSDWriteTime = 0;
const long sdWriteInterval = 60000; // เขียนลง SD ทุก 1 นาที
void setup() {
Serial.begin(115200);
// แสดงข้อความเริ่มต้น
Serial.println("========================================");
Serial.println("ESP32 Weather Station Starting...");
Serial.println("========================================");
// เริ่มต้น I2C สำหรับ BME280
Wire.begin(BME280_SDA, BME280_SCL);
// เริ่มต้น BME280
if (!bme.begin(0x76)) { // ลองที่อยู่ 0x76 ก่อน
if (!bme.begin(0x77)) { // ถ้าไม่ได้ลอง 0x77
Serial.println("❌ ไม่พบ BME280! ตรวจสอบการเชื่อมต่อ");
while (1);
}
}
Serial.println("✅ BME280 เริ่มทำงานแล้ว");
// เริ่มต้น SD Card
SPI.begin(SD_SCK_PIN, SD_MISO_PIN, SD_MOSI_PIN, SD_CS_PIN);
if (!SD.begin(SD_CS_PIN)) {
Serial.println("❌ การ์ด SD ล้มเหลว หรือไม่ได้ใส่การ์ด");
} else {
Serial.println("✅ SD Card เริ่มทำงานแล้ว");
createDataFile();
}
// แสดงข้อความเริ่มใช้งาน
Serial.println("🌤️ สถานีตรวจวัดอากาศพร้อมใช้งาน!");
Serial.println("========================================\n");
}
void loop() {
unsigned long currentTime = millis();
// อ่านค่าเซนเซอร์ทุก 5 วินาที
if (currentTime - lastReadTime >= readInterval) {
lastReadTime = currentTime;
readSensors();
displayData();
}
// เขียนข้อมูลลง SD Card ทุก 1 นาที
if (currentTime - lastSDWriteTime >= sdWriteInterval) {
lastSDWriteTime = currentTime;
logToSD();
}
}
// อ่านค่าจากเซนเซอร์ทั้งหมด
void readSensors() {
// อ่าน BME280
temperature = bme.readTemperature();
humidity = bme.readHumidity();
pressure = bme.readPressure() / 100.0F; // แปลงเป็น hPa
// อ่าน Rain Sensor
rainValue = analogRead(RAIN_SENSOR_PIN);
// ตรวจสอบว่าฝนตกหรือไม่ (ปรับค่า Threshold ตามทดลอง)
// ค่ามาก = แห้ง, ค่าน้อย = มีน้ำ
isRaining = (rainValue < 2000); // ปรับค่านี้ตามทดลองจริง
}
// แสดงข้อมูลบน Serial Monitor
void displayData() {
Serial.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
Serial.printf("🕐 เวลา: %lu ms จากการเริ่มเครื่อง\n", millis());
Serial.printf("🌡️ อุณหภูมิ: %.2f °C\n", temperature);
Serial.printf("💧 ความชื้อ: %.2f %%\n", humidity);
Serial.printf("📊 ความดัน: %.2f hPa\n", pressure);
Serial.printf("🌧️ Rain Sensor: %d", rainValue);
if (isRaining) {
Serial.print(" (ฝนตก! 🌧️)");
} else {
Serial.print(" (ไม่มีฝน ☀️)");
}
Serial.println();
Serial.printf("📈 Heat Index: %.2f °C\n", calculateHeatIndex());
Serial.printf("💨 Dew Point: %.2f °C\n", calculateDewPoint());
Serial.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
}
// คำนวณ Heat Index (ดัชนีความร้อน)
float calculateHeatIndex() {
// สูตรคำนวณ Heat Index แบบง่าย
float T = temperature;
float RH = humidity;
// Rothfusz regression
float HI = -8.78469475556 +
1.61139411 * T +
2.33854883889 * RH -
0.14611605 * T * RH -
0.012308094 * T * T -
0.0164248277778 * RH * RH +
0.002211732 * T * T * RH +
0.00072546 * T * RH * RH -
0.000003582 * T * T * RH * RH;
return HI;
}
// คำนวณ Dew Point (จุดน้ำค้าง)
float calculateDewPoint() {
float T = temperature;
float RH = humidity;
// Magnus formula
float a = 17.27;
float b = 237.7;
float alpha = ((a * T) / (b + T)) + log(RH / 100.0);
float dewPoint = (b * alpha) / (a - alpha);
return dewPoint;
}
// สร้างไฟล์ข้อมูลใน SD Card
void createDataFile() {
File dataFile = SD.open("/weather_data.csv", FILE_WRITE);
if (dataFile) {
// เขียน Header ถ้าไฟล์ว่างเปล่า
if (dataFile.size() == 0) {
dataFile.println("timestamp,temperature,humidity,pressure,rain_value,is_raining,heat_index,dew_point");
Serial.println("📝 สร้างไฟล์ weather_data.csv แล้ว");
}
dataFile.close();
}
}
// เขียนข้อมูลลง SD Card
void logToSD() {
File dataFile = SD.open("/weather_data.csv", FILE_APPEND);
if (dataFile) {
// สร้าง timestamp แบบง่าย
unsigned long timestamp = millis() / 1000; // วินาที
// เขียนข้อมูลแบบ CSV
dataFile.printf("%lu,%.2f,%.2f,%.2f,%d,%d,%.2f,%.2f\n",
timestamp,
temperature,
humidity,
pressure,
rainValue,
isRaining ? 1 : 0,
calculateHeatIndex(),
calculateDewPoint());
dataFile.close();
Serial.println("💾 บันทึกข้อมูลลง SD Card แล้ว");
} else {
Serial.println("❌ ไม่สามารถเปิดไฟล์สำหรับเขียนข้อมูล");
}
}💡 เคล็ดลับการใช้งาน:
- ปรับค่า
rainValue < 2000ตามการทดสอบจริง - ใช้ Serial Plotter (Ctrl+Shift+L) เพื่อดูกราฟแบบ Real-time
- ข้อมูลจะถูกบันทึกเป็นไฟล์ CSV สามารถเปิดด้วย Excel ได้
- เพิ่ม NTP Client เพื่อใช้เวลาจริงแทน millis()
📊 สร้าง Web Dashboard
ตอนนี้เราจะเพิ่ม Web Server เพื่อแสดงผลข้อมูลแบบ Real-time บน Browser
// เพิ่มส่วนนี้ไว้ด้านบนสุดของโค้ด
#include <WiFi.h>
#include <WebServer.h>
// แก้ไข WiFi settings
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
WebServer server(80);
// เพิ่มใน setup()
void setup() {
// ... โค้ดเดิม ...
// เชื่อมต่อ WiFi
WiFi.begin(ssid, password);
Serial.print("📡 กำลังเชื่อมต่อ WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\n✅ เชื่อมต่อ WiFi แล้ว!");
Serial.printf("🌐 IP Address: %s\n", WiFi.localIP().toString().c_str());
Serial.println("🔗 เปิด Browser ไปที่: http://" + WiFi.localIP().toString());
// ตั้งค่า Web Server routes
setupWebServer();
}
// ตั้งค่า Web Server
void setupWebServer() {
// หน้าแรก - Dashboard
server.on("/", HTTP_GET, []() {
String html = generateDashboardHTML();
server.send(200, "text/html", html);
});
// API endpoint สำหรับข้อมูล JSON
server.on("/api/data", HTTP_GET, []() {
String json = generateDataJSON();
server.send(200, "application/json", json);
});
server.begin();
Serial.println("✅ Web Server เริ่มทำงานแล้ว");
}
// เพิ่มใน loop()
void loop() {
// ... โค้ดเดิม ...
server.handleClient(); // จัดการ Client requests
}
// สร้าง HTML Dashboard
String generateDashboardHTML() {
String html = R"rawliteral(
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ESP32 Weather Station</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body { font-family: 'Sarabun', sans-serif; }
</style>
</head>
<body class="bg-gradient-to-br from-blue-50 to-indigo-100 min-h-screen">
<div class="container mx-auto px-4 py-8">
<h1 class="text-4xl font-bold text-center text-gray-800 mb-8">
🌤️ ESP32 Weather Station
</h1>
<!-- Sensor Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<div class="bg-white rounded-lg shadow-lg p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-600">อุณหภูมิ</p>
<p class="text-3xl font-bold text-red-600" id="temp">--</p>
</div>
<div class="text-5xl">🌡️</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-lg p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-600">ความชื้อ</p>
<p class="text-3xl font-bold text-blue-600" id="humid">--</p>
</div>
<div class="text-5xl">💧</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-lg p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-600">ความดัน</p>
<p class="text-3xl font-bold text-purple-600" id="press">--</p>
</div>
<div class="text-5xl">📊</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-lg p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-600">สถานะฝน</p>
<p class="text-3xl font-bold" id="rain">--</p>
</div>
<div class="text-5xl">🌧️</div>
</div>
</div>
</div>
<!-- Chart -->
<div class="bg-white rounded-lg shadow-lg p-6">
<h2 class="text-2xl font-bold mb-4">กราฟประวัติอุณหภูมิ</h2>
<canvas id="tempChart"></canvas>
</div>
<p class="text-center text-gray-600 mt-8">
อัปเดตล่าสุด: <span id="updateTime">--</span>
</p>
</div>
<script>
// เริ่มต้น Chart
const ctx = document.getElementById('tempChart').getContext('2d');
const tempChart = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'อุณหภูมิ (°C)',
data: [],
borderColor: 'rgb(239, 68, 68)',
tension: 0.4,
fill: true,
backgroundColor: 'rgba(239, 68, 68, 0.1)'
}]
},
options: {
responsive: true,
scales: {
y: { beginAtZero: false }
}
}
});
// อัปเดตข้อมูลทุก 5 วินาที
setInterval(updateData, 5000);
async function updateData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
document.getElementById('temp').textContent = data.temperature.toFixed(1) + '°C';
document.getElementById('humid').textContent = data.humidity.toFixed(1) + '%';
document.getElementById('press').textContent = data.pressure.toFixed(1) + ' hPa';
if (data.is_raining) {
document.getElementById('rain').textContent = 'ฝนตก 🌧️';
document.getElementById('rain').classList.add('text-blue-600');
} else {
document.getElementById('rain').textContent = 'ไม่มีฝน ☀️';
document.getElementById('rain').classList.remove('text-blue-600');
}
document.getElementById('updateTime').textContent = new Date().toLocaleString('th-TH');
// เพิ่มข้อมูลลงกราฟ
const now = new Date().toLocaleTimeString('th-TH');
if (tempChart.data.labels.length > 20) {
tempChart.data.labels.shift();
tempChart.data.datasets[0].data.shift();
}
tempChart.data.labels.push(now);
tempChart.data.datasets[0].data.push(data.temperature);
tempChart.update();
} catch (error) {
console.error('Error:', error);
}
}
// โหลดข้อมูลครั้งแรก
updateData();
</script>
</body>
</html>
)rawliteral";
return html;
}
// สร้าง JSON data
String generateDataJSON() {
String json = "{";
json += "\"temperature\":" + String(temperature) + ",";
json += "\"humidity\":" + String(humidity) + ",";
json += "\"pressure\":" + String(pressure) + ",";
json += "\"rain_value\":" + String(rainValue) + ",";
json += "\"is_raining\":" + String(isRaining ? "true" : "false") + ",";
json += "\"heat_index\":" + String(calculateHeatIndex()) + ",";
json += "\"dew_point\":" + String(calculateDewPoint()) + ",";
json += "\"timestamp\":" + String(millis());
json += "}";
return json;
}🌐 วิธีใช้งาน:
- อัปโหลดโค้ดทั้งหมดไปยัง ESP32
- เปิด Serial Monitor (115200 baud)
- รอจนกว่าจะเชื่อมต่อ WiFi และแสดง IP Address
- เปิด Browser ไปที่ IP ที่แสดง
- Dashboard จะอัปเดตข้อมูลอัตโนมัติทุก 5 วินาที
💾 บันทึกข้อมูลลง SD Card
ข้อมูลทุกนาทีจะถูกบันทึกลงไฟล์ weather_data.csv บน SD Card
รูปแบบข้อมูล CSV:
1234,28.5,65.2,1013.25,1500,0,30.2,21.8
1294,28.6,65.0,1013.20,1450,0,30.3,21.7
วิธีดึงข้อมูล:
- วิธีที่ 1: ถอด SD Card ออกแล้วเสียบในคอมพิวเตอร์
- วิธีที่ 2: เพิ่ม Web Server endpoint สำหรับดาวน์โหลดไฟล์
- วิธีที่ 3: อัปโหลดไปยัง Cloud (Google Sheets, ThingSpeak, etc.)
🏠 เชื่อมต่อ Home Assistant (ESPHome)
หากต้องการใช้กับ Home Assistant สามารถใช้ ESPHome แทน Arduino ได้:
# ESPHome Configuration for Weather Station
# esphome/weather-station.yaml
esphome:
name: esp32-weather-station
platform: ESP32
board: esp32dev
# WiFi connection
wifi:
ssid: "YOUR_WIFI_SSID"
password: "YOUR_WIFI_PASSWORD"
# Enable logging
logger:
# Enable Home Assistant API
api:
password: "YOUR_API_PASSWORD"
# Enable OTA updates
ota:
password: "YOUR_OTA_PASSWORD"
# Enable Web server
web_server:
port: 80
# I2C Bus
i2c:
sda: 21
scl: 22
scan: true
# BME280 Sensor
sensor:
- platform: bme280
temperature:
name: "Weather Station Temperature"
id: temp_sensor
oversampling: 16x
pressure:
name: "Weather Station Pressure"
id: pressure_sensor
humidity:
name: "Weather Station Humidity"
id: humidity_sensor
address: 0x76
update_interval: 60s
# Rain Sensor (Analog)
- platform: ads1115
multiplexer: 'A0_GND'
gain: 4.096
name: "Rain Sensor"
id: rain_sensor
update_interval: 5s
# Calculate Heat Index
- platform: template
name: "Heat Index"
lambda: |-
float T = id(temp_sensor).state;
float RH = id(humidity_sensor).state;
float HI = -8.78469475556 +
1.61139411 * T +
2.33854883889 * RH -
0.14611605 * T * RH -
0.012308094 * T * T -
0.0164248277778 * RH * RH +
0.002211732 * T * T * RH +
0.00072546 * T * RH * RH -
0.000003582 * T * T * RH * RH;
return HI;
update_interval: 60s
# Binary Sensor for Rain Detection
binary_sensor:
- platform: template
name: "Is Raining"
lambda: |-
if (id(rain_sensor).state < 2000) {
return true;
} else {
return false;
}
update_interval: 5s
# SD Card (SPI)
spi:
clk_pin: 18
mosi_pin: 23
miso_pin: 19
# Text Sensor for logging
text_sensor:
- platform: template
name: "Weather Log"
lambda: |-
return {"Logging to SD card..."};
update_interval: never🎨 ปรับแต่ง Dashboard ใน Home Assistant:
- เพิ่ม ESPHome ผ่าน Configuration → Integrations
- สร้าง Lovelace Dashboard ใหม่
- เพิ่ม Gauge Card สำหรับอุณหภูมิและความชื้อ
- เพิ่ม History Graph เพื่อดูแนวโน้ม
- สร้าง Automation ตามข้อมูลอากาศ (เช่น เปิดพัดลมเมื่อร้อน)
🔧 แก้ปัญหาที่พบบ่อย
❌ BME280 ไม่ตอบสนอง
สาเหตุ: อาจเป็นที่อยู่ I2C ผิดหรือการเชื่อมต่อหลวม
วิธีแก้:
- ลองเปลี่ยนจาก 0x76 เป็น 0x77
- ตรวจสอบการเชื่อมต่อ SDA/SCL
- ใช้ I2C Scanner เพื่อหาที่อยู่ที่ถูกต้อง
❌ SD Card ไม่ทำงาน
สาเหตุ: รูปแบบไฟล์ไม่ถูกต้องหรือ Pin SPI ผิด
วิธีแก้:
- ฟอร์แมต SD Card เป็น FAT32
- ตรวจสอบ Pin SPI: CS=5, SCK=18, MOSI=23, MISO=19
- ใช้ SD Card ขนาดไม่เกิน 32GB
❌ WiFi เชื่อมต่อไม่ได้
สาเหตุ: รหัสผ่านผิดหรือสัญญาณอ่อน
วิธีแก้:
- ตรวจสอบ SSID และ Password
- ใช้ WiFi 2.4GHz (ESP32 ไม่รองรับ 5GHz บางรุ่น)
- เพิ่ม
WiFi.reconnect()ใน loop()
⚠️ ค่า Rain Sensor ผิดปกติ
วิธีแก้:
- ปรับค่า Threshold ในโค้ด (ลอง 1000-3000)
- ทดสอบโดนน้ำโดยตรงแล้วดูค่าที่อ่านได้
- ใช้ Digital Output แทน Analog ถ้าต้องการเพียงรู้ว่าฝนตกหรือไม่
🎯 สรุป
ในบทความนี้เราได้เรียนรู้วิธีสร้างสถานีตรวจวัดอากาศ ESP32 ที่สมบูรณ์แบบ ตั้งแต่การเชื่อมต่อเซนเซอร์ การเขียนโค้ด การสร้าง Web Dashboard และการบันทึกข้อมูล
✨ สิ่งที่คุณได้เรียนรู้:
- ✓การใช้ BME280 วัดอุณหภูมิ ความชื้อ และความดันอากาศ
- ✓การตรวจจับฝนด้วย Rain Sensor Module
- ✓การบันทึกข้อมูลลง SD Card แบบ CSV
- ✓การสร้าง Web Dashboard ด้วย HTML และ Chart.js
- ✓การเชื่อมต่อกับ Home Assistant ผ่าน ESPHome
- ✓การคำนวณ Heat Index และ Dew Point
🚀 ไอเดียต่อยอด:
- เพิ่ม Anemometer สำหรับวัดความเร็วและทิศทางลม
- เพิ่ม UV Index Sensor เพื่อตรวจสอบรังสีแสงแดด
- อัปโหลดข้อมูลไปยัง ThingSpeak หรือ Google Sheets
- เพิ่ม Solar Panel และ Battery สำหรับใช้งาน Outdoor
- สร้างแจ้งเตือนผ่าน Line Notify หรือ Telegram
- เพิ่ม OLED Display แสดงผลในตัวเครื่อง
📚 อ้างอิงเพิ่มเติม: