สวัสดีชาว Maker สายเซนเซอร์และระบบไฟทุกคนครับ! เคยสงสัยไหมว่าแต่ละเดือนเครื่องใช้ไฟฟ้าในบ้านเรากินไฟไปเท่าไหร่? การรอใบแจ้งหนี้ค่าไฟตอนสิ้นเดือนมันเอาต์ไปแล้วครับ เพราะยุคนี้ "พลังงานไฟฟ้าไม่ใช่ของฟรีที่มีไม่จำกัด" ทุกวัตต์ที่เสียไปคือต้นทุนและผลกระทบต่อความปลอดภัย
วันนี้เราจะพามาทำความรู้จักกับระบบ Smart Grid in IoT แบบเข้าใจง่ายฉบับจับต้องได้ ซึ่งก็คือการเปลี่ยนระบบไฟฟ้าธรรมดาให้ "ฉลาดขึ้น" ด้วยการมอนิเตอร์ข้อมูลแบบเรียลไทม์ ทั้งแรงดัน (Voltage), กระแสไฟ (Current) และกำลังไฟ (Power) เพื่อที่เราจะได้รู้ทันทีว่ามีเครื่องใช้ไฟฟ้าตัวไหนกินไฟผิดปกติ หรือไฟตกไฟกระชากจนอาจทำให้ของพังไหม
โปรเจกต์นี้เราจะพามาสร้าง "โหนดตรวจสอบ Smart Grid แบบ IoT" โดยใช้สมองกลอย่าง Arduino UNO R4 WiFi จับคู่กับเซนเซอร์วัดพลังงาน PZEM-004T ดึงข้อมูลมาโชว์บนจอ OLED และส่งขึ้น Cloud อย่าง ThingSpeak ให้ดูข้อมูลได้จากทุกที่บนโลกครับ!
รู้จักกับพระเอกของเรา: PZEM-004T Energy Meter
ก่อนจะต่อสายไฟ เราต้องมีตัววัดไฟที่ไว้ใจได้ก่อนครับ PZEM-004T คือโมดูลวัดพลังงานดิจิทัลสุดกะทัดรัดที่ออกแบบมาเพื่องาน Embedded โดยเฉพาะ มันมาพร้อมกับ Current Transformer (CT Clamp) หรือตัวคล้องสายไฟ ทำให้เราสามารถวัดกระแสสลับ (AC) ได้อย่างปลอดภัยโดยไม่ต้องปอกสายไฟไปแตะตรงๆ
ด้านในโมดูลมีชิปประมวลผลที่จัดการคำนวณค่า RMS ทั้งหมดให้เสร็จสรรพ แล้วส่งข้อมูลออกมาเป็นดิจิทัลผ่านโปรโตคอล Modbus RTU (UART) ทำให้บอร์ด Arduino ของเราไม่ต้องมานั่งคำนวณสัญญาณอนาล็อกให้ปวดหัว แค่สั่งขอข้อมูล มันก็โยนค่าที่วัดได้กลับมาให้เลย สะดวกสุดๆ
| พารามิเตอร์ (Parameter) |
สิ่งที่บอก (What It Indicates) |
ย่านการวัดของ PZEM-004T |
| Voltage (V) |
คุณภาพและความเสถียรของไฟ |
80 – 260 V AC |
| Current (A) |
ความต้องการโหลด (กันสายไฟร้อน) |
0 – 100 A AC (ผ่าน CT clamp) |
| Real Power (W) |
กำลังไฟฟ้าจริงที่ใช้งาน |
0 – 23,000 W |
| Energy (kWh) |
พลังงานสะสม (ที่ใช้คิดค่าไฟ) |
0 – 9999.99 kWh |
| Frequency (Hz) |
ความเสถียรของระบบส่งกำลัง |
45 – 65 Hz |
| Power Factor (PF) |
ประสิทธิภาพการแปลงกระแสไฟเป็นงาน |
0.00 – 1.00 |
เตรียมอุปกรณ์ให้พร้อม (Components Required)
รายการฮาร์ดแวร์ที่ต้องใช้สำหรับโปรเจกต์นี้มีไม่เยอะครับ เน้นความเรียบง่ายแต่ใช้งานได้จริง:
-
บอร์ดคอนโทรลเลอร์: Arduino UNO R4 WiFi (ตัวจบที่มี Wi-Fi ในตัว และมี Hardware Serial)
-
เซนเซอร์วัดไฟ: PZEM-004T พร้อม CT Clamp
-
จอแสดงผล: OLED Display (128x64 แบบ I2C)
-
อุปกรณ์อื่นๆ: Push Button (สวิตช์ปุ่มกด), Breadboard, สาย Jumper Wires
💡 Maker's Tip: การทำโปรเจกต์ที่ต้องต่อกับไฟบ้าน (Mains AC) เรื่องความปลอดภัยและการจัดระเบียบสายไฟสำคัญมากครับ เพื่อความชัวร์และปลอดภัย แนะนำให้หาเคสใส่ให้มิดชิด
หากเพื่อนๆ กำลังมองหา บอร์ด Arduino UNO R4 แท้, จอ OLED, โมดูลเซนเซอร์ หรืออยากหา เส้นพลาสติก 3D Print (Filament) คุณภาพดีเพื่อนำไปปริ้นท์กล่อง Enclosure ป้องกันไฟช็อต แวะไปช้อปปิ้งของดีพร้อมลุยงานได้ที่ Globalbyte เลยครับ!
สถาปัตยกรรมระบบ & การต่อวงจร (Architecture & Wiring)
ระบบนี้ทำงานแบบ Edge-to-Cloud แบบ 3 เทียร์ครับ เริ่มจาก Edge (Sensing) คือตัว PZEM ที่คล้องสายไฟอ่านค่า -> ส่งมาที่ Gateway (Processing) คือ Arduino R4 ผ่านสาย UART (Modbus RTU) -> และอัปโหลดขึ้น Cloud (Storage & Visualisation) คือแพลตฟอร์ม ThingSpeak ผ่าน Wi-Fi
การต่อสาย (Wiring):
-
PZEM TX ต่อเข้า Arduino RX (Pin 0)
-
PZEM RX ต่อเข้า Arduino TX (Pin 1)
-
OLED SDA/SCL ต่อเข้าขา A4 / A5 ตามลำดับ
-
ปุ่มกด (Button) ต่อเข้า Digital Pin 7
ผังการต่อวงจร (Circuit Diagram)
แผนภาพสถาปัตยกรรม Data Flow (Edge to Cloud)
ประกอบลงกล่อง 3D Print สุดเท่! (Practical Deployment)
เมื่อเราต่อสายไฟและทดสอบฮาร์ดแวร์เสร็จแล้ว (Hardware Setup) การจะเอาไปใช้จริงกับไฟบ้านจะปล่อยสายเปลือยๆ ไม่ได้เด็ดขาดครับ ผู้สร้างโปรเจกต์นี้จึงออกแบบ 3D Printed Enclosure (กล่องพลาสติกปริ้นท์ 3D) เพื่อเก็บซ่อนอุปกรณ์อิเล็กทรอนิกส์ทั้งหมด ทั้งบอร์ด Arduino, PZEM และสายไฟ เจาะช่องใส่จอ OLED และปุ่มกดให้ดูเป็นมืออาชีพและปลอดภัยสุดๆ
การเชื่อมต่อฮาร์ดแวร์และจอ OLED
การนำบอร์ดประกอบลงกล่อง 3D Printed Custom Enclosure เพื่อความปลอดภัย
เมื่อส่งข้อมูลขึ้น ThingSpeak ก็จะได้กราฟสวยๆ แบบนี้ให้ดูย้อนหลังได้สบายๆ
การตั้งค่า ThingSpeak (Cloud Dashboard)
เพื่อให้เราดูค่าไฟแบบออนไลน์ได้ เราต้องส่งข้อมูลขึ้น ThingSpeak ครับ วิธีการคือสมัครบัญชีฟรี สร้าง Channel ใหม่ กำหนดฟิลด์ข้อมูลให้ครบ 6 ค่า (V, I, W, Energy, Hz, PF) แล้วก๊อปปี้ Channel ID และ Write API Key มาใส่ในโค้ด Arduino เท่านี้บอร์ดเราก็จะยิงข้อมูลขึ้นกราฟได้เองทุกๆ 16 วินาทีครับ
วิดีโอสาธิตการทำงาน (Video Demonstration)
เจาะลึกโค้ด (Source Code)
โค้ดโปรเจกต์นี้เขียนด้วยภาษา C++ ผ่าน Arduino IDE โดยเราจะใช้ไลบรารี PZEM004Tv40_R4.h ในการคุยกับเซนเซอร์ผ่าน Hardware Serial 1 ซึ่งเสถียรกว่าการใช้ Software Serial แบบเก่าๆ มาก โค้ดถูกเขียนให้ทำงานแบบ Non-blocking (ใช้ millis() แทน delay()) ทำให้ระบบรันจอ ส่งเน็ต และเช็คปุ่มกดไปพร้อมๆ กันได้โดยไม่สะดุดครับ
#include <WiFiS3.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ThingSpeak.h>
#include <PZEM004Tv40_R4.h>
const char* ssid = "Semicon Media";
const char* password = "cracksen1605";
unsigned long myChannelNumber = 3238257;
const char* myWriteAPIKey = "CRXGU545EOA6Z8R9";
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
#define BUTTON_PIN 4
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
PZEM004Tv40_R4 pzem(&Serial1);
WiFiClient client;
unsigned long lastReadTime = 0;
unsigned long lastUploadTime = 0;
unsigned long lastSerialPrintTime = 0;
unsigned long lastDisplayUpdateTime = 0;
unsigned long lastDisplaySwitchTime = 0;
unsigned long wifiConnectStartTime = 0;
unsigned long lastButtonPressTime = 0;
const unsigned long readInterval = 1000;
const unsigned long serialPrintInterval = 1000;
const unsigned long displayUpdateInterval = 500;
const unsigned long displaySwitchInterval = 5000;
const unsigned long uploadInterval = 16000;
const unsigned long wifiTimeout = 10000;
const unsigned long debounceDelay = 200;
int displayMode = 0;
bool rollingEnabled = false;
bool lastButtonState = HIGH;
bool buttonState = HIGH;
enum WiFiState { WIFI_IDLE, WIFI_CONNECTING, WIFI_CONNECTED, WIFI_FAILED };
WiFiState wifiState = WIFI_IDLE;
float voltage = 0.0;
float current = 0.0;
float power = 0.0;
float energy = 0.0;
float frequency = 0.0;
float powerFactor = 0.0;
bool dataValid = false;
void setup() {
Serial.begin(115200);
Serial.println("PZEM-004T Energy Monitor with ThingSpeak");
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
while (1);
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0, 0);
display.println("PZEM Monitor Init...");
display.display();
pinMode(BUTTON_PIN, INPUT_PULLUP);
pzem.begin();
ThingSpeak.begin(client);
wifiState = WIFI_CONNECTING;
WiFi.begin(ssid, password);
wifiConnectStartTime = millis();
}
void loop() {
unsigned long currentMillis = millis();
handleButton(currentMillis);
handleWiFi(currentMillis);
if (currentMillis - lastReadTime >= readInterval) {
lastReadTime = currentMillis;
readPZEMData();
}
if (currentMillis - lastSerialPrintTime >= serialPrintInterval && dataValid) {
lastSerialPrintTime = currentMillis;
printToSerial();
}
if (rollingEnabled && (currentMillis - lastDisplaySwitchTime >= displaySwitchInterval)) {
lastDisplaySwitchTime = currentMillis;
displayMode++;
if (displayMode > 6) displayMode = 1;
}
if (currentMillis - lastDisplayUpdateTime >= displayUpdateInterval) {
lastDisplayUpdateTime = currentMillis;
updateDisplay();
}
if (currentMillis - lastUploadTime >= uploadInterval && dataValid) {
if (wifiState == WIFI_CONNECTED) {
lastUploadTime = currentMillis;
uploadToThingSpeak();
}
}
}
void readPZEMData() {
if (pzem.readAll()) {
voltage = pzem.getVoltage();
current = pzem.getCurrent() * 1000;
power = pzem.getPower();
energy = pzem.getEnergy();
frequency = pzem.getFrequency();
powerFactor = pzem.getPowerFactor();
dataValid = true;
} else {
dataValid = false;
Serial.println("Error reading PZEM!");
}
}
void uploadToThingSpeak() {
ThingSpeak.setField(1, voltage);
ThingSpeak.setField(2, current);
ThingSpeak.setField(3, power);
ThingSpeak.setField(4, energy);
ThingSpeak.setField(5, frequency);
ThingSpeak.setField(6, powerFactor);
int statusCode = ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
if (statusCode == 200) {
Serial.println("Channel update successful!");
}
}
ตารางแก้ปัญหาเบื้องต้น (Troubleshooting)
| ปัญหาที่พบ |
สาเหตุ (Error / Symptom) |
วิธีแก้ไข |
| อ่านค่าจากมิเตอร์ไม่ได้ |
ไม่ได้รับการตอบสนองจาก Modbus |
เช็คสาย TX, RX (ต้องไขว้กัน) และเช็คว่าแชร์ GND ร่วมกันแล้ว |
| กระแสไฟเป็น 0 A ตลอดเวลา |
คล้อง CT Clamp ผิดวิธี |
ให้คล้องแค่สาย Line (L) เส้นเดียว ห้ามคล้องรวบ L กับ N เด็ดขาด |
| จอ OLED ไม่ติด |
การเชื่อมต่อ I2C ผิดพลาด |
เช็คสาย SDA/SCL และตรวจสอบ I2C Address (ปกติคือ 0x3C) |
| บอร์ดรีเซ็ตตัวเองบ่อย |
ไฟเลี้ยงไม่เสถียร |
ใช้อะแดปเตอร์ 5V คุณภาพดี และหลีกเลี่ยงการดึงไฟโหลดหนักๆ จากพอร์ต USB |
คำถามที่พบบ่อย (FAQs)
-
ทำไมต้องมอนิเตอร์ค่า Power Factor (PF)?
ค่า PF คืออัตราส่วนบอกประสิทธิภาพการใช้ไฟ (ควรเข้าใกล้ 1.0) ถ้าค่าต่ำกว่า 0.9 แปลว่าเครื่องใช้ไฟฟ้ามีการสูญเสียพลังงานเยอะ (เช่น มอเตอร์) ซึ่งโรงงานอุตสาหกรรมจะโดนค่าปรับจาก กฟภ./กฟน. การมอนิเตอร์ค่านี้จึงช่วยลดต้นทุนแฝงได้ครับ
-
ระบบนี้ขยายไปใช้กับไฟ 3 เฟสหรือโรงงานได้ไหม?
ได้แน่นอนครับ! คุณสามารถต่อ PZEM-004T หลายๆ ตัวพ่วงกันบนสาย Modbus (UART) เส้นเดียวได้เลย โดยตั้งค่า Address แต่ละตัวไม่ให้ซ้ำกัน (เฟสละตัว)
-
ข้อควรระวังเรื่องความปลอดภัยสูงสุดคืออะไร?
สับคัตเอาต์หรือเบรกเกอร์ (Mains supply) ลงทุกครั้ง! ก่อนจะทำการไขน็อตเชื่อมต่อสายไฟ AC เข้ากับบอร์ด PZEM และที่สำคัญตัวกล่องควรทำจากวัสดุที่ไม่นำไฟฟ้า (เช่น การปริ้นท์ 3D) เพื่อป้องกันอันตรายจากการสัมผัส
*คำเตือน: เนื้อหานี้เป็นการสรุปและเรียบเรียงแนวคิดจากโปรเจกต์ต้นฉบับภาษาอังกฤษ ข้อมูลและขั้นตอนการต่อวงจรบางส่วนอาจถูกย่อทอนเพื่อให้เข้าใจง่าย
ข้อควรระวังอย่างยิ่ง: โปรเจกต์นี้เกี่ยวข้องกับไฟฟ้าแรงดันสูง (Mains AC 220V) ผู้ปฏิบัติงานต้องมีความรู้ด้านไฟฟ้าและตัดกระแสไฟก่อนปฏิบัติงานทุกครั้ง Globalbyteshop จะไม่รับผิดชอบต่อความเสียหายหรืออุบัติเหตุใดๆ ที่เกิดจากการทำตามบทความนี้ ผู้สนใจสามารถศึกษารายละเอียดเชิงลึกทางเทคนิคได้ที่
เว็บไซต์ต้นฉบับ