PUYIOT
(อิทธิรงค์ ทองย้อย (ปุ๋ย))
1
/*
เครื่องมือ การตั้งค่า Address PZEM
=============================
เครื่องมือนี้รวมฟังก์ชันการเปลี่ยน address และการอ่านข้อมูล
การต่อสายฮาร์ดแวร์:
- ESP32 GPIO16 -> PZEM TX
- ESP32 GPIO17 -> PZEM RX
- ESP32 GND -> PZEM GND
- PZEM L, N -> แหล่งจ่ายไฟ (ต้องต่อเพื่อเปลี่ยน address)
ฟีเจอร์:
1. อ่านข้อมูล PZEM อัตโนมัติ (แรงดัน, กระแส, กำลัง, พลังงาน, ความถี่, PF)
2. เปลี่ยน address PZEM โดยตรงผ่านคำสั่ง
3. ทดสอบการสื่อสารกับ PZEM
คำสั่ง:
- 'r' - เปลี่ยนเป็นโหมดอ่านข้อมูล (เริ่มต้น)
- 't' - ทดสอบการสื่อสาร PZEM
- '0xXX' - ตั้ง address PZEM โดยตรง (เช่น 0x05, 0x10, 0xFF)
- 'reset' - รีเซ็ตตัวนับพลังงาน (kWh) เป็นศูนย์
ตัวอย่างการใช้งาน:
1. เริ่มต้น: อ่านข้อมูล PZEM อัตโนมัติทุก 1 วินาที
2. เปลี่ยน address: พิมพ์ "0x05" เพื่อตั้ง address เป็น 0x05
3. ทดสอบ: พิมพ์ "t" เพื่อทดสอบการสื่อสาร
4. อ่านข้อมูล: พิมพ์ "r" เพื่อเปลี่ยนเป็นโหมดอ่านข้อมูล
5. รีเซ็ตพลังงาน: พิมพ์ "reset" เพื่อรีเซ็ตตัวนับ kWh เป็นศูนย์
ช่วง Address: 0x01 ถึง 0xF7 (0x00 และ 0xF8 เป็นที่สงวน)
*/
#include <PZEM004Tv30.h>
// ============================================================================
// การตั้งค่าฮาร์ดแวร์
// ============================================================================
// การต่อสายระหว่าง ESP32 กับ PZEM
#define PZEM_RX_PIN 16 // ESP32 GPIO16 -> PZEM TX
#define PZEM_TX_PIN 17 // ESP32 GPIO17 -> PZEM RX
#define PZEM_SERIAL Serial2 // ใช้ Serial2 สำหรับการสื่อสาร
PZEM004Tv30 pzem(PZEM_SERIAL, PZEM_RX_PIN, PZEM_TX_PIN);
// ============================================================================
// การตั้งค่าซอฟต์แวร์
// ============================================================================
#define MODE_READ_DATA 1 // โหมดสำหรับอ่านข้อมูล PZEM
int currentMode = MODE_READ_DATA; // เริ่มต้นด้วยโหมดอ่านข้อมูล
// ตัวแปรสำหรับจัดการข้อมูลจาก Serial
String inputString = ""; // บัฟเฟอร์สำหรับข้อมูลที่เข้ามาจาก Serial
bool stringComplete = false; // ฟลากแสดงว่าคำสั่งครบถ้วนแล้ว
// ============================================================================
// ฟังก์ชัน SETUP
// ============================================================================
void setup() {
Serial.begin(115200);
// รอให้ Serial port เชื่อมต่อ
while (!Serial) {
delay(10);
}
// แสดงข้อความต้อนรับและคำสั่งที่ใช้ได้
Serial.println("PZEM Complete Tool");
Serial.println("=================");
Serial.println();
Serial.println("Commands:");
Serial.println(" 'r' - Read data mode (default)");
Serial.println(" 't' - Test communication");
Serial.println(" '0xXX' - Set address directly (e.g., 0x05)");
Serial.println(" 'reset' - Reset energy counter (kWh) to zero");
Serial.println();
// ทดสอบการสื่อสารกับ PZEM ก่อน
testCommunication();
}
// ============================================================================
// ลูปหลัก
// ============================================================================
void loop() {
// ตรวจสอบคำสั่งที่เข้ามาจาก Serial
if (Serial.available()) {
char inChar = (char)Serial.read();
// ตรวจจับจุดสิ้นสุดคำสั่ง (newline หรือ carriage return)
if (inChar == '\n' || inChar == '\r') {
stringComplete = true;
} else {
inputString += inChar; // สร้างสตริงคำสั่ง
}
}
// ประมวลผลคำสั่งที่ครบถ้วนแล้ว
if (stringComplete) {
inputString.trim(); // ลบช่องว่าง
handleCommand(inputString); // ดำเนินการคำสั่ง
inputString = ""; // ล้างบัฟเฟอร์
stringComplete = false; // รีเซ็ตฟลาก
}
// ดำเนินการตามโหมด
if (currentMode == MODE_READ_DATA) {
readDataMode(); // อ่านข้อมูล PZEM อัตโนมัติ
}
delay(1000); // รอ 1 วินาทีก่อนรอบถัดไป
}
// ============================================================================
// ตัวจัดการคำสั่ง
// ============================================================================
void handleCommand(String command) {
command.toLowerCase();
if (command == "r") {
// เปลี่ยนเป็นโหมดอ่านข้อมูล
currentMode = MODE_READ_DATA;
Serial.println("Switched to Data Reading Mode");
}
else if (command == "t") {
// ทดสอบการสื่อสาร PZEM
testCommunication();
}
else if (command == "reset") {
// รีเซ็ตตัวนับพลังงาน
resetEnergyCounter();
}
else if (command.startsWith("0x")) {
// แปลง address แบบ hex และตั้งค่าโดยตรง
String hexStr = command.substring(2); // ลบ "0x" ออก
uint8_t newAddr = (uint8_t)strtol(hexStr.c_str(), NULL, 16);
// ตรวจสอบช่วง address (0x01 ถึง 0xF7)
if (newAddr >= 0x01 && newAddr <= 0xF7) {
Serial.print("Setting address to 0x");
Serial.print(newAddr, HEX);
Serial.print("... ");
// พยายามตั้ง address
if (pzem.setAddress(newAddr)) {
Serial.println("SUCCESS");
delay(500); // รอให้ address ถูกตั้งค่า
uint8_t currentAddr = pzem.readAddress();
Serial.print("Current address: 0x");
Serial.println(currentAddr, HEX);
} else {
Serial.println("FAILED");
}
} else {
Serial.println("Invalid address! Must be between 0x01 and 0xF7");
}
}
else {
// คำสั่งไม่รู้จัก - แสดงความช่วยเหลือ
Serial.println("Unknown command. Available commands:");
Serial.println(" 'r' - Read data mode (default)");
Serial.println(" 't' - Test communication");
Serial.println(" '0xXX' - Set address directly (e.g., 0x05)");
Serial.println(" 'reset' - Reset energy counter (kWh) to zero");
}
}
// ============================================================================
// โหมดอ่านข้อมูล
// ============================================================================
void readDataMode() {
// อ่าน address หลายครั้งเพื่อความเสถียร (PZEM บางครั้งคืนค่าที่ไม่สอดคล้องกัน)
uint8_t address = 0;
int validReads = 0;
// ลองอ่าน address 5 ครั้งและใช้ผลที่พบบ่อยที่สุด
uint8_t addresses[5] = {0};
for(int i = 0; i < 5; i++) {
addresses[i] = pzem.readAddress();
delay(150); // หน่วงเวลาเล็กน้อยระหว่างการอ่าน
}
// นับจำนวนครั้งของ address แต่ละค่า
int count2 = 0, count0 = 0, countOther = 0;
for(int i = 0; i < 5; i++) {
if(addresses[i] == 2) count2++; // address ที่กำหนดเองที่พบบ่อยที่สุด
else if(addresses[i] == 0) count0++; // address เริ่มต้น/ข้อผิดพลาด
else countOther++; // address อื่นๆ
}
// ใช้ address ที่พบบ่อยที่สุดสำหรับการแสดงผล
if(count2 >= count0 && count2 >= countOther) {
address = 2;
validReads = count2;
} else if(count0 >= countOther) {
address = 0;
validReads = count0;
} else {
address = addresses[0];
validReads = 1;
}
// แสดง address ปัจจุบันของ PZEM พร้อมตัวบ่งชี้ความเสถียร
Serial.print("Custom Address: 0x");
if(validReads > 0) {
Serial.print(address, HEX);
Serial.print(" (");
Serial.print(validReads);
Serial.print("/5 reads)");
Serial.println();
} else {
Serial.println("ERROR (No valid address read)");
}
// อ่านข้อมูลเซ็นเซอร์ PZEM ทั้งหมด
float voltage = pzem.voltage(); // แรงดันในหน่วยโวลต์
float current = pzem.current(); // กระแสในหน่วยแอมแปร์
float power = pzem.power(); // กำลังในหน่วยวัตต์
float energy = pzem.energy(); // พลังงานในหน่วยกิโลวัตต์-ชั่วโมง
float frequency = pzem.frequency(); // ความถี่ในหน่วยเฮิรตซ์
float pf = pzem.pf(); // ตัวประกอบกำลัง
// ตรวจสอบและแสดงข้อมูลเซ็นเซอร์
if(isnan(voltage)){
Serial.println("Error reading voltage");
} else if (isnan(current)) {
Serial.println("Error reading current");
} else if (isnan(power)) {
Serial.println("Error reading power");
} else if (isnan(energy)) {
Serial.println("Error reading energy");
} else if (isnan(frequency)) {
Serial.println("Error reading frequency");
} else if (isnan(pf)) {
Serial.println("Error reading power factor");
} else {
// แสดงการอ่านค่าเซ็นเซอร์ที่ถูกต้องทั้งหมด
Serial.print("Voltage: "); Serial.print(voltage); Serial.println("V");
Serial.print("Current: "); Serial.print(current); Serial.println("A");
Serial.print("Power: "); Serial.print(power); Serial.println("W");
Serial.print("Energy: "); Serial.print(energy,3); Serial.println("kWh");
Serial.print("Frequency: "); Serial.print(frequency, 1); Serial.println("Hz");
Serial.print("PF: "); Serial.println(pf);
}
Serial.println();
}
// ============================================================================
// ฟังก์ชันทดสอบการสื่อสาร
// ============================================================================
void testCommunication() {
Serial.println("Testing PZEM communication...");
// ทดสอบการอ่านแรงดัน
float voltage = pzem.voltage();
if (!isnan(voltage)) {
Serial.print("Voltage: ");
Serial.print(voltage);
Serial.println("V");
} else {
Serial.println("Voltage reading failed");
}
// ทดสอบการอ่าน address
uint8_t addr = pzem.readAddress();
Serial.print("Address: 0x");
Serial.println(addr, HEX);
// ทดสอบการอ่านความถี่
float frequency = pzem.frequency();
if (!isnan(frequency)) {
Serial.print("Frequency: ");
Serial.print(frequency);
Serial.println("Hz");
} else {
Serial.println("Frequency reading failed");
}
Serial.println(); // บรรทัดว่างเพื่อความอ่านง่าย
}
// ============================================================================
// ฟังก์ชันรีเซ็ตตัวนับพลังงาน
// ============================================================================
void resetEnergyCounter() {
Serial.println("Resetting energy counter...");
// อ่านค่าพลังงานปัจจุบันก่อนรีเซ็ต
float currentEnergy = pzem.energy();
if (!isnan(currentEnergy)) {
Serial.print("Current energy: ");
Serial.print(currentEnergy, 3);
Serial.println(" kWh");
}
// พยายามรีเซ็ตตัวนับพลังงาน
if (pzem.resetEnergy()) {
Serial.println("Energy counter reset SUCCESS");
delay(1000);
// ตรวจสอบการรีเซ็ตโดยอ่านพลังงานอีกครั้ง
float newEnergy = pzem.energy();
if (!isnan(newEnergy)) {
Serial.print("New energy value: ");
Serial.print(newEnergy, 3);
Serial.println(" kWh");
if (newEnergy < 0.001) {
Serial.println("Energy counter successfully reset to zero");
} else {
Serial.println("Warning: Energy counter may not have reset completely");
}
}
} else {
Serial.println("Energy counter reset FAILED");
Serial.println("Make sure PZEM is powered and connected properly");
}
Serial.println();
}