📑 เนื้อหาในบทความ
🎯 ภาพรวมโปรเจกต์
การวัดแรงดันไฟ (Voltage Monitoring) เป็นสิ่งสำคัญในโปรเจกต์ IoT หลายประเภท ไม่ว่าจะเป็นการตรวจสอบแบตเตอรี่, ติดตามประสิทธิภาพของโซลาร์เซลล์, หรือตรวจสอบระบบไฟฟ้าในอุปกรณ์ต่างๆ บทความนี้จะสอนวิธีใช้ ESP32 วัดแรงดันไฟได้อย่างแม่นยำ พร้อมเชื่อมต่อกับ CynoIoT Platform เพื่อตรวจสอบข้อมูลได้ตลอด 24 ชั่วโมง
📚 พื้นฐานการวัดแรงดันไฟฟ้า
ทำไมต้องใช้ Voltage Divider?
ESP32 มี ADC (Analog-to-Digital Converter) ที่สามารถวัดแรงดันไฟได้สูงสุดที่ 3.3V เท่านั้น ดังนั้นหากเราต้องการวัดแรงดันที่สูงกว่านั้น (เช่น แบตเตอรี่ 12V, 24V) เราต้องใช้วงจรแบ่งแรงดัน (Voltage Divider) เพื่อลดแรงดันลงมาอยู่ในช่วงที่ ESP32 วัดได้
🧮 สูตรคำนวณ Voltage Divider
โดยที่:
• Vin = แรงดันไฟฟ้าขาเข้าที่ต้องการวัด
• Vout = แรงดันไฟฟ้าขาออกที่เข้า ESP32 (ต้อง ≤ 3.3V)
• R1 = ตัวต้านทานบน (Top Resistor)
• R2 = ตัวต้านทานล่าง (Bottom Resistor)
ตัวอย่างการคำนวณ
สมมติเราต้องการวัดแบตเตอรี่ 12V และให้ Vout = 3V (ปลอดภัยสำหรับ ESP32):
- •ถ้าเลือก R2 = 10 kΩ
- •3V = 12V × (10k / (R1 + 10k))
- •3/12 = 10k / (R1 + 10k)
- •0.25 = 10k / (R1 + 10k)
- •R1 = 30 kΩ
🔧 อุปกรณ์ที่ต้องใช้
Hardware Components
- ✓ESP32 Development Board (NodeMCU, Wemos, หรืออื่นๆ) - 120 บาท
- ✓ตัวต้านทาน 30 kΩ (1/4W หรือ 1/2W) - 5 บาท
- ✓ตัวต้านทาน 10 kΩ (1/4W หรือ 1/2W) - 5 บาท
- ✓ตัวต้านทาน 100 kΩ (สำหรับวัดแรงดันสูง) - 5 บาท
- ✓แหล่งจ่ายไฟ (แบตเตอรี่ 12V, แบตเตอรี่ Li-Ion, หรือ Adaptor) - แล้วแต่โปรเจกต์
- ✓Breadboard และ Jumper Wires - 20 บาท
⚡ วิธีต่อวงจร
วงจร Voltage Divider สำหรับวัด 0-12V
แบตเตอรี่/แรงดันไฟ (0-12V)
│
├───────────────── Vin (+)
│
▼
R1 = 30 kΩ
│
├───────────────── Vout → ESP32 GPIO34 (ADC1_CH6)
│
R2 = 10 kΩ
│
▼
GND (0V) ───────────── GND
ขั้นตอนการต่อวงจร
- 1ต่อขา + ของแบตเตอรี่เข้ากับตัวต้านทาน R1 (30 kΩ)
- 2ต่ออีกด้านของ R1 เข้ากับ R2 (10 kΩ) และต่อเข้า GPIO34 ของ ESP32
- 3ต่ออีกด้านของ R2 เข้ากับ GND ของ ESP32 และขา - ของแบตเตอรี่
- 4สำคัญ: ต้องแชร์ GND ระหว่างแบตเตอรี่กับ ESP32 เสมอ
💻 โค้ดพื้นฐาน - วัดแรงดันไฟฟ้า
โค้ดนี้ใช้สำหรับวัดแรงดันไฟฟ้าและแสดงค่าบน Serial Monitor ซึ่งเป็นพื้นฐานสำหรับทุกโปรเจกต์
/*
* ESP32 Voltage Sensor - Basic Code
* วัดแรงดันไฟฟ้าด้วย ESP32 ผ่าน Voltage Divider
*
* Hardware:
* - R1 = 30 kΩ (ตัวต้านทานบน)
* - R2 = 10 kΩ (ตัวต้านทานล่าง)
* - Vin เข้าที่ R1
* - Vout จากจุดเชื่อมต่อ R1 กับ R2 เข้า GPIO34
*
* Author: CynoIoT
* Date: March 2026
*/
// ตั้งค่าขา ADC
const int voltagePin = 34; // GPIO34 (ADC1_CH6) - รองรับ ADC เมื่อใช้ WiFi
// ค่าคงที่สำหรับคำนวณ
const float R1 = 30000.0; // 30 kΩ
const float R2 = 10000.0; // 10 kΩ
const float REFERENCE_VOLTAGE = 3.3; // แรงดันอ้างอิงของ ESP32
const int ADC_RESOLUTION = 4095; // ความละเอียด ADC 12-bit (0-4095)
void setup() {
Serial.begin(115200); // เปิด Serial Monitor
delay(1000);
Serial.println("\n\n=== ESP32 Voltage Sensor ===");
Serial.println("Starting voltage measurement...");
Serial.println("--------------------------------");
// ตั้งค่าความละเอียด ADC (เริ่มต้นที่ 12-bit)
analogReadResolution(12); // 0-4095
analogSetAttenuation(ADC_11db); // รองรับแรงดัน 0-3.3V
}
void loop() {
// อ่านค่าจาก ADC
int adcValue = analogRead(voltagePin);
// คำนวณแรงดันที่ขา ADC (Vout)
float adcVoltage = (adcValue * REFERENCE_VOLTAGE) / ADC_RESOLUTION;
// คำนวณแรงดันจริง (Vin) จากสูตร Voltage Divider
// Vin = Vout * (R1 + R2) / R2
float inputVoltage = adcVoltage * (R1 + R2) / R2;
// แสดงผลบน Serial Monitor
Serial.println("\n--- Voltage Reading ---");
Serial.print("ADC Raw Value: ");
Serial.print(adcValue);
Serial.print(" / ");
Serial.println(ADC_RESOLUTION);
Serial.print("ADC Voltage: ");
Serial.print(adcVoltage, 3);
Serial.println(" V");
Serial.print("Input Voltage: ");
Serial.print(inputVoltage, 3); // แสดง 3 ตำแหน่งทศนิยม
Serial.println(" V");
// ตรวจสอบระดับแบตเตอรี่ (สำหรับแบตเตอรี่ 12V)
if (inputVoltage > 12.6) {
Serial.println("⚡ Battery: Fully Charged (100%)");
} else if (inputVoltage > 12.0) {
Serial.println("🔋 Battery: Good (75-100%)");
} else if (inputVoltage > 11.6) {
Serial.println("⚠️ Battery: Medium (50-75%)");
} else if (inputVoltage > 11.0) {
Serial.println("🔴 Battery: Low (25-50%) - Consider charging!");
} else {
Serial.println("🚨 Battery: Critical (<25%) - Charge immediately!");
}
delay(2000); // วัดทุก 2 วินาที
}อธิบายโค้ด
- •GPIO34 เลือกเพราะเป็น ADC channel ที่สามารถใช้งานได้แม้เปิด WiFi
- •analogSetAttenuation(ADC_11db) ตั้งค่าให้อ่านแรงดันได้ถึง 3.3V
- •สูตรคำนวณ แปลงค่า ADC (0-4095) เป็นแรงดันจริงๆ ที่ต้องการวัด
- •ระดับแบตเตอรี่ ตรวจสอบสถานะแบตเตอรี่แบบง่าย (12V Lead Acid)
🔋 โค้ดขั้นสูง - Battery Monitor System
โค้ดนี้ปรับปรุงให้มีฟีเจอร์เพิ่มเติม เช่น การคำนวณเปอร์เซ็นต์แบตเตอรี่, การเฉลี่ยค่า (Moving Average) เพื่อลดความคลาดเคลื่อน, และแจ้งเตือนเมื่อแบตเตอรี่ต่ำ
/*
* ESP32 Advanced Battery Monitor
* ระบบตรวจสอบแบตเตอรี่แบบละเอียด
*
* Features:
* - Moving Average Filter (ลด Noise)
* - Percentage Calculation
* - Low Battery Alert
* - Data Logging (สามารถบันทึกลง SD Card ได้)
*
* Author: CynoIoT
* Date: March 2026
*/
// ตั้งค่าขา ADC
const int voltagePin = 34;
// ค่าคงที่สำหรับคำนวณ
const float R1 = 30000.0;
const float R2 = 10000.0;
const float REFERENCE_VOLTAGE = 3.3;
const int ADC_RESOLUTION = 4095;
// การตั้งค่าแบตเตอรี่ (แก้ตามประเภทแบตเตอรี่ของคุณ)
// ตัวอย่างสำหรับ 12V Lead Acid Battery
const float BATTERY_FULL = 12.6; // 100% - แบตเตอรี่เต็ม
const float BATTERY_EMPTY = 10.5; // 0% - แบตเตอรี่หมด
const float LOW_BATTERY_THRESHOLD = 11.0; // แจ้งเตือนเมื่อ < 25%
// Moving Average Filter
const int SAMPLE_SIZE = 10; // จำนวน Sample สำหรับเฉลี่ย
float voltageBuffer[SAMPLE_SIZE];
int bufferIndex = 0;
// LED แสดงสถานะ
const int ledOK = 2; // LED สีเขียว (ในตัว ESP32)
const int ledWarning = 4; // LED สีเหลือง/แดง (ต่อภายนอก)
void setup() {
Serial.begin(115200);
delay(1000);
// ตั้งค่า LED
pinMode(ledOK, OUTPUT);
pinMode(ledWarning, OUTPUT);
digitalWrite(ledOK, HIGH); // เปิด LED เริ่มต้น
digitalWrite(ledWarning, LOW);
Serial.println("\n\n=== ESP32 Battery Monitor ===");
Serial.print("Battery Type: 12V Lead Acid");
Serial.print(" | Full: ");
Serial.print(BATTERY_FULL);
Serial.print("V | Empty: ");
Serial.print(BATTERY_EMPTY);
Serial.println("V");
Serial.println("--------------------------------");
// ตั้งค่า ADC
analogReadResolution(12);
analogSetAttenuation(ADC_11db);
// เริ่มต้น Buffer
for (int i = 0; i < SAMPLE_SIZE; i++) {
voltageBuffer[i] = 0.0;
}
}
float readVoltage() {
// อ่านค่า ADC หลายครั้งเพื่อเฉลี่ย (Reduce Noise)
float sum = 0;
for (int i = 0; i < 5; i++) {
int adcValue = analogRead(voltagePin);
float adcVoltage = (adcValue * REFERENCE_VOLTAGE) / ADC_RESOLUTION;
float inputVoltage = adcVoltage * (R1 + R2) / R2;
sum += inputVoltage;
delay(10);
}
return sum / 5; // ค่าเฉลี่ย
}
float getMovingAverage(float newVoltage) {
// บันทึกค่าใหม่ลงใน Buffer
voltageBuffer[bufferIndex] = newVoltage;
bufferIndex = (bufferIndex + 1) % SAMPLE_SIZE; // วนกลับไปเริ่มต้น
// คำนวณค่าเฉลี่ย
float sum = 0;
for (int i = 0; i < SAMPLE_SIZE; i++) {
sum += voltageBuffer[i];
}
return sum / SAMPLE_SIZE;
}
float calculatePercentage(float voltage) {
// คำนวณเปอร์เซ็นต์จากแรงดัน
float percentage = ((voltage - BATTERY_EMPTY) / (BATTERY_FULL - BATTERY_EMPTY)) * 100.0;
// จำกัดค่าระหว่าง 0-100%
if (percentage < 0) percentage = 0;
if (percentage > 100) percentage = 100;
return percentage;
}
void displayBatteryStatus(float voltage, float percentage) {
Serial.println("\n📊 Battery Status");
Serial.println("====================");
// แสดงแรงดันไฟ
Serial.print("Voltage: ");
Serial.print(voltage, 3);
Serial.println(" V");
// แสดงเปอร์เซ็นต์
Serial.print("Capacity: ");
Serial.print(percentage, 1);
Serial.println("%");
// แสดง Bar Graph
int bars = (int)(percentage / 5); // 1 bar = 5%
Serial.print("Status: [");
for (int i = 0; i < 20; i++) {
if (i < bars) {
Serial.print("█");
} else {
Serial.print("░");
}
}
Serial.println("]");
// แสดงสถานะ
if (percentage >= 75) {
Serial.println("✅ Status: EXCELLENT");
digitalWrite(ledOK, HIGH);
digitalWrite(ledWarning, LOW);
} else if (percentage >= 50) {
Serial.println("👍 Status: GOOD");
digitalWrite(ledOK, HIGH);
digitalWrite(ledWarning, LOW);
} else if (percentage >= 25) {
Serial.println("⚠️ Status: LOW - Consider charging");
digitalWrite(ledOK, LOW);
digitalWrite(ledWarning, HIGH);
} else {
Serial.println("🚨 Status: CRITICAL - Charge NOW!");
digitalWrite(ledOK, LOW);
digitalWrite(ledWarning, HIGH);
}
// ประมาณการใช้งานที่เหลือ
if (percentage >= 75) {
Serial.println("⏱️ Est. Runtime: 8+ hours");
} else if (percentage >= 50) {
Serial.println("⏱️ Est. Runtime: 4-8 hours");
} else if (percentage >= 25) {
Serial.println("⏱️ Est. Runtime: 1-4 hours");
} else {
Serial.println("⏱️ Est. Runtime: < 1 hour");
}
}
void loop() {
// อ่านแรงดัน
float rawVoltage = readVoltage();
// กรอง Signal ด้วย Moving Average
float filteredVoltage = getMovingAverage(rawVoltage);
// คำนวณเปอร์เซ็นต์
float percentage = calculatePercentage(filteredVoltage);
// แสดงผล
displayBatteryStatus(filteredVoltage, percentage);
// เช็ค Low Battery
if (filteredVoltage < LOW_BATTERY_THRESHOLD) {
Serial.println("\n🔔 LOW BATTERY ALERT!");
Serial.println("⚠️ Please charge your battery soon!");
// สามารถเพิ่มการแจ้งเตือนอื่นๆ เช่น Buzzer, Email, Line Notify
}
delay(5000); // อัปเดตทุก 5 วินาที
}🌐 เชื่อมต่อกับ CynoIoT Platform
หลังจากที่วัดแรงดันได้แล้ว เราสามารถส่งข้อมูลไปยัง CynoIoT Platform เพื่อตรวจสอบได้ตลอด 24 ชั่วโมง วิธีเชื่อมต่อง่ายๆ มีดังนี้:
ขั้นตอนการเชื่อมต่อ
- 1สมัครสมาชิก CynoIoT ที่ cynoiot.com
- 2สร้าง Device ใหม่ และคัดลอก Device Token
- 3ใส่ Token ลงในโค้ด ESP32 และอัปโหลด
- 4ดูข้อมูลแบตเตอรี่ได้ที่ Dashboard ของ CynoIoT
/*
* ESP32 Battery Monitor + CynoIoT Integration
* ส่งข้อมูลแบตเตอรี่ไปยัง CynoIoT Platform
*
* ตัวอย่างนี้ใช้ HTTP/MQTT ในการส่งข้อมูล
* แก้ไขตาม API ของ CynoIoT ที่คุณใช้
*/
#include <WiFi.h>
#include <HTTPClient.h>
// WiFi Credentials
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// CynoIoT Credentials
const char* cynoiotToken = "YOUR_DEVICE_TOKEN";
const char* cynoiotServer = "api.cynoiot.com"; // ตรวจสอบจากเอกสาร CynoIoT
// Voltage Sensor Setup
const int voltagePin = 34;
const float R1 = 30000.0;
const float R2 = 10000.0;
const float REFERENCE_VOLTAGE = 3.3;
const int ADC_RESOLUTION = 4095;
unsigned long lastSendTime = 0;
const unsigned long SEND_INTERVAL = 60000; // ส่งทุก 60 วินาที
void setup() {
Serial.begin(115200);
delay(1000);
// เชื่อมต่อ WiFi
Serial.println("\n=== ESP32 Battery Monitor + CynoIoT ===");
Serial.print("Connecting to WiFi");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\n✅ WiFi Connected!");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
// ตั้งค่า ADC
analogReadResolution(12);
analogSetAttenuation(ADC_11db);
}
float readBatteryVoltage() {
int adcValue = analogRead(voltagePin);
float adcVoltage = (adcValue * REFERENCE_VOLTAGE) / ADC_RESOLUTION;
float inputVoltage = adcVoltage * (R1 + R2) / R2;
return inputVoltage;
}
void sendToCynoIoT(float voltage, float percentage) {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("⚠️ WiFi not connected. Skipping data send.");
return;
}
HTTPClient http;
// สร้าง JSON payload (ตัวอย่าง - แก้ตาม API ของ CynoIoT)
String payload = "{";
payload += "\"voltage\": " + String(voltage, 3) + ",";
payload += "\"percentage\": " + String(percentage, 1) + ",";
payload += "\"timestamp\": " + String(millis());
payload += "}";
// ส่งข้อมูล
String url = String("https://") + cynoiotServer + "/api/v1/devices/data";
http.begin(url);
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", "Bearer " + String(cynoiotToken));
int httpResponseCode = http.POST(payload);
if (httpResponseCode > 0) {
Serial.print("✅ Data sent to CynoIoT. Response: ");
Serial.println(httpResponseCode);
String response = http.getString();
Serial.println(response);
} else {
Serial.print("❌ Error sending data: ");
Serial.println(httpResponseCode);
}
http.end();
}
void loop() {
unsigned long currentTime = millis();
// อ่านและส่งข้อมูลทุกๆ SEND_INTERVAL
if (currentTime - lastSendTime >= SEND_INTERVAL) {
lastSendTime = currentTime;
// อ่านแรงดัน
float voltage = readBatteryVoltage();
// คำนวณเปอร์เซ็นต์ (สำหรับ 12V Lead Acid)
float percentage = ((voltage - 10.5) / (12.6 - 10.5)) * 100.0;
if (percentage < 0) percentage = 0;
if (percentage > 100) percentage = 100;
// แสดงผลบน Serial Monitor
Serial.println("\n📊 Battery Reading");
Serial.print("Voltage: ");
Serial.print(voltage, 3);
Serial.println(" V");
Serial.print("Percentage: ");
Serial.print(percentage, 1);
Serial.println(" %");
// ส่งไป CynoIoT
Serial.println("📤 Sending to CynoIoT...");
sendToCynoIoT(voltage, percentage);
}
delay(100);
}🎯 แอปพลิเคชันที่ใช้งานได้
Solar Panel Monitoring
ติดตามประสิทธิภาพของโซลาร์เซลล์ วัดแรงดันที่ผลิตได้ตลอดทั้งวัน และวิเคราะห์พลังงานที่เก็บได้
Battery Management
ระบบจัดการแบตเตอรี่อัจฉริยะ แจ้งเตือนเมื่อแบตเตอรี่ต่ำ และป้องกันการใช้งานเกินขีดจำกัด
Smart Home UPS
ระบบสำรองไฟฉุกเฉินสำหรับ Smart Home ตรวจสอบแบตเตอรี่ และสลับไฟอัตโนมัติเมื่อไฟดับ
EV/Battery Testing
ทดสอบและวิเคราะห์สมรรถนะแบตเตอรี่ วัดอัตราการคายประจุ และวงจรการทำงาน
🔧 การแก้ปัญหา
❌ ค่าที่อ่านได้ไม่แม่นยำ
- • ตรวจสอบค่าตัวต้านทาน R1 และ R2 ว่าตรงตามที่คำนวณหรือไม่
- • ใช้ multimeter วัดค่าตัวต้านทานจริงเพื่อความแม่นยำ
- • เพิ่มจำนวน Sample และใช้ Moving Average Filter
- • ตรวจสอบแรงดันอ้างอิง 3.3V ของ ESP32 ว่าคงที่หรือไม่
⚠️ ค่าที่อ่านได้กระโดดง่าย
- • เพิ่ม Capacitor 10µF ขนานกับ R2 เพื่อกรอง Noise
- • ใช้ Shielded Wire หรือ Twisted Pair
- • เพิ่ม Sample Size ใน Moving Average
- • ตรวจสอบว่าแหล่งจ่ายไฟคงที่หรือไม่
📡 ESP32 อ่านค่าไม่ได้เมื่อเปิด WiFi
- • ใช้ GPIO34, GPIO35, GPIO36 หรือ GPIO39 (ADC1)
- • หลีกเลี่ยงการใช้ ADC2 (GPIO4, 12-15, 25-27) เมื่อเปิด WiFi
- • ปิด WiFi ชั่วคราวขณะอ่านค่า ADC หากจำเป็น
🔌 วัดแรงดันได้ไม่ถึงค่าสูงสุด
- • ตรวจสอบสูตรคำนวณ Voltage Divider อีกครั้ง
- • แรงดันขาออก (Vout) ต้องไม่เกิน 3.3V
- • ปรับค่า R1 และ R2 ใหม่ถ้าจำเป็น
- • ใช้ Zener Diode 3.3V ป้องกันไฟเกิน
🚀 ขั้นตอนถัดไป
ตอนนี้คุณสามารถวัดแรงดันไฟฟ้าด้วย ESP32 ได้แล้ว! ต่อไปนี้คือแนวคิดสำหรับพัฒนาต่อ: