#include <WiFi.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
#include <PubSubClient.h>
#include <ModbusMaster.h>
#include <HTTPClient.h>
#include <BlynkSimpleEsp32.h>
#include <WiFiClient.h>
HardwareSerial PZEMSerial(2);
// ตั้งค่า pin สำหรับต่อกับ MAX485
#define MAX485_RO 16
#define MAX485_RE 5
#define MAX485_DE 4
#define MAX485_DI 17
// Update these with values suitable for your network.
#define wifi_ssid "Mark" //"iPhone"
#define wifi_password "111111111" //"12345678"
#define BLYNK_PRINT Serial
#define BLYNK_TEMPLATE_ID "TMPL6AgLJsnTg"
#define BLYNK_TEMPLATE_NAME "MM"
#define BLYNK_AUTH_TOKEN "29M1je1JivLkzpRGfC-IO0uuSIdy1Au1"
// Address ของ PZEM-017 : 0x01-0xF7
static uint8_t pzemSlaveAddr = 0x01;
// ตั้งค่า shunt -->> 0x0000-100A, 0x0001-50A, 0x0002-200A, 0x0003-300A
static uint16_t NewshuntAddr = 0x0003;
// Line Notify token -
#define LINE_NOTIFY_TOKEN "rIDYQKgjVZ9WbJhHtr5PckbHs5qRjOtTIuIsoA7JIc7"
WiFiClient espClient; // ESP32 in Station/Client mode
PubSubClient mqttClient(espClient);
ModbusMaster node;
float PZEMVoltage, PZEMCurrent, PZEMPower, PZEMEnergy;
unsigned long startMillisPZEM; /* start counting time for LCD Display */
unsigned long currentMillisPZEM; /* current counting time for LCD Display */
const unsigned long periodPZEM = 1000; // refresh every X seconds (in seconds) in LED Display. Default 1000 = 1 second
unsigned long startMillisReadData; /* start counting time for data collection */
unsigned long startMillis1; // to count time during initial start up (PZEM Software got some error so need to have initial pending time)
void wifiSetup() {
Serial.println();
Serial.print("Connecting to ");
Serial.println(wifi_ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(wifi_ssid, wifi_password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
//Blynk.config(auth);
//Blynk.connect();
}
}
void setup()
{
lcd.begin();
lcd.backlight();
startMillis1 = millis();
Serial.begin(115200);
PZEMSerial.begin(9600, SERIAL_8N2, MAX485_RO, MAX485_DI); // software serial สำหรับติดต่อกับ MAX485
Blynk.begin(BLYNK_AUTH_TOKEN, wifi_ssid, wifi_password);
startMillisPZEM = millis(); /* Start counting time for run code */
pinMode(MAX485_RE, OUTPUT); /* Define RE Pin as Signal Output for RS485 converter. Output pin means Arduino command the pin signal to go high or low so that signal is received by the converter*/
pinMode(MAX485_DE, OUTPUT); /* Define DE Pin as Signal Output for RS485 converter. Output pin means Arduino command the pin signal to go high or low so that signal is received by the converter*/
digitalWrite(MAX485_RE, 0); /* Arduino create output signal for pin RE as LOW (no output)*/
digitalWrite(MAX485_DE, 0); /* Arduino create output signal for pin DE as LOW (no output)*/
node.preTransmission(preTransmission); // Callbacks allow us to configure the RS485 transceiver correctly
node.postTransmission(postTransmission);
node.begin(pzemSlaveAddr, PZEMSerial);
delay(1000); /* after everything done, wait for 1 second */
// รอครบ 5 วินาที แล้วตั้งค่า shunt และ address
while (millis() - startMillis1 < 5000) {
delay(500);
Serial.print(".");
wifiSetup();
delay(1000); // Allow the hardware to sort itself out
}
setShunt(pzemSlaveAddr); // ตั้งค่า shunt
changeAddress(0xF8, pzemSlaveAddr); // ตั้งค่า address 0x01 ซื่งเป็นค่า default ของตัว PZEM-017
// resetEnergy(); // รีเซ็ตค่า Energy[Wh] (หน่วยใช้ไฟสะสม)
}
void sendLineNotify(String message) {
HTTPClient http;
// กำหนด URL สำหรับ Line Notify
String url = "https://notify-api.line.me/api/notify";
http.begin(url);
// กำหนด Header ในการส่งข้อมูลไปยัง Line Notify
http.addHeader("Authorization", "Bearer " + String(LINE_NOTIFY_TOKEN));
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
// กำหนดข้อความที่จะส่ง
String postData = "message=" + message;
// ส่งข้อมูลไปยัง Line Notify
int httpResponseCode = http.POST(postData);
// ตรวจสอบการส่งข้อมูล
if (httpResponseCode > 0) {
Serial.print("Line Notify HTTP Response Code: ");
Serial.println(httpResponseCode);
} else {
Serial.println("Error sending message to Line Notify");
}
http.end();
}
void loop()
{
currentMillisPZEM = millis();
// อ่านค่าจาก PZEM-017
if (currentMillisPZEM - startMillisPZEM >= periodPZEM) /* for every x seconds, run the codes below*/
{
uint8_t result; /* Declare variable "result" as 8 bits */
result = node.readInputRegisters(0x0000, 6); /* read the 9 registers (information) of the PZEM-014 / 016 starting 0x0000 (voltage information) kindly refer to manual)*/
if (result == node.ku8MBSuccess) /* If there is a response */
{
uint32_t tempdouble = 0x00000000; /* Declare variable "tempdouble" as 32 bits with initial value is 0 */
PZEMVoltage = node.getResponseBuffer(0x0000) / 100.0; /* get the 16bit value for the voltage value, divide it by 100 (as per manual) */
// 0x0000 to 0x0008 are the register address of the measurement value
PZEMCurrent = node.getResponseBuffer(0x0001) / 100.0; /* get the 16bit value for the current value, divide it by 100 (as per manual) */
tempdouble = (node.getResponseBuffer(0x0003) << 16) + node.getResponseBuffer(0x0002); /* get the power value. Power value is consists of 2 parts (2 digits of 16 bits in front and 2 digits of 16 bits at the back) and combine them to an unsigned 32bit */
PZEMPower = tempdouble / 10.0; /* Divide the value by 10 to get actual power value (as per manual) */
tempdouble = (node.getResponseBuffer(0x0005) << 16) + node.getResponseBuffer(0x0004); /* get the energy value. Energy value is consists of 2 parts (2 digits of 16 bits in front and 2 digits of 16 bits at the back) and combine them to an unsigned 32bit */
PZEMEnergy = tempdouble;
}
else // ถ้าติดต่อ PZEM-017 ไม่ได้ ให้ใส่ค่า NAN(Not a Number)
{
PZEMVoltage = NAN;
PZEMCurrent = NAN;
PZEMPower = NAN;
PZEMEnergy = NAN;
}
// แสดงค่าที่ได้จากบน Serial monitor
lcd.setCursor(0, 0);
lcd.print("V = ");
lcd.setCursor(4,0);
lcd.print(PZEMVoltage);
lcd.setCursor(10,0);
lcd.print("I=");
lcd.setCursor(12,0);
lcd.print(PZEMCurrent);
lcd.setCursor(0, 1);
lcd.print("W = ");
lcd.setCursor(4, 1);
lcd.print(PZEMPower);
lcd.setCursor(8,1);
lcd.print("KWh=");
lcd.setCursor(12,1);
lcd.print(PZEMEnergy);
Serial.print("Vdc : "); Serial.print(PZEMVoltage); Serial.println(" V ");
Serial.print("Idc : "); Serial.print(PZEMCurrent); Serial.println(" A ");
Serial.print("Power : "); Serial.print(PZEMPower); Serial.println(" W ");
Serial.print("Energy : "); Serial.print(PZEMEnergy); Serial.println(" Wh ");
delay(10000);
sendLineNotify("Vdc : " + String(PZEMVoltage) + " [v]" +
"\nIdc :" + String(PZEMCurrent) + " A " +
"\nPower :" + String(PZEMPower) + " W " +
"\nEnergy :" + String(PZEMEnergy) + " Wh ");
//Blynk.virtualWrite(V0, PZEMVoltage); //
//Blynk.virtualWrite(V1, PZEMCurrent); //
// Blynk.virtualWrite(V2, PZEMPower); //
// Blynk.virtualWrite(V3, PZEMEnergy); //
Blynk.run();
startMillisPZEM = currentMillisPZEM ;
/* Set the starting point again for next counting time */
}
}
void preTransmission() /* transmission program when triggered*/
{
/* 1- PZEM-017 DC Energy Meter */
if (millis() - startMillis1 > 5000) // Wait for 5 seconds as ESP Serial cause start up code crash
{
digitalWrite(MAX485_RE, 1); /* put RE Pin to high*/
digitalWrite(MAX485_DE, 1); /* put DE Pin to high*/
delay(1); // When both RE and DE Pin are high, converter is allow to transmit communication
}
}
void postTransmission() /* Reception program when triggered*/
{
/* 1- PZEM-017 DC Energy Meter */
if (millis() - startMillis1 > 5000) // Wait for 5 seconds as ESP Serial cause start up code crash
{
delay(3); // When both RE and DE Pin are low, converter is allow to receive communication
digitalWrite(MAX485_RE, 0); /* put RE Pin to low*/
digitalWrite(MAX485_DE, 0); /* put DE Pin to low*/
}
}
void setShunt(uint8_t slaveAddr) //Change the slave address of a node
{
/* 1- PZEM-017 DC Energy Meter */
static uint8_t SlaveParameter = 0x06; /* Write command code to PZEM */
static uint16_t registerAddress = 0x0003; /* change shunt register address command code */
uint16_t u16CRC = 0xFFFF; /* declare CRC check 16 bits*/
u16CRC = crc16_update(u16CRC, slaveAddr); // Calculate the crc16 over the 6bytes to be send
u16CRC = crc16_update(u16CRC, SlaveParameter);
u16CRC = crc16_update(u16CRC, highByte(registerAddress));
u16CRC = crc16_update(u16CRC, lowByte(registerAddress));
u16CRC = crc16_update(u16CRC, highByte(NewshuntAddr));
u16CRC = crc16_update(u16CRC, lowByte(NewshuntAddr));
preTransmission(); /* trigger transmission mode*/
PZEMSerial.write(slaveAddr); /* these whole process code sequence refer to manual*/
PZEMSerial.write(SlaveParameter);
PZEMSerial.write(highByte(registerAddress));
PZEMSerial.write(lowByte(registerAddress));
PZEMSerial.write(highByte(NewshuntAddr));
PZEMSerial.write(lowByte(NewshuntAddr));
PZEMSerial.write(lowByte(u16CRC));
PZEMSerial.write(highByte(u16CRC));
delay(100);
postTransmission(); /* trigger reception mode*/
delay(2000);
}
void resetEnergy() // reset energy for Meter 1
{
uint16_t u16CRC = 0xFFFF; /* declare CRC check 16 bits*/
static uint8_t resetCommand = 0x42; /* reset command code*/
uint8_t slaveAddr = pzemSlaveAddr; // if you set different address, make sure this slaveAddr must change also
u16CRC = crc16_update(u16CRC, slaveAddr);
u16CRC = crc16_update(u16CRC, resetCommand);
preTransmission(); /* trigger transmission mode*/
PZEMSerial.write(slaveAddr); /* send device address in 8 bit*/
PZEMSerial.write(resetCommand); /* send reset command */
PZEMSerial.write(lowByte(u16CRC)); /* send CRC check code low byte (1st part) */
PZEMSerial.write(highByte(u16CRC)); /* send CRC check code high byte (2nd part) */
delay(100);
postTransmission(); /* trigger reception mode*/
delay(2000);
}
void changeAddress(uint8_t OldslaveAddr, uint8_t NewslaveAddr) //Change the slave address of a node
{
/* 1- PZEM-017 DC Energy Meter */
static uint8_t SlaveParameter = 0x06; /* Write command code to PZEM */
static uint16_t registerAddress = 0x0002; /* Modbus RTU device address command code */
uint16_t u16CRC = 0xFFFF; /* declare CRC check 16 bits*/
u16CRC = crc16_update(u16CRC, OldslaveAddr); // Calculate the crc16 over the 6bytes to be send
u16CRC = crc16_update(u16CRC, SlaveParameter);
u16CRC = crc16_update(u16CRC, highByte(registerAddress));
u16CRC = crc16_update(u16CRC, lowByte(registerAddress));
u16CRC = crc16_update(u16CRC, highByte(NewslaveAddr));
u16CRC = crc16_update(u16CRC, lowByte(NewslaveAddr));
preTransmission(); /* trigger transmission mode*/
PZEMSerial.write(OldslaveAddr); /* these whole process code sequence refer to manual*/
PZEMSerial.write(SlaveParameter);
PZEMSerial.write(highByte(registerAddress));
PZEMSerial.write(lowByte(registerAddress));
PZEMSerial.write(highByte(NewslaveAddr));
PZEMSerial.write(lowByte(NewslaveAddr));
PZEMSerial.write(lowByte(u16CRC));
PZEMSerial.write(highByte(u16CRC));
delay(100);
postTransmission(); /* trigger reception mode*/
delay(2000);
}
แล้วมันrunไม่ได้
Arduino: 1.8.19 (Windows 10), Board: "Arduino Nano ESP32, With FAT partition (default), By Arduino pin (default), Normal mode (TinyUSB)"
In file included from c:\Users\Acer\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.6\libraries\WiFi\libraries\Blynk\src/BlynkApiArduino.h:14,
from c:\Users\Acer\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.6\libraries\WiFi\libraries\Blynk\src/BlynkSimpleEsp32.h:20,
from D:\arduionCodes\Pzem017-main\PZEM017-Serial-MQTT-EnergyReset\05\05.ino:7:
c:\Users\Acer\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.6\libraries\WiFi\libraries\Blynk\src/Blynk/BlynkApi.h:39:6: error: #error "Please specify your BLYNK_TEMPLATE_ID and BLYNK_TEMPLATE_NAME"
#error "Please specify your BLYNK_TEMPLATE_ID and BLYNK_TEMPLATE_NAME"
^~~~~
exit status 1
Error compiling for board Arduino Nano ESP32.
This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.
ผมใช่ BLYNK_TEMPLATE_ID and BLYNK_TEMPLATE_NAME" แทน ลงไปก็ตรง กับblynk