การวัดไฟ 1 เฟส (ไฟบ้าน) ด้วย PZEM-004T-v30 + ESP32

สำหรับ การวัดไฟ 1เฟส (ไฟบ้าน) ด้วย PZEM-004T-v30 + ESP8266 เดิม เราจะใช้การต่อ PZEM-004T-v30 แบบ SoftwareSerial แต่ไม่สามารถใช้กับ ESP32 ได้ นอกจากใช้การต่อแบบ HardSerial ดังนั้น บทความนี้ ผมจะมานำเสนอวิธี การต่อแบบ HardSerial กันครับ โดยมีเทคนิควิธีการดังนี้

  • ดู HardwareSerial.cpp ที่ตำแหน่ง C:\Users\PUYIOT (ชื่อเครื่องคอมพิวเตอร์ของเรา) \AppData \Local \Arduino15\packages\esp32\hardware\esp32\1.0.6\cores\esp32\ HardwareSerial.cpp

1

เปิดขึ้นมา ดูบรรทัดที่ 33 - 57

ในความหมาย คือ Hardware Serial ใน ESP32 มีอินเทอร์เฟซแบบอนุกรมที่รองรับฮาร์ดแวร์ 3 ชุด ดังนี้

  1. UART0 (Serial0) ใช้เพื่อสื่อสารกับ ESP32 สำหรับการเขียนโปรแกรมและระหว่างรีเซ็ต / บูต สำหรับ UART0 (Serial0) นี้ ถูกกำหนดโดยโครงสร้างมาแล้วโดยใช้ PIN GPIO3 (RX) และ GPIO1 (TX) ซึ่งเราไม่สามารถใช้ PIN GPIO3 (RX) และ GPIO1 (TX) นี้ได้ แต่เราอาจเปลี่ยนตำแหน่ง PIN ไปเป็นตำแหน่งอื่นได้ โดยใช้ เป็น UART0 (Serial0) เหมือนเดิม แต่ไปเบลี่ยนหมายเลข PIN จาก rxPin = 3 ไปเป็น rxPin = 18(…สมมุติเป็นขา 18) และ txPin = 1 ไปเป็น txPin = 19(…สมมุติเป็นขา 19)

1

*** กรณีค่า การวัดของ PZEM ไม่ขึ้น ให้สลับหมายเลข PIN RX TX เช่น

แก้แล้วบันทึกไฟล์ HardwareSerial.cpp

หมายเหตุ สำหรับข้อที่ 1 ผู้เขียนยังไม่ได้ทดสอบ แต่มีแนวโน้มว่าเป็นไปได้ และถ้าใช้ได้ โอกาสที่ใช้ PZEM-004T-v30 วัดไฟ 3 เฟส ก็จะมีความเป็นไปได้สูง โดยมีแนวคิดคือ

  • PZEM-004T-v30 ตัวที่ 1 วัดไฟเฟส A ใช้ UART0 (Serial0) โดยใช้วิธีการเปลี่ยนตำแหน่ง PIN ดังกล่าวข้างต้น
  • PZEM-004T-v30 ตัวที่ 2 วัดไฟเฟส B ใช้ UART1 (Serial1) โดยใช้วิธีการเปลี่ยนเปลี่ยนตำแหน่ง PIN หรือใช้ขาตามโครงสร้างที่กำหนดไว้เดิม คือ PIN GPIO9 (RX) และ GPIO10 (TX)
  • PZEM-004T-v30 ตัวที่ 3 วัดไฟเฟส C ใช้ UART2 (Serial2) โดยใช้วิธีการเปลี่ยนเปลี่ยนตำแหน่ง PIN หรือใช้ขาตามโครงสร้างที่กำหนดไว้เดิม คือ PIN GPIO16 (RX) และ GPIO17 (TX)

ในส่วนของ Code ควรเขียนได้ดังนี้

//ใช้ ESP32S  38 pins  ESP32 Core  1.0.6
#include <HardwareSerial.h>
PZEM004Tv30 pzem(&Serial0);
void setup()
{
Serial.begin(115200);
Serial0.begin(9600, SERIAL_8N1);
}

void loop()
{
//เอามาแค่ส่วนหนึ่งของ code ชื่อไฟล์: PZEMSoftwareSerial.ino 
//ใน Library PZEM-004T-v30-master   เพื่อเป็นตัวอย่าง
  float voltage = pzem.voltage();
  if (!isnan(voltage)) {
    Serial.print("Voltage: "); Serial.print(voltage); Serial.println("V");
  } else {
    Serial.println("Error reading voltage");
  }
delay(1000);
}

PUYIOT April 26, 2021, 10:53pm #2

  1. UART1 (Serial1) ไม่ได้ใช้งานและสามารถใช้กับโครงการของเราได้ บอร์ดบางตัวใช้พอร์ตนี้สำหรับการเข้าถึง SPI Flash UART1 (Serial1) นี้ ถูกกำหนดโดยโครงสร้างมาแล้วโดยใช้ PIN GPIO09 (RX) และ GPIO10 (TX) โดยเราสามารถใช้ PIN GPIO9 (RX) และ GPIO10 (TX) นี้ได้เลย หรือเราอาจเปลี่ยนตำแหน่ง PIN ไปเป็นตำแหน่งอื่นได้ โดยใช้ เป็น UART1 (Serial1) เหมือนเดิม แต่ไปเปลี่ยนหมายเลข PIN เอง

image

จาก Code (HardwareSerial.cpp) เราจะเห็นว่า rxPin = RX1; และ txPin = TX1; นั้น หมายถึง เราสามารถเปลี่ยนหมายเลข PIN เองได้ โดยป้อนผ่านตัวแปร ชื่อ RX1 และ TX1

ในส่วนของ Code เขียนได้ดังนี้ (กรณี เปลี่ยนหมายเลข PIN เอง)

//ใช้ ESP32S  38 pins  ESP32 Core  1.0.6
//*** กรณีค่า การวัดของ PZEM ไม่ขึ้น ให้สลับหมายเลข PIN RX1  TX1
#define RX1 14  //(หมายเลขขา 14 เป็นตัวอย่างที่สมมุติขึ้น)
#define TX1 12  //(หมายเลขขา 12 เป็นตัวอย่างที่สมมุติขึ้น)
#include <HardwareSerial.h>
PZEM004Tv30 pzem(&Serial1);

void setup()
{
Serial.begin(115200);
Serial1.begin(9600, SERIAL_8N1, RX1, TX1);
}

void loop()
{
//เอามาแค่ส่วนหนึ่งของ code ชื่อไฟล์: PZEMSoftwareSerial.ino 
//ใน Library PZEM-004T-v30-master   เพื่อเป็นตัวอย่าง
  float voltage = pzem.voltage();
  if (!isnan(voltage)) {
    Serial.print("Voltage: "); Serial.print(voltage); Serial.println("V");
  } else {
    Serial.println("Error reading voltage");
  }
delay(1000);
}


ในส่วนของ Code เขียนได้ดังนี้ (กรณี ใช้หมายเลข PIN ตามโครงสร้างที่กำหนดไว้ RX1 = GPIO9, TX1=GPIO10)

//ใช้ ESP32S  38 pins  ESP32 Core  1.0.6
//*** กรณีค่า การวัดของ PZEM ไม่ขึ้น ให้สลับหมายเลข PIN RX1  TX1
#define RX1 9  //(หมายเลขขา 9 เป็นตามโครงสร้างที่กำหนดไว้)
#define TX1 10  //(หมายเลขขา 10 เป็นตามโครงสร้างที่กำหนดไว้)
#include <HardwareSerial.h>
PZEM004Tv30 pzem(&Serial1);

void setup()
{
Serial.begin(115200);
Serial1.begin(9600, SERIAL_8N1, RX1, TX1);
}

void loop()
{
//เอามาแค่ส่วนหนึ่งของ code ชื่อไฟล์: PZEMSoftwareSerial.ino 
//ใน Library PZEM-004T-v30-master   เพื่อเป็นตัวอย่าง
  float voltage = pzem.voltage();
  if (!isnan(voltage)) {
    Serial.print("Voltage: "); Serial.print(voltage); Serial.println("V");
  } else {
    Serial.println("Error reading voltage");
   }
delay(1000);
}


PUYIOT April 26, 2021, 10:53pm #3

  1. UART2 (Serial2) ไม่ได้ใช้งานและสามารถใช้กับโครงการของเราได้
    UART1 (Serial2) นี้ ถูกกำหนดโดยโครงสร้างมาแล้วโดยใช้ PIN GPIO016 (RX) และ GPIO17 (TX) โดยเราสามารถใช้ PIN GPI16 (RX) และ GPIO17 (TX) นี้ได้เลย หรือเราอาจเปลี่ยนตำแหน่ง PIN ไปเป็นตำแหน่งอื่นได้ โดยใช้ เป็น UART2 (Serial2) เหมือนเดิม แต่ไปเปลี่ยนหมายเลข PIN เอง

image

จาก Code (HardwareSerial.cpp) เราจะเห็นว่า rxPin = RX2; และ txPin = TX2; นั้น หมายถึง เราสามารถเปลี่ยนหมายเลข PIN เองได้ โดยป้อนผ่านตัวแปร ชื่อ RX2 และ TX2

ในส่วนของ Code เขียนได้ดังนี้ (กรณี เปลี่ยนหมายเลข PIN เอง)

//ใช้ ESP32S  38 pins  ESP32 Core  1.0.6
//*** กรณีค่า การวัดของ PZEM ไม่ขึ้น ให้สลับหมายเลข PIN RX1  TX1
#define RX2 14  //(หมายเลขขา 14 เป็นตัวอย่างที่สมมุติขึ้น)
#define TX2 12  //(หมายเลขขา 12 เป็นตัวอย่างที่สมมุติขึ้น)
#include <HardwareSerial.h>
PZEM004Tv30 pzem(&Serial2);

void setup()
{
Serial.begin(115200);
Serial2.begin(9600, SERIAL_8N1, RX2, TX2);
}

void loop()
{
//เอามาแค่ส่วนหนึ่งของ code ชื่อไฟล์: PZEMSoftwareSerial.ino 
//ใน Library PZEM-004T-v30-master   เพื่อเป็นตัวอย่าง
  float voltage = pzem.voltage();
  if (!isnan(voltage)) {
    Serial.print("Voltage: "); Serial.print(voltage); Serial.println("V");
  } else {
    Serial.println("Error reading voltage");
   }
delay(1000);
}

ในส่วนของ Code เขียนได้ดังนี้ (กรณี ใช้หมายเลข PIN ตามโครงสร้างที่กำหนดไว้ RX1 = GPI16, TX1=GPIO17)

//ใช้ ESP32S  38 pins  ESP32 Core  1.0.6
//*** กรณีค่า การวัดของ PZEM ไม่ขึ้น ให้สลับหมายเลข PIN RX1  TX1
#define RX1 16 // (หมายเลขขา 16 เป็นตามโครงสร้างที่กำหนดไว้)
#define TX1 17  //(หมายเลขขา 17 เป็นตามโครงสร้างที่กำหนดไว้)
#include <HardwareSerial.h>
PZEM004Tv30 pzem(&Serial2);

void setup()
{
Serial.begin(115200);
Serial2.begin(9600, SERIAL_8N1, RX2, TX2);
}

void loop()
{
//เอามาแค่ส่วนหนึ่งของ code ชื่อไฟล์: PZEMSoftwareSerial.ino 
//ใน Library PZEM-004T-v30-master   เพื่อเป็นตัวอย่าง
  float voltage = pzem.voltage();
  if (!isnan(voltage)) {
    Serial.print("Voltage: "); Serial.print(voltage); Serial.println("V");
  } else {
    Serial.println("Error reading voltage");
    }
delay(1000);
}

PUYIOT April 27, 2021, 3:35am #4

ปัญหาทางเทคนิค และวิธีการแก้ (อยู่ในขั้นทดสอบ)

  1. จากตัวอย่าง Code ที่ให้ไป สามารถ complie ได้ปกติ แต่เมื่อเรานำไปต่อร่วมกับ Hardware คือ PZEM-004T-v30 จะพบว่า ช่วง Upload code จะไม่สามารถ Upload ได้อย่างสมบูรณ์ เนื่องจาก มี PZEM-004T-v30 มีการต่อ RX และ TX เข้ามาร่วมด้วย จึงทำให้ไม่สามารถ UPLOAD code ได้วิธีการแก้
    ส่วนตัว ผู้เขียนใช้วิธีดังนี้ (จุดประสงค์ เพื่อเลี่ยง การต่อถึงกันของสาย RX หรือ TX ระหว่าง ESP32 กับ PZEM-004T-v30 ในช่วงที่ ESP32 Reboot ในช่วงแรก )
  • Relay 5 Vdc 2 ตัว (Low Active) เพื่อทำหน้าที่ ตัดการทำงานในช่วงที่ ESP32 Reboot ในช่วงแรก

หรือ

โปรเจคนี้ผมใช้ Relay 2 Channel 5V 2A Solid State Relay (SSR) มันเงียบดี

วิธีการ

  1. ตัดสาย RX หรือ TX ที่เชื่อมต่อระหว่าง ESP32 กับ PZEM-004T-v30 ออก
  2. นำปลายสาย 2 ปลาย ที่ตัดออกมา ต่อเข้า Output Relay โดยต่อเข้า ขา Com กับ NO
  3. เขียน code ควบคุมการทำงานของ Relay

***หมายเหตุ จะต่อ เป็น Com กับ NC ก็ได้ คือช่วง ESP32 Reboot ก็ให้ Relay ทำงาน สถานะจะเป็น NO (สาย RX หรือ TX จะถูกตัดขาดออกจากกัน) จากนั้นจะกลายเป็น NC เหมือนเดิม (สาย RX หรือ TX จะถูกต่อติดกัน) โดยมีจุดประสงค์ เพื่อการป้องกัน ไม่ให้ Relay ถูกไฟป้อนตลอดเวลาที่ทำงาน และสามารถประหยัดพลังงานในระบบได้

ตัวอย่างภาพการตัดสาย RX ออกไปเข้า Relay

ESP32S1

ตัวอย่าง Code

const int relay = 26;

void setup()
{
Serial.begin(115200);
pinMode(relay, OUTPUT);
//---------------------------------
ชุดคำสั่งต่างๆ
//---------------------------------
digitalWrite(relay , 0);//สุดท้าย ของ void loop  ให้ใส่ Code Relay ให้ทำงาน
}



PUYIOT April 27, 2021, 9:35am #5

1

PUYIOT April 30, 2021, 10:45am #6

อันนี้เป็นตัวอย่าง นะครับ

เดี๋ยวมาดูบิลค่าไฟกันครับ ว่า ใกล้เคียงกันหรือปล่าว

จะเห็นว่ามีความคลาดเคลื่อนอยู่นะครับ เนื่องจากสาเหตุ

  1. วันที่การพิมพ์ใบเสร็จ หรือวันที่มาตรวจ คลาดเคลื่อนไปจากเดิม 1 วัน ทำให้ ค่าหน่วย Pzem กับ มิเตอร์ของการไฟฟ้า ห่างกันออกไปเยอะ โดยใน PZEM ผมกำหนดวัน การตัดค่าไฟประจำเดือนไว้ทุกวันที่ 17 ของเดือน ในเวลา 9.30 น. แต่ การไฟฟ้ามาตัดค่าไฟประจำเดือนในวันที่ 18 เวลา 9.05น. ผลก็เลยคลาดเคลื่อน
  2. ค่าความคลาดเคลื่อนห่างกัน 429 - 422 = 7 หน่วย
1 Like