ว่าด้วยเรื่อง Pzem 004t v3.0 จะวัดพลังงาน+ค่าใช้จ่าย/เดือน

สวัสดีครับสำหรับโพสต์นี้ผมจะทำโปรเจคเกี่ยวกับการวัดค่าพลังงานไฟฟ้าบวกกับค่าใช้จ่ายต่อเดือนที่บ้านด้วย Pzem 004t v3.0 + esp 8266+ LCD 2004+Blynk+ … ซึ่งก็มีหลายท่านนะครับที่ทำตัวนี้อยู่แล้ว ถ้ามีอะไรแนะนำก็ขอความกรุณาด้วยนะครับจะได้นำไปวิเคราะห์และเพิ่มเติมเข้าไปในโปรเจคของผม สาเหตุที่ตั้งกระทู้ใหม่ก็เพราะว่าผมต้องการที่จะค้นหาข้อมูลในกรณีที่ต้องกลับมาทบทวนซ้ำอีกครั้ง ดังนั้นจึงไม่ได้เอาไปนำเสนอไว้ในโพสต์อื่นๆ สำหรับตัว Project นี้ถ้ามีเวลาผมจะมาลงรายละเอียดอีกทีนะครับ ตอนนี้ถ้าใครอยากดูคลิปที่ผมทำก็ชมคลิปกันไปก่อน การพูดอาจจะตะกุกตะกักหน่อย เพราะไม่ได้คุยกับคนมาระยะหนึ่งแล้ว อิ…อิ…แต่บอกไว้ก่อนนะครับว่าผมยังต้องมีส่วนที่เพิ่มเติมอีก

หรือท่านใดที่ชมคลิปแล้ว คิดว่าแค่นี้พอแล้ว ก็ลอง Code ดูครับ แก้ไข 01/03/2563 23:42

  • รับค่าข้อความจาก Blynk เข้ามา เก็บในรูปแบบ String /แปลงค่าจาก String เป็น Float/เขียนค่า ที่เป็น Float ลงใน EEPROM Address 0 /ใช้ในกรณีที่ ไม่ได้เชื่อมต่อกับ App Blynk ก็ยังสามารถดูค่าทั้งหมดได่้อย่างถูกต้อง แต่ทุกครั้งที่มีการเปลี่ยนค่า Ft จะต้องถูกกำหนดค่าผ่าน App Blynk มาทุกครั้ง
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include <FS.h>
#include <DNSServer.h>
#include <ArduinoJson.h>
//------------------------------------------------------------------------------------------------------------------------//
#include <BlynkSimpleEsp8266.h>
//------------------------------------------------------------------------------------------------------------------------//

#include <EEPROM.h>


//------------------------------        การกำหนดค่าในส่วนของ Blynk         ------------------------------------------------//
#define BLYNK_DEBUG
#define BLYNK_PRINT Serial

int blynkIsDownCount = 0;

char blynk_token[34] = "8dwBaSBOE8htLFrK37JPAYB1hgtm8TaQkU";//ใส่ Blynk_token ของเราที่ Blynk ส่งมาทาง Email ตอน Create Project ใหม่

BlynkTimer timer;





//----------------------------------  กำหนดหมายเลขของขาของ Node MCU ESP 8266  --------------------------------------------//

#define D0 16 // ใช้ไฟ LED สีฟ้า ของบอร์ด MCU ESP8266 ให้มีสัญญาณไฟกระพริบ ตาม Code ที่เขียน

#define D3 0 // ใช้เป็นปุ่มกด เพื่อเข้า AP Config ได้ตามความต้องการของผู้ใช้ + เป็นปุ่ม Reset Energy (kWh)
//------------------------------------------------------------------------------------------------------------------------//


#include <Scheduler.h>



// ------------------------โหลดไลบารี่ของจอ LCD มาใช้งาน โดยแบบนี้จะสามารถ Create Characterได้ ---------------------------------//
#include <LCD.h>                          // บรรทัดที่ 1 และ 2 สามารถสลับบรรทัดกันได้
#include <Wire.h>                         // บรรทัดที่ 1 และ 2 สามารถสลับบรรทัดกันได้
#include <LiquidCrystal_I2C.h>            //บรรทัดนี้ต้องอยู่ในบรรทัดที่ 3 เท่านั้น ถ้าไปอยู่บรรทัดที่ 1 และ 2 จะคอมไพล์ไม่ผ่าน
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); //// ตั้งค่า Address ของอุปกรณ์จอ LCD ที่มีการเชื่อมต่อแบบ I2C
//------------------------------------------------------------------------------------------------------------------------//





bool shouldSaveConfig = false;

//callback notifying us of the need to save config
void saveConfigCallback () {
  Serial.println("Should save config");
  shouldSaveConfig = true;
}
//------------------------------------------------------------------------------------------------------------------------//


//-------------สำหรับ Server local ที่แจกให้ เพิ่ม **** แค่ 2 บรรทัดนี้--------------   (ถ้าเป็น Blynk Server ปกติไม่ต้องใส่)  ----------//
char server[] = "oasiskit.com";
int port = 8080;
//------------------------------------------------------------------------------------------------------------------------//



#include <PZEM004Tv30.h>
PZEM004Tv30 pzem(12, 13); // 12(D6)=RX , 13(D7) = TX  :  TX->D6(GPIO012),RX->D7(GPIO013)
//ฉันเพิ่งเปลี่ยน PZEM004T.h บรรทัด 34 (#define PZEM_DEFAULT_ADDR 0x0F เป็น 0x001)ที่บ้าน
float Ft;
String Ft_1;
float Ft_2;

float Electric_bill;
float kWh_1;
float kWh_2;
float kWh_3;
float kWh_T;
float kWh_T1;

int Blynkreset_kWh;
WidgetLED led_rst_kWh(V12);//จะ ON เมื่อกดปุ่มค้างไว้ 5 วินาที


BLYNK_WRITE(V10)

{
  Ft_1 = param.asString();//รับค่าข้อความจาก Blynk เข้ามา เก็บในรูปแบบ String

  Ft_2 = Ft_1.toFloat();//แปลงค่าจาก String เป็น Float
  //เราป้อนค่า จาก Blynk เป็น String = -0.116 ให้ Ft_1 รับค่ามา
  //Ft จะรับค่าการแปลง จาก Ft_1 เป็น Float = -0.12
  //จะเห็นว่า ค่า Folat จะปัดค่าเป็นจุดทศนิยม ไปเป็น 2 ตำแหน่งจะไม่มากกว่านี้เนื่องด้วยความสามารถตัวของอุปกรณ์เอง
  //ทำให้ค่าเพี้ยนไป ประมาณ 3.44%  ทำให้ค่าไฟ/เดือน น้อยกว่าค่าจริงไป 3.44% ประมาณ 0.64(150หน่วย) - 2.14(500หน่วย) บาท
  //โปรเจคนี้จะไม่เปลี่ยนค่า ใดๆ เพราะค่าต่างกันเล็กน้อย (1 เดือน ใช้ไฟไม่ถึง 500 หน่วยแน่นอน)

  //ชุดนี้ทำขึ้นในกรณีที่ ไม่ได้เชื่อมต่อกับ App Blynk ก็ยังสามารถดูค่าทั้งหมดได่อย่างถูกต้อง แต่ทุกครั้งที่มีการเปลี่ยนค่า Ft จะต้องถูกกำนดค่าผ่าน App Blynk มาทุกครั้ง
  EEPROM.put(0, Ft_2); //เขียนค่า Ft_2 ที่เป็น Float ลงใน Address 0
  EEPROM.commit(); //จบการเขียน

  Ft = EEPROM.get(0, Ft_2);//อ่าน ค่า Ft_2 ที่เป็น Float จาก Address 0
  Serial.println(Ft);//แสดงค่า Ft_2 ใน Serial Monitor
  lcd.clear();
  delay(200);

}


BLYNK_WRITE(V11)//Reset kWh ภายใน 5 วินาที
{

  int pinValue = param.asInt();
  if (pinValue == 1) {
    Blynkreset_kWh = 1;
  }
  if (pinValue == 0) {
    Blynkreset_kWh = 0;
  }
}








class Task_1 : public Task {
  protected:
    void setup() { }


    void loop()  {

      //-----------------------------------------------------------------------------------------------------------//
      if (Blynk.connected())
      {
        Blynk.run();
        digitalWrite(D0, LOW);

      } else {

        digitalWrite(D0, LOW);
        delay(200);
        digitalWrite(D0, HIGH);
        delay(100);

      }
      //-----------------------------------------------------------------------------------------------------------//

      timer.run();//ให้เวลาของ Blynk ทำงาน

      delay(1000);

    }
} Task_1_task;




class Task_2 : public Task {
  protected:
    void setup() { }


    void loop()  {
      //      Serial.println(Ft_1);
      //      Serial.print("Ft:");
      //      Serial.println(Ft);
      //
      //      Serial.print("Ft:");
      //      Serial.println(Ft, 3);


      float voltage = pzem.voltage();
      if (voltage != NAN) {
        //Serial.print("Voltage: "); Serial.print(voltage); Serial.println("V");
        lcd.setCursor(0, 0);
        lcd.print("V: ");
        lcd.setCursor(3, 0);
        lcd.print(voltage, 2);
        Blynk.virtualWrite(V1, voltage);
      } else {
        //Serial.println("Error reading voltage");
        //        lcd.setCursor(0, 0);
        //        lcd.print((char)0x20);
        //        lcd.print((char)0x20);
      }


      float current = pzem.current();
      if (current != NAN) {
        //Serial.print("Current: "); Serial.print(current); Serial.println("A");
        lcd.setCursor(12, 0);
        lcd.print("A: ");
        lcd.setCursor(15, 0);
        lcd.print(current, 2);
        Blynk.virtualWrite(V2, current);
      } else {
        //Serial.println("Error reading current");
        //        lcd.setCursor(15, 0);
        //        lcd.print((char)0x20);
        //        lcd.print((char)0x20);
      }

      float power = pzem.power();
      if (current != NAN) {
        //Serial.print("Power: "); Serial.print(power); Serial.println("W");
        lcd.setCursor(0, 1);
        lcd.print("W: ");
        lcd.setCursor(3, 1);
        lcd.print(power, 2);
        Blynk.virtualWrite(V3, power);
      } else {
        //Serial.println("Error reading power");
        //        lcd.setCursor(3, 1);
        //        lcd.print((char)0x20);
        //        lcd.print((char)0x20);
      }

      float energy = pzem.energy();
      if (current != NAN) {
        //Serial.print("Energy: "); Serial.print(energy, 3); Serial.println("kWh");
        lcd.setCursor(10, 1);
        lcd.print("kWh: ");
        lcd.setCursor(15, 1);
        lcd.print(energy, 2);
        Blynk.virtualWrite(V4, energy);
      } else {
        //Serial.println("Error reading energy");
        //        lcd.setCursor(15, 1);
        //        lcd.print((char)0x20);
        //        lcd.print((char)0x20);
      }

      float frequency = pzem.frequency();
      if (current != NAN) {
        //Serial.print("Frequency: "); Serial.print(frequency, 1); Serial.println("Hz");
        lcd.setCursor(0, 2);
        lcd.print("Hz: ");
        lcd.setCursor(3, 2);
        lcd.print(frequency, 1);
        Blynk.virtualWrite(V5, frequency);
      } else {
        //Serial.println("Error reading frequency");
        //        lcd.setCursor(3, 2);
        //        lcd.print((char)0x20);
        //        lcd.print((char)0x20);
      }

      float pf = pzem.pf();
      if (current != NAN) {
        //Serial.print("PF: "); Serial.println(pf);
        lcd.setCursor(11, 2);
        lcd.print("PF: ");
        lcd.setCursor(15, 2);
        lcd.print(pf, 2);
        Blynk.virtualWrite(V6, pf);
      } else {
        //Serial.println("Error reading power factor");
        //        lcd.setCursor(15, 2);
        //        lcd.print((char)0x20);
        //        lcd.print((char)0x20);
      }






      //Serial.println();
      delay(2000);

    }//loop



} Task_2_task;





class Task_3 : public Task {
  protected:
    void setup() { }



    void loop()  {

      float energy = pzem.energy();

      // กดปุ่มที่หน้าเครื่อง 5 วินาที เพื่อ Reset kWh
      if (digitalRead(D3) == LOW) {
        delay(5000);
        pzem.resetEnergy();
      }

      // กดปุ่มจาก Blynk 5 วินาที เพื่อ Reset kWh กดค้างจนกว่า LED จะดับ
      if (Blynkreset_kWh == 1) {
        led_rst_kWh.on();
        delay(5000);
        pzem.resetEnergy();
        led_rst_kWh.off();

      }
      if (Blynkreset_kWh == 0) {

        led_rst_kWh.off();

      }



      //1.2 อัตราปกติปริมาณการใช้พลังงานไฟฟ้าเกินกว่า 150 หน่วยต่อเดือน
      //ที่บ้านเราใช้ค่านี้ ประเภท 1125 คือ ใช้ไฟฟ้าเกิน 150 หน่วย/เดือน อัตราค่าบริการ 38.22 บาท/เดือน

      //1     -  150 หน่วยละ 3.2484  บาท

      if (energy >= 1 && energy <= 150) {
        kWh_1 = energy * 3.2484;
      }
      if (energy > 150) {
        kWh_1 = 150 * 3.2484;
      }



      //หน่วยที่  151    -  400 หน่วยละ 4.2218  บาท

      if (energy >= 151 && energy <= 400) {
        kWh_2 = energy * 4.2218;
      }
      if (energy > 400) {
        kWh_2 = 250 * 4.2218;
        //401        -- >   หน่วยละ 4.4217  บาท
        kWh_3 = (energy-400) * 4.4217;
      }



      //รวมค่าใช้จ่ายทั้งหมด
      kWh_T = kWh_1 + kWh_2 + kWh_3;
      kWh_T1 = kWh_T + 38.22 + (energy * Ft);

      Electric_bill = kWh_T1 + (kWh_T1 * 0.07);

      lcd.setCursor(0, 3);
      lcd.print("Bill/M: ");
      lcd.setCursor(8, 3);
      lcd.print(Electric_bill , 2);
      lcd.setCursor(15, 3);
      lcd.print("Bath");
      Blynk.virtualWrite(V7, Electric_bill);



      delay(2000);

    }//loop





} Task_3_task;













//------------------------------------------------------------------------------------------------------------------------//
//*********************************************       void setup        **************************************************//
//------------------------------------------------------------------------------------------------------------------------//

void setup() {

  //-------IO NODE MCU Esp8266-------//
  pinMode(D0, OUTPUT);      //กำหนดโหมดใช้งานให้กับขา D0 เป็นขา สัญญาณไฟ ในสภาวะต่างๆ
  pinMode(D3, INPUT_PULLUP);//กำหนดโหมดใช้งานให้กับขา D3 เป็นขา กดปุ่ม ค้าง เพื่อตั้งค่า AP config

  // ให้ LED ทั้งหมดดับก่อน
  digitalWrite(D0, LOW);//ให้หลอด LED สีฟ้าดับก่อน

  //-------------------------------//


  Serial.begin(115200);
  //-------------------------------//
  EEPROM.begin(512);
  //ต้องการให้รู้ว่า ค่า Ft ที่เป็น Float ที่เก็บใน EEPROM มีค่า = ค่าที่ถูกป้อนมาจาก Bylnk จริงๆ
  Ft = EEPROM.get(0, Ft_2);
  Serial.print("Ft in Void setup : ");
  Serial.println( Ft);
  
  lcd.clear();
  lcd.begin(20, 4);
  lcd.clear();



    //read configuration from FS json
  Serial.println("mounting FS...");//แสดงข้อความใน Serial Monitor

  if (SPIFFS.begin()) {
    Serial.println("mounted file system");//แสดงข้อความใน Serial Monitor

    if (SPIFFS.exists("/config.json")) {
      //file exists, reading and loading
      Serial.println("reading config file");//แสดงข้อความใน Serial Monitor
      File configFile = SPIFFS.open("/config.json", "r");

      if (configFile) {
        Serial.println("opened config file");
        size_t size = configFile.size();
        // Allocate a buffer to store contents of the file.
        std::unique_ptr<char[]> buf(new char[size]);

        configFile.readBytes(buf.get(), size);
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject(buf.get());
        json.printTo(Serial);
        if (json.success()) {
          Serial.println("\nparsed json");//แสดงข้อความใน Serial Monitor
          strcpy(blynk_token, json["blynk_token"]);

        } else {
          Serial.println("failed to load json config");//แสดงข้อความใน Serial Monitor
        }
      }
    }
  } else {
    Serial.println("failed to mount FS");//แสดงข้อความใน Serial Monitor
  }
  //end read






  //**************************        AP AUTO CONNECT   ********************************************//

  WiFiManagerParameter custom_blynk_token("blynk", "blynk token", blynk_token, 34);

  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;

  //set config save notify callback
  wifiManager.setSaveConfigCallback(saveConfigCallback);
  wifiManager.addParameter(&custom_blynk_token);
  lcd.clear();
  lcd.setCursor(0, 0);//กำหนดตำแหน่งที่0 ของแถวที่ 0 (อยู่ข้างบนสุดของจอ LCD)
  lcd.print("Wait 5 Sec AP Config"); //แถวที่ 0  ตำแหน่งที่  0  ให้แสดงตัวอักษร

  for (int i = 5; i > -1; i--) {  // นับเวลาถอยหลัง 5 วินาทีก่อนกดปุ่ม AP Config
    digitalWrite(D0, HIGH);
    delay(500);
    digitalWrite(D0, LOW);
    delay(500);
    Serial.print (String(i) + " ");//แสดงข้อความใน Serial Monitor
    lcd.setCursor(10, 1);
    lcd.print(String(i) + " ");
    lcd.setCursor(0, 2);
    lcd.print("Button Press->config");

  }


  if (digitalRead(D3) == LOW) {
    digitalWrite(D0, HIGH);
    Serial.println("Button Pressed");//แสดงข้อความใน Serial Monitor
    lcd.clear();
    lcd.setCursor(4, 0);
    lcd.print("Button Pressed");
    lcd.setCursor(0, 1);
    lcd.print("Smartphone AP Config ");
    wifiManager.resetSettings();//ให้ล้างค่า SSID และ Password ที่เคยบันทึกไว้


    // wifiManager.autoConnect(); ใช้สร้างชื่อแอคเซสพอยต์อันโนมัติจาก ESP + ChipID

    if (!wifiManager.autoConnect("PUYIOT PZEM")) {
      Serial.println("failed to connect and hit timeout");//แสดงข้อความใน Serial Monitor
      delay(3000);
      //reset and try again, or maybe put it to deep sleep
      ESP.reset();
      delay(5000);

    }
  }



  Serial.println("Connected.......OK!)");//แสดงข้อความใน Serial Monitor
  strcpy(blynk_token, custom_blynk_token.getValue());
  lcd.clear();
  lcd.setCursor(2, 2);
  lcd.print("Connected....OK!");
  delay(1000);


  //save the custom parameters to FS
  if (shouldSaveConfig) {
    Serial.println("saving config");//แสดงข้อความใน Serial Monitor
    lcd.setCursor(4, 1);
    lcd.print("saving config");
    delay(1000);
    DynamicJsonBuffer jsonBuffer;
    JsonObject& json = jsonBuffer.createObject();
    json["blynk_token"] = blynk_token;
    File configFile = SPIFFS.open("/config.json", "w");

    if (!configFile) {
      Serial.println("failed to open config file for writing");//แสดงข้อความใน Serial Monitor
      lcd.setCursor(4, 1);
      lcd.print("Failed> open config");
      delay(1000);
    }
    json.printTo(Serial);
    json.printTo(configFile);
    configFile.close();
    //end save
  }

  //**************************    จบ    AP AUTO CONNECT   *****************************************//




  Serial.println("local ip");
  delay(100);
  Serial.println(WiFi.localIP());
  Serial.println("gateway");
  delay(100);
  Serial.println(WiFi.gatewayIP());
  Serial.println("subnetMask");
  delay(100);
  Serial.println(WiFi.subnetMask());
  Serial.println("SSID");
  delay(100);
  Serial.println(WiFi.SSID());
  Serial.println("Password");
  delay(100);
  Serial.println(WiFi.psk());




  Blynk.config(blynk_token, server, port);
  ////เริ่มการเชื่อมต่อ Blynk Server*********สำหรับ Server local ที่แจกให้ เพิ่ม, server, port  แค่นี้จบ



  timer.setInterval(30000L, reconnecting);  //Function reconnect



  timer.setInterval(30000L, reconnecting);  //Function reconnect

  //ตั้งเวลาส่งข้อมูลให้ Blynk Server ทุกๆ 30 วินาที



  Scheduler.start(&Task_1_task);
  Scheduler.start(&Task_2_task);
  Scheduler.start(&Task_3_task);
  Scheduler.begin();




}


//------------------------------------------------------------------------------------------------------------------------//
//*********************************************   จบ  void setup        **************************************************//
//------------------------------------------------------------------------------------------------------------------------//









//------------------------------------------------------------------------------------------------------------------------//
//*********************************************       void Loop        ***************************************************//
//------------------------------------------------------------------------------------------------------------------------//


void loop() {





}

//------------------------------------------------------------------------------------------------------------------------//
//*********************************************      จบ void Loop       **************************************************//
//------------------------------------------------------------------------------------------------------------------------//









//------------------------------------------------------------------------------------------------------------------------//
void reconnecting()
{
  if (!Blynk.connected())
  {
    blynkIsDownCount++;
    BLYNK_LOG("blynk server is down! %d  times", blynkIsDownCount);
    Blynk.connect(5000);
  }
}


BLYNK_CONNECTED()
{

  Blynk.syncAll();//ให้ซิงค์ข้อมูลทั้งหมดล่าสุดจาก Blynk Server

}

3 Likes

Update ข้อมูล ครั้งที่ 2 : AP_WiFi_Manager_Blynk_RTC_LCD2004


#include <ESP8266WiFi.h>                  //โหลดไลบารี่ ..มาใช้งาน     //https://github.com/esp8266/Arduino
#include <ESP8266WebServer.h>             //โหลดไลบารี่ ..มาใช้งาน
#include <WiFiManager.h>                  //โหลดไลบารี่ ..มาใช้งาน     //https://github.com/tzapu/WiFiManager
#include <FS.h>                           //โหลดไลบารี่ ..มาใช้งาน     //this needs to be first, or it all crashes and burns...
#include <DNSServer.h>                    //โหลดไลบารี่ ..มาใช้งาน
#include <ArduinoJson.h>                  //โหลดไลบารี่ ..มาใช้งาน     //https://github.com/bblanchon/ArduinoJson

#include <BlynkSimpleEsp8266.h>           //โหลดไลบรารี่ ของ BLYNK      มาใช้งาน
#include <EEPROM.h>


//------------------------------        การกำหนดค่าในส่วนของ Blynk         ------------------------------------------------//
#define BLYNK_DEBUG
#define BLYNK_PRINT Serial        // ใส่ความคิดเห็นในส่วนนี้เพื่อปิดการใช้งานพิมพ์และประหยัดพื้นที่

int blynkIsDownCount = 0;        //Integer Variable ตัวแปรชื่อ blynkIsDownCount ใช้เก็บเลขจำนวนเต็ม โดยเริ่มต้นให้มีค่า = 0

char blynk_token[34] = "8dwBaSyBOE8htLFrK375JPAYB1hm 8TaQkU";//ใส่ Blynk_token ของเราที่ Blynk ส่งมาทาง Email ตอน Create Project ใหม่

BlynkTimer timer;

//----------------------------------  กำหนดหมายเลขของขาของ Node MCU ESP 8266  --------------------------------------------//

#define D0 16 // ใช้ไฟ LED สีฟ้า ของบอร์ด MCU ESP8266 ให้มีสัญญาณไฟกระพริบ ตาม Code ที่เขียน

#define D3 0 // ใช้เป็นปุ่มกด เพื่อเข้า AP Config ได้ตามความต้องการของผู้ใช้
//------------------------------------------------------------------------------------------------------------------------//

#include <Scheduler.h>



// ------------------------โหลดไลบารี่ของจอ LCD มาใช้งาน โดยแบบนี้จะสามารถ Create Characterได้ ---------------------------------//
#include <LCD.h>                          // บรรทัดที่ 1 และ 2 สามารถสลับบรรทัดกันได้
#include <Wire.h>                         // บรรทัดที่ 1 และ 2 สามารถสลับบรรทัดกันได้
#include <LiquidCrystal_I2C.h>            //บรรทัดนี้ต้องอยู่ในบรรทัดที่ 3 เท่านั้น ถ้าไปอยู่บรรทัดที่ 1 และ 2 จะคอมไพล์ไม่ผ่าน
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); //// ตั้งค่า Address ของอุปกรณ์จอ LCD ที่มีการเชื่อมต่อแบบ I2C
//------------------------------------------------------------------------------------------------------------------------//



bool shouldSaveConfig = false;

//callback notifying us of the need to save config
void saveConfigCallback () {
  Serial.println("Should save config");
  shouldSaveConfig = true;
}
//------------------------------------------------------------------------------------------------------------------------//


//-------------สำหรับ Server local ที่แจกให้ เพิ่ม **** แค่ 2 บรรทัดนี้--------------   (ถ้าเป็น Blynk Server ปกติไม่ต้องใส่)  ----------//
char server[] = "oasiskit.com";
int port = 8080;
//------------------------------------------------------------------------------------------------------------------------//



#include <PZEM004Tv30.h>
PZEM004Tv30 pzem(12, 13); // 12(D6)=RX , 13(D7) = TX  :  TX->D6(GPIO012),RX->D7(GPIO013)

float Ft;
String Ft_1;
float Ft_2;

float Rst_D;
String Rst_D1;
float Rst_D2;

float Rst_H;
String Rst_H1;
float Rst_H2;





float Electric_bill;
float kWh_1;
float kWh_2;
float kWh_3;
float kWh_T;
float kWh_T1;

int Blynkreset_kWh;
WidgetLED led_rst_kWh(V12);//จะ ON เมื่อกดปุ่มค้างไว้ 5 วินาที



#include "Arduino.h"
#include "Wire.h"
#include "uRTCLib.h"
// uRTCLib rtc;
uRTCLib rtc(0x68);





BLYNK_WRITE(V10)

{
  Ft_1 = param.asString();//รับค่าข้อความจาก Blynk เข้ามา เก็บในรูปแบบ String

  Ft_2 = Ft_1.toFloat();//แปลงค่าจาก String เป็น Float
  //เราป้อนค่า จาก Blynk เป็น String = -0.116 ให้ Ft_1 รับค่ามา
  //Ft จะรับค่าการแปลง จาก Ft_1 เป็น Float = -0.12
  //จะเห็นว่า ค่า Folat จะปัดค่าเป็นจุดทศนิยม ไปเป็น 2 ตำแหน่งจะไม่มากกว่านี้เนื่องด้วยความสามารถตัวของอุปกรณ์เอง
  //ทำให้ค่าเพี้ยนไป ประมาณ 3.44%  ทำให้ค่าไฟ/เดือน น้อยกว่าค่าจริงไป 3.44% ประมาณ 0.64(150หน่วย) - 2.14(500หน่วย) บาท
  //โปรเจคนี้จะไม่เปลี่ยนค่า ใดๆ เพราะค่าต่างกันเล็กน้อย (1 เดือน ใช้ไฟไม่ถึง 500 หน่วยแน่นอน)

  //ชุดนี้ทำขึ้นในกรณีที่ ไม่ได้เชื่อมต่อกับ App Blynk ก็ยังสามารถดูค่าทั้งหมดได่อย่างถูกต้อง แต่ทุกครั้งที่มีการเปลี่ยนค่า Ft จะต้องถูกกำนดค่าผ่าน App Blynk มาทุกครั้ง
  EEPROM.put(0, Ft_2); //เขียนค่า Ft_2 ที่เป็น Float ลงใน Address 0
  EEPROM.commit(); //จบการเขียน

  Ft = EEPROM.get(0, Ft_2);//อ่าน ค่า Ft_2 ที่เป็น Float จาก Address 0
  Serial.println(Ft);//แสดงค่า Ft_2 ใน Serial Monitor
  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print("...... Sync Ft .....");
  lcd.setCursor(6, 3);
  lcd.print(Ft, 3);
  delay(2000);
  lcd.clear();

}






BLYNK_WRITE(V21)//Reset kWh Automatic  ตามชั่วโมง  ที่ตั้งจาก Blynk เข้าเครื่อง

{
  Rst_H1 = param.asString();//รับค่าข้อความจาก Blynk เข้ามา เก็บในรูปแบบ String

  Rst_H2 = Rst_H1.toFloat();//แปลงค่าจาก String เป็น Float

  //ชุดนี้ทำขึ้นในกรณีที่ ไม่ได้เชื่อมต่อกับ App Blynk ก็ยังสามารถ Rst ค่า kWh ได้ แต่ทุกครั้งที่มีการเปลี่ยนค่า วัน เวลา  จะต้องถูกกำนดค่าผ่าน App Blynk มาทุกครั้ง
  EEPROM.put(30, Rst_H2); //เขียนค่า Rst_H2 ที่เป็น Float ลงใน Address 0
  EEPROM.commit(); //จบการเขียน

  Rst_H = EEPROM.get(30, Rst_H2);//อ่าน ค่า Rst_H2 ที่เป็น Float จาก Address 0
  Serial.println(Rst_H);//แสดงค่า Rst_H2 ใน Serial Monitor
  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print("Sync AutoRst By Hour");
  lcd.setCursor(0, 2);
  lcd.print("    Hour:  ");
  lcd.setCursor(10, 2);
  lcd.print(Rst_H, 0);
  delay(2000);
  lcd.clear();

}




BLYNK_WRITE(V20)//Reset kWh Automatic  ตามวันที่  ที่ตั้งจาก Blynk เข้าเครื่อง

{
  Rst_D1 = param.asString();//รับค่าข้อความจาก Blynk เข้ามา เก็บในรูปแบบ String

  Rst_D2 = Rst_D1.toFloat();//แปลงค่าจาก String เป็น Float

  //ชุดนี้ทำขึ้นในกรณีที่ ไม่ได้เชื่อมต่อกับ App Blynk ก็ยังสามารถ Rst ค่า kWh ได้ แต่ทุกครั้งที่มีการเปลี่ยนค่า วัน เวลา  จะต้องถูกกำนดค่าผ่าน App Blynk มาทุกครั้ง
  EEPROM.put(20, Rst_D2); //เขียนค่า Rst_D2 ที่เป็น Float ลงใน Address 0
  EEPROM.commit(); //จบการเขียน

  Rst_D = EEPROM.get(20, Rst_D2);//อ่าน ค่า Rst_2 ที่เป็น Float จาก Address 0
  Serial.println(Rst_D);//แสดงค่า Rst_2 ใน Serial Monitor
  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print("Sync Auto Rst By Day");
  lcd.setCursor(0, 2);
  lcd.print("     Day:  ");
  lcd.setCursor(10, 2);
  lcd.print(Rst_D, 0);
  delay(2000);
  lcd.clear();
}











BLYNK_WRITE(V11)//Reset kWh ภายใน 5 วินาที
{

  int pinValue = param.asInt();
  if (pinValue == 1) {
    Blynkreset_kWh = 1;
  }
  if (pinValue == 0) {
    Blynkreset_kWh = 0;
  }
}








class Task_1 : public Task {
  protected:
    void setup() { }


    void loop()  {

      //-----------------------------------------------------------------------------------------------------------//
      if (Blynk.connected())
      {
        Blynk.run();
        digitalWrite(D0, LOW);

      } else {

        digitalWrite(D0, LOW);
        delay(200);
        digitalWrite(D0, HIGH);
        delay(100);

      }
      //-----------------------------------------------------------------------------------------------------------//

      timer.run();//ให้เวลาของ Blynk ทำงาน

      delay(1000);

    }
} Task_1_task;




class Task_2 : public Task {
  protected:
    void setup() { }


    void loop()  {

      float voltage = pzem.voltage();
      if (voltage != NAN) {
        //Serial.print("Voltage: "); Serial.print(voltage); Serial.println("V");
        lcd.setCursor(1, 0);
        //        lcd.print("V: ");
        //        lcd.setCursor(3, 0);
        //        lcd.print(voltage, 2);
        lcd.print(voltage, 0);
        lcd.setCursor(4, 0);
        lcd.print("Vac");
        Blynk.virtualWrite(V1, voltage);
      } else {
        //Serial.println("Error reading voltage");
        //        lcd.setCursor(0, 0);
        //        lcd.print((char)0x20);
        //        lcd.print((char)0x20);
      }


      float current = pzem.current();
      if (current != NAN) {
        //Serial.print("Current: "); Serial.print(current); Serial.println("A");
        lcd.setCursor(13, 0);
        //        lcd.print("A: ");
        //        lcd.setCursor(15, 0);
        lcd.print(current, 2);
//        lcd.setCursor(19, 0);
        lcd.print(" A");
        Blynk.virtualWrite(V2, current);
      } else {
        //Serial.println("Error reading current");
        //        lcd.setCursor(15, 0);
        //        lcd.print((char)0x20);
        //        lcd.print((char)0x20);
      }

      float power = pzem.power();
      if (current != NAN) {
        //Serial.print("Power: "); Serial.print(power); Serial.println("W");
        lcd.setCursor(0, 1);
        lcd.print(power, 2);
        lcd.setCursor(8, 1);
        lcd.print("W");
        
        
        Blynk.virtualWrite(V3, power);
      } else {
        //Serial.println("Error reading power");
        //        lcd.setCursor(3, 1);
        //        lcd.print((char)0x20);
        //        lcd.print((char)0x20);
      }

      float energy = pzem.energy();
      if (current != NAN) {
        //Serial.print("Energy: "); Serial.print(energy, 3); Serial.println("kWh");
        lcd.setCursor(10, 1);
        lcd.print(energy, 2);
//        lcd.setCursor(17, 1);
        lcd.print(" kWh");
        Blynk.virtualWrite(V4, energy);
      } else {
        //Serial.println("Error reading energy");
        //        lcd.setCursor(15, 1);
        //        lcd.print((char)0x20);
        //        lcd.print((char)0x20);
      }

      float frequency = pzem.frequency();
      if (current != NAN) {
        //Serial.print("Frequency: "); Serial.print(frequency, 1); Serial.println("Hz");
        //        lcd.setCursor(0, 2);
        //        lcd.print("Hz: ");
        lcd.setCursor(8, 0);
        lcd.print(frequency, 0);
        lcd.setCursor(10, 0);
        lcd.print("Hz");
        Blynk.virtualWrite(V5, frequency);
      } else {
        //Serial.println("Error reading frequency");
        //        lcd.setCursor(3, 2);
        //        lcd.print((char)0x20);
        //        lcd.print((char)0x20);
      }

      float pf = pzem.pf();
      if (current != NAN) {
        //Serial.print("PF: "); Serial.println(pf);
        //        lcd.setCursor(11, 2);
        //        lcd.print("PF: ");
        //        lcd.setCursor(15, 2);
        //        lcd.print(pf, 2);
        Blynk.virtualWrite(V6, pf);
      } else {
        //Serial.println("Error reading power factor");
        //        lcd.setCursor(15, 2);
        //        lcd.print((char)0x20);
        //        lcd.print((char)0x20);
      }






      //Serial.println();
      delay(2000);

    }//loop



} Task_2_task;





class Task_3 : public Task {
  protected:
    void setup() { }



    void loop()  {




      float energy = pzem.energy();

      // กดปุ่มที่หน้าเครื่อง 5 วินาที เพื่อ Reset kWh
      if (digitalRead(D3) == LOW) {
        delay(3000);
        pzem.resetEnergy();

      }

      // กดปุ่มจาก Blynk 5 วินาที เพื่อ Reset kWh กดค้างจนกว่า LED จะดับ
      if (Blynkreset_kWh == 1) {
        led_rst_kWh.on();
        delay(3000);
        pzem.resetEnergy();
        led_rst_kWh.off();
        pzem.resetEnergy();

      }
      if (Blynkreset_kWh == 0) {

        led_rst_kWh.off();

      }


      //  Reset kWh  แบบ Auto โดยการตั้งค่ามาจาก Blynk โดยเก็บไว้ใน EEPROM ของเครื่อง
      if (Rst_D == rtc.day() && Rst_H == rtc.minute()) {
        pzem.resetEnergy();
      }



      //1.2 อัตราปกติปริมาณการใช้พลังงานไฟฟ้าเกินกว่า 150 หน่วยต่อเดือน
      //ที่บ้านเราใช้ค่านี้ ประเภท 1125 คือ ใช้ไฟฟ้าเกิน 150 หน่วย/เดือน อัตราค่าบริการ 38.22 บาท/เดือน

      //1     -  150 หน่วยละ 3.2484  บาท

      if (energy >= 1 && energy <= 150) {
        kWh_1 = energy * 3.2484;
      }
      if (energy > 150) {
        kWh_1 = 150 * 3.2484;
      }



      //หน่วยที่  151    -  400 หน่วยละ 4.2218  บาท

      if (energy >= 151 && energy <= 400) {
        kWh_2 = energy * 4.2218;
      }
      if (energy > 400) {
        kWh_2 = 250 * 4.2218;
        //401        -- >   หน่วยละ 4.4217  บาท
        kWh_3 = (energy - 400) * 4.4217;
      }




      //รวมค่าใช้จ่ายทั้งหมด
      kWh_T = kWh_1 + kWh_2 + kWh_3;
      kWh_T1 = kWh_T + 38.22 + (energy * Ft);

      Electric_bill = kWh_T1 + (kWh_T1 * 0.07);

      lcd.setCursor(0, 3);
      lcd.print("B/M: ");
      lcd.setCursor(5, 3);
      lcd.print(Electric_bill , 2);
      lcd.setCursor(12, 3);
      //lcd.print("Bath");
      Blynk.virtualWrite(V7, Electric_bill);



      lcd.setCursor(13, 3);
      lcd.print(Rst_D, 0);
      lcd.print("D");
      lcd.setCursor(17, 3);
      lcd.print(Rst_H, 0);
      lcd.print("H");


      delay(2000);

    }//loop





} Task_3_task;




class Task_4 : public Task {
  protected:
    void setup() { }


    void loop()  {
      rtc.refresh();
      int year_ = rtc.year() + 43;

      lcd.setCursor(0, 2);

      if (rtc.day() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.day());
      }
      else lcd.print(rtc.day());
      lcd.print('-');


      if (rtc.month() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.month());
      }
      else lcd.print(rtc.month());
      lcd.print('-');

      lcd.print(year_);



      lcd.setCursor(12, 2);


      if (rtc.hour() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.hour());
      }
      else lcd.print(rtc.hour());


      lcd.print(':');



      if (rtc.minute() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.minute());
      }
      else lcd.print(rtc.minute());


      lcd.print(':');



      if (rtc.second() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.second());
      }
      else lcd.print(rtc.second());


      delay(1000);



    }
} Task_4_task;












//------------------------------------------------------------------------------------------------------------------------//
//*********************************************       void setup        **************************************************//
//------------------------------------------------------------------------------------------------------------------------//

void setup() {

  //-------IO NODE MCU Esp8266-------//
  pinMode(D0, OUTPUT);      //กำหนดโหมดใช้งานให้กับขา D0 เป็นขา สัญญาณไฟ ในสภาวะต่างๆ
  pinMode(D3, INPUT_PULLUP);//กำหนดโหมดใช้งานให้กับขา D3 เป็นขา กดปุ่ม ค้าง เพื่อตั้งค่า AP config


  // ให้ LED ทั้งหมดดับก่อน
  digitalWrite(D0, LOW);//ให้หลอด LED สีฟ้าดับก่อน

  //-------------------------------//


  Serial.begin(115200);
  //-------------------------------//

  EEPROM.begin(512);
  //ต้องการให้รู้ว่า ค่า Ft ที่เป็น Float ที่เก็บใน EEPROM มีค่า = ค่าที่ถูกป้อนมาจาก Bylnk จริงๆ
  Ft = EEPROM.get(0, Ft_2);
  Serial.print("Ft in Void setup : ");
  Serial.println( Ft);

  Rst_D = EEPROM.get(20, Rst_D2);
  Serial.print("Day in Void setup : ");
  Serial.println( Rst_D);

  Rst_H = EEPROM.get(30, Rst_H2);
  Serial.print("Hour in Void setup : ");
  Serial.println( Rst_H);

  lcd.begin(20, 4);
  lcd.clear();

#ifdef ARDUINO_ARCH_ESP8266
  Wire.begin(4, 5); // GPIO4 = D2 and GPIO5 = D1 on ESP8266
#else
  Wire.begin();
#endif

  //rtc.set(0, 19, 15, 2, 25, 2, 20);//ใช้เฉพาะตั้งเวลา
  //  RTCLib::set(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year)







  //read configuration from FS json
  Serial.println("mounting FS...");//แสดงข้อความใน Serial Monitor

  if (SPIFFS.begin()) {
    Serial.println("mounted file system");//แสดงข้อความใน Serial Monitor

    if (SPIFFS.exists("/config.json")) {
      //file exists, reading and loading
      Serial.println("reading config file");//แสดงข้อความใน Serial Monitor
      File configFile = SPIFFS.open("/config.json", "r");

      if (configFile) {
        Serial.println("opened config file");
        size_t size = configFile.size();
        // Allocate a buffer to store contents of the file.
        std::unique_ptr<char[]> buf(new char[size]);

        configFile.readBytes(buf.get(), size);
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject(buf.get());
        json.printTo(Serial);
        if (json.success()) {
          Serial.println("\nparsed json");//แสดงข้อความใน Serial Monitor
          strcpy(blynk_token, json["blynk_token"]);

        } else {
          Serial.println("failed to load json config");//แสดงข้อความใน Serial Monitor
        }
      }
    }
  } else {
    Serial.println("failed to mount FS");//แสดงข้อความใน Serial Monitor
  }
  //end read



  //**************************        AP AUTO CONNECT   ********************************************//

  WiFiManagerParameter custom_blynk_token("blynk", "blynk token", blynk_token, 34);

  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;

  //set config save notify callback
  wifiManager.setSaveConfigCallback(saveConfigCallback);
  wifiManager.addParameter(&custom_blynk_token);


  for (int i = 5; i > -1; i--) {  // นับเวลาถอยหลัง 5 วินาทีก่อนกดปุ่ม AP Config
    digitalWrite(D0, HIGH);
    delay(500);
    digitalWrite(D0, LOW);
    delay(500);
    Serial.print (String(i) + " ");//แสดงข้อความใน Serial Monitor
  }


  if (digitalRead(D3) == LOW) {
    digitalWrite(D0, LOW);
    Serial.println("Button Pressed");//แสดงข้อความใน Serial Monitor

    wifiManager.resetSettings();//ให้ล้างค่า SSID และ Password ที่เคยบันทึกไว้


    // wifiManager.autoConnect(); ใช้สร้างชื่อแอคเซสพอยต์อันโนมัติจาก ESP + ChipID

    if (!wifiManager.autoConnect("PUYIOT AP CONFIG")) {
      Serial.println("failed to connect and hit timeout");//แสดงข้อความใน Serial Monitor
      delay(3000);
      //reset and try again, or maybe put it to deep sleep
      ESP.reset();
      delay(5000);

    }
  }



  Serial.println("Connected.......OK!)");//แสดงข้อความใน Serial Monitor
  strcpy(blynk_token, custom_blynk_token.getValue());


  //save the custom parameters to FS
  if (shouldSaveConfig) {
    Serial.println("saving config");//แสดงข้อความใน Serial Monitor
    DynamicJsonBuffer jsonBuffer;
    JsonObject& json = jsonBuffer.createObject();
    json["blynk_token"] = blynk_token;
    File configFile = SPIFFS.open("/config.json", "w");

    if (!configFile) {
      Serial.println("failed to open config file for writing");//แสดงข้อความใน Serial Monitor

    }
    json.printTo(Serial);
    json.printTo(configFile);
    configFile.close();
    //end save
  }

  //**************************    จบ    AP AUTO CONNECT   *****************************************//




  Serial.println("local ip"); //แสดงข้อความใน Serial Monitor
  delay(100);
  Serial.println(WiFi.localIP());//แสดงข้อความใน Serial Monitor
  Serial.println("gateway");
  delay(100);
  Serial.println(WiFi.gatewayIP());
  Serial.println("subnetMask");
  delay(100);
  Serial.println(WiFi.subnetMask());
  Serial.println("SSID");
  delay(100);
  Serial.println(WiFi.SSID());
  Serial.println("Password");
  delay(100);
  Serial.println(WiFi.psk());


  // Blynk.config(blynk_token);////เริ่มการเชื่อมต่อ Blynk Server แบบปกติ
  Blynk.config(blynk_token, server, port);
  ////เริ่มการเชื่อมต่อ Blynk Server*********สำหรับ Server local ที่แจกให้ เพิ่ม, server, port  แค่นี้จบ



  timer.setInterval(30000L, reconnecting);  //Function reconnect




  Scheduler.start(&Task_1_task);
  Scheduler.start(&Task_2_task);
  Scheduler.start(&Task_3_task);
  Scheduler.start(&Task_4_task);
  Scheduler.begin();




}


//------------------------------------------------------------------------------------------------------------------------//
//*********************************************   จบ  void setup        **************************************************//
//------------------------------------------------------------------------------------------------------------------------//



//------------------------------------------------------------------------------------------------------------------------//
//*********************************************       void Loop        ***************************************************//
//------------------------------------------------------------------------------------------------------------------------//


void loop() {



}

//------------------------------------------------------------------------------------------------------------------------//
//*********************************************      จบ void Loop       **************************************************//
//------------------------------------------------------------------------------------------------------------------------//





//------------------------------------------------------------------------------------------------------------------------//
void reconnecting()
{
  if (!Blynk.connected())
  {
    blynkIsDownCount++;
    BLYNK_LOG("blynk server is down! %d  times", blynkIsDownCount);
    Blynk.connect(5000);
  }
}


BLYNK_CONNECTED()
{

  Blynk.syncAll();//ให้ซิงค์ข้อมูลทั้งหมดล่าสุดจาก Blynk Server

}

1 Likes

Code Update (คิดว่าสมบูรณ์เกือบ 100% แล้วล่ะนะ) :sweat_smile:


#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include <FS.h>
#include <DNSServer.h>
#include <ArduinoJson.h>
#define BLYNK_MAX_SENDBYTES 1200
#include <BlynkSimpleEsp8266.h>
#include <EEPROM.h>


//------------------------------        การกำหนดค่าในส่วนของ Blynk         ------------------------------------------------//
#define BLYNK_DEBUG
#define BLYNK_PRINT Serial

int blynkIsDownCount = 0;

char blynk_token[34] = "8dwBaSBOE8htLFrK3217JPA1YB1hm8TaQkU";//ใส่ Blynk_token ของเราที่ Blynk ส่งมาทาง Email ตอน Create Project ใหม่

BlynkTimer timer;

//----------------------------------  กำหนดหมายเลขของขาของ Node MCU ESP 8266  --------------------------------------------//

#define D0 16 // ใช้ไฟ LED สีฟ้า ของบอร์ด MCU ESP8266 ให้มีสัญญาณไฟกระพริบ ตาม Code ที่เขียน

#define D3 0 // ใช้เป็นปุ่มกด เพื่อเข้า AP Config ได้ตามความต้องการของผู้ใช้
//------------------------------------------------------------------------------------------------------------------------//

#include <Scheduler.h>



// ------------------------โหลดไลบารี่ของจอ LCD มาใช้งาน โดยแบบนี้จะสามารถ Create Characterได้ ---------------------------------//
#include <LCD.h>                          // บรรทัดที่ 1 และ 2 สามารถสลับบรรทัดกันได้
#include <Wire.h>                         // บรรทัดที่ 1 และ 2 สามารถสลับบรรทัดกันได้
#include <LiquidCrystal_I2C.h>            //บรรทัดนี้ต้องอยู่ในบรรทัดที่ 3 เท่านั้น ถ้าไปอยู่บรรทัดที่ 1 และ 2 จะคอมไพล์ไม่ผ่าน
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); //// ตั้งค่า Address ของอุปกรณ์จอ LCD ที่มีการเชื่อมต่อแบบ I2C
//------------------------------------------------------------------------------------------------------------------------//



bool shouldSaveConfig = false;

//callback notifying us of the need to save config
void saveConfigCallback () {
  Serial.println("Should save config");
  shouldSaveConfig = true;
}
//------------------------------------------------------------------------------------------------------------------------//


//-------------สำหรับ Server local ที่แจกให้ เพิ่ม **** แค่ 2 บรรทัดนี้--------------   (ถ้าเป็น Blynk Server ปกติไม่ต้องใส่)  ----------//
char server[] = "oasiskit.com";
int port = 8080;
//------------------------------------------------------------------------------------------------------------------------//


#include <PZEM004Tv30.h>
PZEM004Tv30 pzem(12, 13); // 12(D6)=RX , 13(D7) = TX  :  TX->D6(GPIO012),RX->D7(GPIO013)
//ตัวแปรรับค่า Ft จาก Blynk มาเก็บใน EEPROM
float Ft;
String Ft_1;
float Ft_2;

//ตัวแปรรับค่า วันที่ จาก Blynk มาเก็บใน EEPROM
float Rst_D;
String Rst_D1;
float Rst_D2;

//ตัวแปรรับค่า ชั่วโมง จาก Blynk มาเก็บใน EEPROM
float Rst_H;
String Rst_H1;
float Rst_H2;

//ตัวแปรรับค่า นาที จาก Blynk มาเก็บใน EEPROM
float Rst_M;
String Rst_M1;
float Rst_M2;

//ตัวแปรรับค่า ค่าไฟประเดือน 
float Electric_bill;
float kWh_1;
float kWh_2;
float kWh_3;
float kWh_T;
float kWh_T1;

//กดปุ่ม Reset kWh จาก Blynk
int Blynkreset_kWh;
WidgetLED led_rst_kWh(V12);//จะ ON เมื่อกดปุ่มค้างไว้ 5 วินาที


//RTC Library
#include "Arduino.h"
#include "Wire.h"
#include "uRTCLib.h"
// uRTCLib rtc;
uRTCLib rtc(0x68);

//ชุดกำหนดการส่ง Email แค่ครั้งเดียว
int Email_status = 0;
int Email_status1 = 0;


BLYNK_WRITE(V10)

{
  Ft_1 = param.asString();//รับค่าข้อความจาก Blynk เข้ามา เก็บในรูปแบบ String

  Ft_2 = Ft_1.toFloat();//แปลงค่าจาก String เป็น Float
  //เราป้อนค่า จาก Blynk เป็น String = -0.116 ให้ Ft_1 รับค่ามา
  //Ft จะรับค่าการแปลง จาก Ft_1 เป็น Float = -0.12
  //จะเห็นว่า ค่า Folat จะปัดค่าเป็นจุดทศนิยม ไปเป็น 2 ตำแหน่งจะไม่มากกว่านี้เนื่องด้วยความสามารถตัวของอุปกรณ์เอง
  //ทำให้ค่าเพี้ยนไป ประมาณ 3.44%  ทำให้ค่าไฟ/เดือน น้อยกว่าค่าจริงไป 3.44% ประมาณ 0.64(150หน่วย) - 2.14(500หน่วย) บาท
  //โปรเจคนี้จะไม่เปลี่ยนค่า ใดๆ เพราะค่าต่างกันเล็กน้อย (1 เดือน ใช้ไฟไม่ถึง 500 หน่วยแน่นอน)

  //ชุดนี้ทำขึ้นในกรณีที่ ไม่ได้เชื่อมต่อกับ App Blynk ก็ยังสามารถดูค่าทั้งหมดได่อย่างถูกต้อง แต่ทุกครั้งที่มีการเปลี่ยนค่า Ft วันที่ ชั่วโมง นาที  จะต้องถูกกำนดค่าผ่าน App Blynk มาทุกครั้ง
  
  EEPROM.put(0, Ft_2); //เขียนค่า Ft_2 ที่เป็น Float ลงใน Address 0
  EEPROM.commit(); //จบการเขียน

  Ft = EEPROM.get(0, Ft_2);//อ่าน ค่า Ft_2 ที่เป็น Float จาก Address 0
  Serial.println(Ft);//แสดงค่า Ft ใน Serial Monitor
  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print("...... Sync Ft .....");
  lcd.setCursor(6, 3);
  lcd.print(Ft, 3);
  delay(2000);
  lcd.clear();

}


BLYNK_WRITE(V20)//Reset kWh Automatic  ตามวันที่  ที่ตั้งจาก Blynk เข้าเครื่อง

{
  Rst_D1 = param.asString();//รับค่าข้อความจาก Blynk เข้ามา เก็บในรูปแบบ String

  Rst_D2 = Rst_D1.toFloat();//แปลงค่าจาก String เป็น Float

  //ชุดนี้ทำขึ้นในกรณีที่ ไม่ได้เชื่อมต่อกับ App Blynk ก็ยังสามารถ Rst ค่า kWh ได้ แต่ทุกครั้งที่มีการเปลี่ยนค่า วัน เวลา  จะต้องถูกกำนดค่าผ่าน App Blynk มาทุกครั้ง
  EEPROM.put(20, Rst_D2); //เขียนค่า Rst_D2 ที่เป็น Float ลงใน Address 20
  EEPROM.commit(); //จบการเขียน

  Rst_D = EEPROM.get(20, Rst_D2);//อ่าน ค่า Rst_2 ที่เป็น Float จาก Address 20
  Serial.println(Rst_D);//แสดงค่า Rst ใน Serial Monitor
  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print("Sync Auto Rst By Day");
  lcd.setCursor(0, 2);
  lcd.print("     Day:  ");
  lcd.setCursor(10, 2);
  lcd.print(Rst_D, 0);
  delay(2000);
  lcd.clear();
}





BLYNK_WRITE(V21)//Reset kWh Automatic  ตามชั่วโมง  ที่ตั้งจาก Blynk เข้าเครื่อง

{
  Rst_H1 = param.asString();//รับค่าข้อความจาก Blynk เข้ามา เก็บในรูปแบบ String

  Rst_H2 = Rst_H1.toFloat();//แปลงค่าจาก String เป็น Float

  //ชุดนี้ทำขึ้นในกรณีที่ ไม่ได้เชื่อมต่อกับ App Blynk ก็ยังสามารถ Rst ค่า kWh ได้ แต่ทุกครั้งที่มีการเปลี่ยนค่า วัน เวลา  จะต้องถูกกำนดค่าผ่าน App Blynk มาทุกครั้ง
  EEPROM.put(30, Rst_H2); //เขียนค่า Rst_H2 ที่เป็น Float ลงใน Address 30
  EEPROM.commit(); //จบการเขียน

  Rst_H = EEPROM.get(30, Rst_H2);//อ่าน ค่า Rst_H2 ที่เป็น Float จาก Address 30
  Serial.println(Rst_H);//แสดงค่า Rst ใน Serial Monitor
  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print("Sync AutoRst By Hour");
  lcd.setCursor(0, 2);
  lcd.print("    Hour:  ");
  lcd.setCursor(10, 2);
  lcd.print(Rst_H, 0);
  delay(2000);
  lcd.clear();

}



BLYNK_WRITE(V22)//Reset kWh Automatic  ตามนาที  ที่ตั้งจาก Blynk เข้าเครื่อง

{
  Rst_M1 = param.asString();//รับค่าข้อความจาก Blynk เข้ามา เก็บในรูปแบบ String

  Rst_M2 = Rst_M1.toFloat();//แปลงค่าจาก String เป็น Float

  //ชุดนี้ทำขึ้นในกรณีที่ ไม่ได้เชื่อมต่อกับ App Blynk ก็ยังสามารถ Rst ค่า kWh ได้ แต่ทุกครั้งที่มีการเปลี่ยนค่า วัน เวลา  จะต้องถูกกำนดค่าผ่าน App Blynk มาทุกครั้ง
  EEPROM.put(40, Rst_M2); //เขียนค่า Rst_H2 ที่เป็น Float ลงใน Address 0
  EEPROM.commit(); //จบการเขียน

  Rst_M = EEPROM.get(40, Rst_M2);//อ่าน ค่า Rst_M2 ที่เป็น Float จาก Address 40
  Serial.println(Rst_M);//แสดงค่า Rst ใน Serial Monitor
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("   Sync Auto Rst    ");
  lcd.setCursor(0, 1);
  lcd.print("     By Minute      ");
  lcd.setCursor(0, 3);
  lcd.print("  Minute:  ");
  lcd.setCursor(10, 3);
  lcd.print(Rst_M, 0);
  delay(2000);
  lcd.clear();

}











BLYNK_WRITE(V11)//Reset kWh ภายใน 5 วินาที จาก Blynk
{

  int pinValue = param.asInt();
  if (pinValue == 1) {
    Blynkreset_kWh = 1;
  }
  if (pinValue == 0) {
    Blynkreset_kWh = 0;
  }
}







//การเชื่อมต่อ Blynk Server
class Task_1 : public Task {
  protected:
    void setup() { }


    void loop()  {

      //-----------------------------------------------------------------------------------------------------------//
      if (Blynk.connected())
      {
        Blynk.run();
        digitalWrite(D0, LOW);

      } else {

        digitalWrite(D0, LOW);
        delay(200);
        digitalWrite(D0, HIGH);
        delay(100);

      }
      //-----------------------------------------------------------------------------------------------------------//

      timer.run();//ให้เวลาของ Blynk ทำงาน

      delay(500);

    }
} Task_1_task;



//แสดงค่า ต่างๆ ของ PZEM ออกมา
class Task_2 : public Task {
  protected:
    void setup() { }


    void loop()  {

      float voltage = pzem.voltage();
      if (voltage != NAN) {
        //Serial.print("Voltage: "); Serial.print(voltage); Serial.println("V");
        lcd.setCursor(1, 0);
        lcd.print(voltage, 0);
        lcd.setCursor(4, 0);
        lcd.print("Vac");
        Blynk.virtualWrite(V1, voltage);
      } else {

      }


      float current = pzem.current();
      if (current != NAN) {
        //Serial.print("Current: "); Serial.print(current); Serial.println("A");
        lcd.setCursor(13, 0);
        lcd.print(current, 2);
        lcd.print(" A");
        Blynk.virtualWrite(V2, current);
      } else {

      }

      float power = pzem.power();
      if (current != NAN) {
        lcd.setCursor(0, 1);
        lcd.print(power, 2);
        lcd.setCursor(8, 1);
        lcd.print("W");


        Blynk.virtualWrite(V3, power);
      } else {

      }

      float energy = pzem.energy();
      if (current != NAN) {
        lcd.setCursor(10, 1);
        lcd.print(energy, 2);
        lcd.print(" kWh");
        Blynk.virtualWrite(V4, energy);
      } else {

      }

      float frequency = pzem.frequency();
      if (current != NAN) {
        lcd.setCursor(8, 0);
        lcd.print(frequency, 0);
        lcd.setCursor(10, 0);
        lcd.print("Hz");
        Blynk.virtualWrite(V5, frequency);
      } else {

      }

      float pf = pzem.pf();
      if (current != NAN) {

        Blynk.virtualWrite(V6, pf);
      } else {

      }
      delay(4000);

    }//loop



} Task_2_task;



//คิดค่าไฟฟ้า ตามการใช้งานที่บ้าน ของเรามากกว่า 150 หน่วย/เดือน ใครน้อยกว่านี้ก็คิดสูตรเอง

class Task_3 : public Task {
  protected:
    void setup() { }

    void loop()  {


      float energy = pzem.energy();

      // กดปุ่มที่หน้าเครื่อง 5 วินาที เพื่อ Reset kWh
      if (digitalRead(D3) == LOW) {
        delay(3000);
        pzem.resetEnergy();

      }

      // กดปุ่มจาก Blynk 5 วินาที เพื่อ Reset kWh กดค้างจนกว่า LED จะดับ
      if (Blynkreset_kWh == 1) {
        led_rst_kWh.on();
        delay(3000);
        pzem.resetEnergy();
        led_rst_kWh.off();
        pzem.resetEnergy();

      }
      if (Blynkreset_kWh == 0) {

        led_rst_kWh.off();

      }


      //  Reset kWh  แบบ Auto โดยการตั้งค่ามาจาก Blynk โดยเก็บไว้ใน EEPROM ของเครื่อง
      // ส่ง Email 1 ครั้งเท่านั้น
      if (Rst_D == rtc.day() && Rst_H == rtc.hour() &&  Rst_M == rtc.minute() && Email_status == 0) {
        pzem.resetEnergy();
        //Serial.println("reset kWh");มันจะ Reset ซ้ำ กันใน 1 นาทีนี้ และยกเลิกจนกว่าจะขึ้นนาทีใหม่
        String body = String("ค่าไฟฟ้าประจำเดือนที่ ") + (rtc.month() - 1) + String(" เป็นจำนวนเงิน ") + Electric_bill + String(" บาท ระบบทำการ Reset kWh = 0 แล้ว ***ให้ตรวจสอบค่า Ft ประจำเดือนด้วย***  By  PUYIOT");
        Blynk.email("mysmarthomepuy@gmail.com", "แจ้งค่าไฟฟ้าประจำเดือน ", body);
        delay(500);
        Email_status = 1;
        Email_status1 = 0;
      }
      else if (Rst_D == rtc.day() && Rst_H == rtc.hour() &&  Rst_M == rtc.minute() && Email_status == 1) {
        delay(10);
        Email_status1 = 1;
      }

      if (Rst_M != rtc.minute() && Email_status1 == 1 ) {
        delay(10);
        Email_status = 0;
      }



      //1.2 อัตราปกติปริมาณการใช้พลังงานไฟฟ้าเกินกว่า 150 หน่วยต่อเดือน
      //ที่บ้านเราใช้ค่านี้ ประเภท 1125 คือ ใช้ไฟฟ้าเกิน 150 หน่วย/เดือน อัตราค่าบริการ 38.22 บาท/เดือน

      //1     -  150 หน่วยละ 3.2484  บาท

      if (energy >= 1 && energy <= 150) {
        kWh_1 = energy * 3.2484;
      }
      if (energy > 150) {
        kWh_1 = 150 * 3.2484;
      }


      //หน่วยที่  151    -  400 หน่วยละ 4.2218  บาท

      if (energy >= 151 && energy <= 400) {
        kWh_2 = energy * 4.2218;
      }
      if (energy > 400) {
        kWh_2 = 250 * 4.2218;
        //401        -- >   หน่วยละ 4.4217  บาท
        kWh_3 = (energy - 400) * 4.4217;
      }


      //รวมค่าใช้จ่ายทั้งหมด
      kWh_T = kWh_1 + kWh_2 + kWh_3;
      kWh_T1 = kWh_T + 38.22 + (energy * Ft);

      Electric_bill = kWh_T1 + (kWh_T1 * 0.07);

      lcd.setCursor(0, 3);
      lcd.print("B/M: ");
      lcd.setCursor(5, 3);
      lcd.print(Electric_bill , 2);
      lcd.setCursor(12, 3);
      //lcd.print("Bath");
      Blynk.virtualWrite(V7, Electric_bill);


      lcd.setCursor(13, 3);
      lcd.print(Rst_D, 0);
      //lcd.print("D");
      lcd.setCursor(15, 3);
      lcd.print(Rst_H, 0);
      lcd.setCursor(17, 3);
      lcd.print(":");
      //lcd.print("H");
      lcd.setCursor(18, 3);
      lcd.print(Rst_M, 0);

      delay(2000);

    }//loop






} Task_3_task;



//แสดง dd:mm:yy   hh:mm
class Task_4 : public Task {
  protected:
    void setup() { }

    void loop()  {
      rtc.refresh();
      int year_ = rtc.year() + 43; //+43 เพื่อให้ได้ปี พ.ศ. 63

      lcd.setCursor(0, 2);
      
//เติม 0 กรณีเป็นเลข วัน เดือน ชม. นาที วินาที ตัวเดียว
      if (rtc.day() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.day());
      }
      else lcd.print(rtc.day());
      lcd.print('-');


      if (rtc.month() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.month());
      }
      else lcd.print(rtc.month());
      lcd.print('-');

      lcd.print(year_);

      lcd.setCursor(12, 2);

      if (rtc.hour() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.hour());
      }
      else lcd.print(rtc.hour());

      lcd.print(':');



      if (rtc.minute() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.minute());
      }
      else lcd.print(rtc.minute());

      lcd.print(':');



      if (rtc.second() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.second());
      }
      else lcd.print(rtc.second());


      delay(1000);



    }
} Task_4_task;








//------------------------------------------------------------------------------------------------------------------------//
//*********************************************       void setup        **************************************************//
//------------------------------------------------------------------------------------------------------------------------//

void setup() {

  //-------IO NODE MCU Esp8266-------//
  pinMode(D0, OUTPUT);      //กำหนดโหมดใช้งานให้กับขา D0 เป็นขา สัญญาณไฟ ในสภาวะต่างๆ
  pinMode(D3, INPUT_PULLUP);//กำหนดโหมดใช้งานให้กับขา D3 เป็นขา กดปุ่ม ค้าง เพื่อตั้งค่า AP config


  // ให้ LED ทั้งหมดดับก่อน
  digitalWrite(D0, LOW);//ให้หลอด LED สีฟ้าดับก่อน

  //-------------------------------//


  Serial.begin(115200);
  //-------------------------------//

  EEPROM.begin(512);
  
  //ต้องการให้รู้ว่า ค่า Ft ที่เป็น Float ที่เก็บใน EEPROM มีค่า = ค่าที่ถูกป้อนมาจาก Bylnk จริงๆหรือปล่าว
  Ft = EEPROM.get(0, Ft_2);
  Serial.print("Ft in Void setup : ");
  Serial.println(Ft);
  
//ต้องการให้รู้ว่า ค่า วันที่ ที่เป็น Float ที่เก็บใน EEPROM มีค่า = ค่าที่ถูกป้อนมาจาก Bylnk จริงๆหรือปล่าว
  Rst_D = EEPROM.get(20, Rst_D2);
  Serial.print("Day in Void setup : ");
  Serial.println(Rst_D);
  
//ต้องการให้รู้ว่า ค่า ชั่วโมง ที่เป็น Float ที่เก็บใน EEPROM มีค่า = ค่าที่ถูกป้อนมาจาก Bylnk จริงๆหรือปล่าว
  Rst_H = EEPROM.get(30, Rst_H2);
  Serial.print("Hour in Void setup : ");
  Serial.println(Rst_H);
  
//ต้องการให้รู้ว่า ค่า นาที ที่เป็น Float ที่เก็บใน EEPROM มีค่า = ค่าที่ถูกป้อนมาจาก Bylnk จริงๆหรือปล่าว
  Rst_M = EEPROM.get(40, Rst_M2);
  Serial.print("Minute in Void setup : ");
  Serial.println(Rst_M);
  

  lcd.begin(20, 4);
  lcd.clear();




#ifdef ARDUINO_ARCH_ESP8266
  Wire.begin(4, 5); // GPIO4 = D2 and GPIO5 = D1 on ESP8266
#else
  Wire.begin();
#endif

  //rtc.set(0, 46, 11, 3, 3, 3, 20);//ใช้เฉพาะตั้งเวลา ถ้าจะตั้งก็ เอา // ออก เมื่อตั้งได้ตรงแล้วก็เอา // ใส่กลับไปเหมือนเดิม
  //  RTCLib::set(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year)







  //read configuration from FS json
  Serial.println("mounting FS...");//แสดงข้อความใน Serial Monitor

  if (SPIFFS.begin()) {
    Serial.println("mounted file system");//แสดงข้อความใน Serial Monitor

    if (SPIFFS.exists("/config.json")) {
      //file exists, reading and loading
      Serial.println("reading config file");//แสดงข้อความใน Serial Monitor
      File configFile = SPIFFS.open("/config.json", "r");

      if (configFile) {
        Serial.println("opened config file");
        size_t size = configFile.size();
        // Allocate a buffer to store contents of the file.
        std::unique_ptr<char[]> buf(new char[size]);

        configFile.readBytes(buf.get(), size);
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject(buf.get());
        json.printTo(Serial);
        if (json.success()) {
          Serial.println("\nparsed json");//แสดงข้อความใน Serial Monitor
          strcpy(blynk_token, json["blynk_token"]);

        } else {
          Serial.println("failed to load json config");//แสดงข้อความใน Serial Monitor
        }
      }
    }
  } else {
    Serial.println("failed to mount FS");//แสดงข้อความใน Serial Monitor
  }
  //end read



  //**************************        AP AUTO CONNECT   ********************************************//

  WiFiManagerParameter custom_blynk_token("blynk", "blynk token", blynk_token, 34);

  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;

  //set config save notify callback
  wifiManager.setSaveConfigCallback(saveConfigCallback);
  wifiManager.addParameter(&custom_blynk_token);


  for (int i = 5; i > -1; i--) {  // นับเวลาถอยหลัง 5 วินาทีก่อนกดปุ่ม AP Config
    digitalWrite(D0, HIGH);
    delay(500);
    digitalWrite(D0, LOW);
    delay(500);
    Serial.print (String(i) + " ");//แสดงข้อความใน Serial Monitor
  }


  if (digitalRead(D3) == LOW) {
    digitalWrite(D0, LOW);
    Serial.println("Button Pressed");//แสดงข้อความใน Serial Monitor

    wifiManager.resetSettings();//ให้ล้างค่า SSID และ Password ที่เคยบันทึกไว้


    // wifiManager.autoConnect(); ใช้สร้างชื่อแอคเซสพอยต์อันโนมัติจาก ESP + ChipID

    if (!wifiManager.autoConnect("PUYIOT AP CONFIG")) {
      Serial.println("failed to connect and hit timeout");//แสดงข้อความใน Serial Monitor
      delay(3000);
      //reset and try again, or maybe put it to deep sleep
      ESP.reset();
      delay(5000);

    }
  }



  Serial.println("Connected.......OK!)");//แสดงข้อความใน Serial Monitor
  strcpy(blynk_token, custom_blynk_token.getValue());


  //save the custom parameters to FS
  if (shouldSaveConfig) {
    Serial.println("saving config");//แสดงข้อความใน Serial Monitor
    DynamicJsonBuffer jsonBuffer;
    JsonObject& json = jsonBuffer.createObject();
    json["blynk_token"] = blynk_token;
    File configFile = SPIFFS.open("/config.json", "w");

    if (!configFile) {
      Serial.println("failed to open config file for writing");//แสดงข้อความใน Serial Monitor

    }
    json.printTo(Serial);
    json.printTo(configFile);
    configFile.close();
    //end save
  }

  //**************************    จบ    AP AUTO CONNECT   *****************************************//




  Serial.println("local ip"); //แสดงข้อความใน Serial Monitor
  delay(100);
  Serial.println(WiFi.localIP());//แสดงข้อความใน Serial Monitor
  Serial.println("gateway");
  delay(100);
  Serial.println(WiFi.gatewayIP());
  Serial.println("subnetMask");
  delay(100);
  Serial.println(WiFi.subnetMask());
  Serial.println("SSID");
  delay(100);
  Serial.println(WiFi.SSID());
  Serial.println("Password");
  delay(100);
  Serial.println(WiFi.psk());


  // Blynk.config(blynk_token);////เริ่มการเชื่อมต่อ Blynk Server แบบปกติ
  Blynk.config(blynk_token, server, port);
  ////เริ่มการเชื่อมต่อ Blynk Server*********สำหรับ Server local ที่แจกให้ เพิ่ม, server, port  แค่นี้จบ



  timer.setInterval(30000L, reconnecting);  //Function reconnect




  Scheduler.start(&Task_1_task);
  Scheduler.start(&Task_2_task);
  Scheduler.start(&Task_3_task);
  Scheduler.start(&Task_4_task);

  Scheduler.begin();




}


//------------------------------------------------------------------------------------------------------------------------//
//*********************************************   จบ  void setup        **************************************************//
//------------------------------------------------------------------------------------------------------------------------//



//------------------------------------------------------------------------------------------------------------------------//
//*********************************************       void Loop        ***************************************************//
//------------------------------------------------------------------------------------------------------------------------//


void loop() {



}

//------------------------------------------------------------------------------------------------------------------------//
//*********************************************      จบ void Loop       **************************************************//
//------------------------------------------------------------------------------------------------------------------------//





//------------------------------------------------------------------------------------------------------------------------//
void reconnecting()
{
  if (!Blynk.connected())
  {
    blynkIsDownCount++;
    BLYNK_LOG("blynk server is down! %d  times", blynkIsDownCount);
    Blynk.connect(5000);
  }
}


BLYNK_CONNECTED()
{

  Blynk.syncAll();//ให้ซิงค์ข้อมูลทั้งหมดล่าสุดจาก Blynk Server

}
1 Likes

สำหรับ Post นี้ เป็นของจริงแล้วครับ​ แต่ต้องผ่านการทดสอบก่อนโดยผมจะนำมาแชร์ Project กัน เริ่มเลยนะครับ
Diagram การต่อสาย

ความต้องการของ Project (เพื่อป้องกันความผิดพลาดเรื่องของ Version และตรวจสอบย้อนหลังได้)

  1. Arduino IDE Version 1.8.7
  2. WiFiManager Version 0.14.0
  3. Blynk Version 0.5.4
  4. ArduinoJson Version 5.13.3
  5. uRTCLib By Naguissa Version 6.2.4
  6. New-LiquidCrystal-master Version 6.2.4 :crazy_face:
  7. PZEM-004T-v30-master
    เอาเป็นว่า ข้อ 2 - 7 มีให้ Download ครับ
  8. ESP8266Scheduler https://drive.google.com/file/d/1Rx-S8AXVycXHkHUely-kQKZ0nvagssS3/view?usp=sharing

ต่อไปมากล่าวถึงคุณลักษณะของ Proj นี้กันบ้าง ว่าทำอะไรได้บ้าง
0. Code ที่เขียนไว้คิดค่าไฟ และค่า Ft เป็นของการไฟฟ้าส่วนภูมิภาค
*** 1.2 อัตราปกติปริมาณการใช้พลังงานไฟฟ้าเกินกว่า 150 หน่วยต่อเดือน*** ส่วนของท่านใดที่ไม่ใช่แบบผมก็ประยุกต์จาก Project ผมได้ครับ

  1. ดูค่า พารามิเตอร์ ของ PZEM-004T-v30 ตามที่มันระบุไว้ได้ เช่น แรงดันไฟฟ้า กระแสไฟฟ้า ความถี่ไฟฟ้า วัตต์ กิโลวัตต์/ชั่วโมง (โดยค่านี้จะเก็บไว้ที่ตัว PZEM-004T-v30 เลย ไม่ต้องกลัวว่าไฟดับแล้ว ค่า กิโลวัตต์/ชั่วโมง จะหาย ถ้าต้องการ ให้ค่ามันเป็น 0 ก็ เขียน Code reset เอา ) ค่า Power Factor
  2. ดูค่าไฟเป็น เงิน บาท/เดือน ได้เลย แบบ Real time โดย + ค่าบริการและภาษี 7% แล้ว
  3. สามารถตั้งค่า ft /เดือน เอง ได้ โดย กดปุ่มที่ Ft/M ที่ Blynk เพื่อ Link เข้า Web ที่แสดงค่า Ft ในช่วงต่างของปี โดยการไฟฟ้า จะออกมาเป็นช่วง ๆ เช่น มกราคม – เมษายน 2563 ** Ft ขายปลีก = -11.60 สตางค์ ก็ = -0.116 บาท

Link : http://www.erc.or.th/ERCWeb2/Front/StaticPage/StaticPage.aspx?p=186&Tag=ค่าไฟฟ้าโดยอัตโนมัติ%2

Link นี้ผมทำไว้เพื่อสะดวกเวลาเปิดหน้า Web ด้วย Blynk ก็จะเห็นค่าตัวเลข ให้เราเอามาป้อนเพื่อกำหนดค่า Ft ได้ เลย เพราะโดยปกติการไฟฟ้า จะออกมาเป็นไฟล์ .PDF ทำให้ไม่สะดวกเป็นอย่างมาก

  1. ตรวจสอบค่าไฟฟ้าย้อนหลังได้ จากการไฟฟ้าส่วนภูมิภาค โดยกดปุ่ม Bill/M เพื่อ Link เข้า Web ที่แสดงเป็นการแสดงค่าใช้จ่าย ย้อนหลัง ประมาณ 6-7 เดือน โดยเราต้องป้อน หมายเลขผู้ใช้ไฟ้า และรหัสเครื่องวัด เข้าไป ระบบจึงจะแสดงกราฟออกมา

Link : https://www.pea.co.th/e-service/สอบถามประวัติการใช้ไฟ
5. ดูอัตราการคิดค่าไฟฟ้าได้ โดยกดปุ่ม Rate/M เพื่อ Link เข้า Web ที่แสดง อัตราค่าไฟฟ้า ประเภทที่ 1 บ้านอยู่อาศัย เพื่อสะดวกในการดูข้อมูล

Link : https://webcache.googleusercontent.com/search?q=cache:hrvodg5Q4EUJ:https://www.pea.co.th/Portals/0/Document/Rate2015Update.pdf+&cd=3&hl=th&ct=clnk&gl=th
*** หมายเหตุ เป็น web cache***

  1. สามารถกำหนดค่า Ft จาก Blynk เข้าไปเก็บใน EEPROM ของ ESP8266 ได้ ดังนั้นจึงไม่ต้องกังวลว่า กรณี ESP8266 ไม่สามารถ เชื่อม WiFi Internet +Blynk Server ได้แล้ว เรายังสามารถดูค่าจริง จาก จอ LCD ได้ โดยมีข้อแม้ว่า ทุกครั้งที่มีการเปลี่ยนค่า Ft จะต้องมีการป้อนค่าจาก Blynk เสมอ

  2. สามารถกำหนดค่า วันที่ ชั่วโมง นาที ในการตัดบิลค่าไฟฟ้า ประจำเดือน จาก Blynk เข้าไปเก็บใน EEPROM ของ ESP8266 ได้ ดังนั้นจึงไม่ต้องกังวลว่า กรณี ESP8266 ไม่สามารถ เชื่อม WiFi Internet +Blynk Server ได้แล้ว เรายังสามารถดูค่าจริง จาก จอ LCD ได้ โดยมีข้อแม้ว่า ทุกครั้งที่มีการเปลี่ยนค่า วันที่ ชั่วโมง นาที จะต้องมีการป้อนค่าจาก Blynk เสมอ และเมื่อถึงวันเวลาดังกล่าว ที่เรากำหนดไว้ ระบบจะทำการส่ง Email แจ้งค่าไฟมาให้เราในแต่ละเดือน พร้อมทั้ง Reset ค่า kWh ให้เราอัตโนมัติ

  3. สามารถ Reset ค่า kWh โดย การกดปุ่มที่เครื่อง ค้างไว้ 5 วินาที หรือ กดปุ่ม Reset ค่า kWh จาก Blynk ก็ได้ หรือ Reset แบบ Auto ตามข้อที่ 7

  4. มีกราฟแสดง ค่าที่เราสนใจ เช่น Watt kWh ค่าไฟฟ้า แรงดัน กระแส อันนี้ท่านที่เคยใช้งาน blynk ทำได้อยู่แล้ว

  5. ดูค่า พารามิเตอร์ แบบ real time ทั้ง Online (ดูจาก Blynk) หรือ Offline (จอ LCD )ก็ได้

PZEM Project Qr CODE clone

พักก่อนครับ…19.02 03-03-2563

ต่อครับ 09.41 03-03-2563

Code Final ครับ (ใน Code ผมอธิบายการทำงานบางส่วนเอาไว้แล้ว)


#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include <FS.h>
#include <DNSServer.h>
#include <ArduinoJson.h>
#define BLYNK_MAX_SENDBYTES 1200
#include <BlynkSimpleEsp8266.h>
#include <EEPROM.h>


//------------------------------        การกำหนดค่าในส่วนของ Blynk         ------------------------------------------------//
#define BLYNK_DEBUG
#define BLYNK_PRINT Serial

int blynkIsDownCount = 0;

char blynk_token[34] = "8dwBaSBOE8htLFrK37JPAYB1hm8TaQkU";//ใส่ Blynk_token ของเราที่ Blynk ส่งมาทาง Email ตอน Create Project ใหม่

BlynkTimer timer;

//----------------------------------  กำหนดหมายเลขของขาของ Node MCU ESP 8266  --------------------------------------------//

#define D0 16 // ใช้ไฟ LED สีฟ้า ของบอร์ด MCU ESP8266 ให้มีสัญญาณไฟกระพริบ ตาม Code ที่เขียน

#define D3 0 // ใช้เป็นปุ่มกด เพื่อเข้า AP Config ได้ตามความต้องการของผู้ใช้
//------------------------------------------------------------------------------------------------------------------------//

#include <Scheduler.h>



// ------------------------โหลดไลบารี่ของจอ LCD มาใช้งาน โดยแบบนี้จะสามารถ Create Characterได้ ---------------------------------//
#include <LCD.h>                          // บรรทัดที่ 1 และ 2 สามารถสลับบรรทัดกันได้
#include <Wire.h>                         // บรรทัดที่ 1 และ 2 สามารถสลับบรรทัดกันได้
#include <LiquidCrystal_I2C.h>            //บรรทัดนี้ต้องอยู่ในบรรทัดที่ 3 เท่านั้น ถ้าไปอยู่บรรทัดที่ 1 และ 2 จะคอมไพล์ไม่ผ่าน
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); //// ตั้งค่า Address ของอุปกรณ์จอ LCD ที่มีการเชื่อมต่อแบบ I2C
//------------------------------------------------------------------------------------------------------------------------//



bool shouldSaveConfig = false;

//callback notifying us of the need to save config
void saveConfigCallback () {
  Serial.println("Should save config");
  shouldSaveConfig = true;
}
//------------------------------------------------------------------------------------------------------------------------//


//-------------สำหรับ Server local ที่แจกให้ เพิ่ม **** แค่ 2 บรรทัดนี้--------------   (ถ้าเป็น Blynk Server ปกติไม่ต้องใส่)  ----------//
char server[] = "oasiskit.com";
int port = 8080;
//------------------------------------------------------------------------------------------------------------------------//


#include <PZEM004Tv30.h>
PZEM004Tv30 pzem(12, 13); // 12(D6)=RX , 13(D7) = TX  :  TX->D6(GPIO012),RX->D7(GPIO013)
//ตัวแปรรับค่า Ft จาก Blynk มาเก็บใน EEPROM
float Ft;
String Ft_1;
float Ft_2;

//ตัวแปรรับค่า วันที่ จาก Blynk มาเก็บใน EEPROM
float Rst_D;
String Rst_D1;
float Rst_D2;

//ตัวแปรรับค่า ชั่วโมง จาก Blynk มาเก็บใน EEPROM
float Rst_H;
String Rst_H1;
float Rst_H2;

//ตัวแปรรับค่า นาที จาก Blynk มาเก็บใน EEPROM
float Rst_M;
String Rst_M1;
float Rst_M2;

//ตัวแปรรับค่า ค่าไฟประเดือน 
float Electric_bill;
float kWh_1;
float kWh_2;
float kWh_3;
float kWh_T;
float kWh_T1;

//กดปุ่ม Reset kWh จาก Blynk
int Blynkreset_kWh;
WidgetLED led_rst_kWh(V12);//จะ ON เมื่อกดปุ่มค้างไว้ 5 วินาที


//RTC Library
#include "Arduino.h"
#include "Wire.h"
#include "uRTCLib.h"
// uRTCLib rtc;
uRTCLib rtc(0x68);

//ชุดกำหนดการส่ง Email แค่ครั้งเดียว
int Email_status = 0;
int Email_status1 = 0;


BLYNK_WRITE(V10)

{
  Ft_1 = param.asString();//รับค่าข้อความจาก Blynk เข้ามา เก็บในรูปแบบ String

  Ft_2 = Ft_1.toFloat();//แปลงค่าจาก String เป็น Float
  //เราป้อนค่า จาก Blynk เป็น String = -0.116 ให้ Ft_1 รับค่ามา
  //Ft จะรับค่าการแปลง จาก Ft_1 เป็น Float = -0.12
  //จะเห็นว่า ค่า Folat จะปัดค่าเป็นจุดทศนิยม ไปเป็น 2 ตำแหน่งจะไม่มากกว่านี้เนื่องด้วยความสามารถตัวของอุปกรณ์เอง
  //ทำให้ค่าเพี้ยนไป ประมาณ 3.44%  ทำให้ค่าไฟ/เดือน น้อยกว่าค่าจริงไป 3.44% ประมาณ 0.64(150หน่วย) - 2.14(500หน่วย) บาท
  //โปรเจคนี้จะไม่เปลี่ยนค่า ใดๆ เพราะค่าต่างกันเล็กน้อย (1 เดือน ใช้ไฟไม่ถึง 500 หน่วยแน่นอน)

  //ชุดนี้ทำขึ้นในกรณีที่ ไม่ได้เชื่อมต่อกับ App Blynk ก็ยังสามารถดูค่าทั้งหมดได่อย่างถูกต้อง แต่ทุกครั้งที่มีการเปลี่ยนค่า Ft วันที่ ชั่วโมง นาที  จะต้องถูกกำนดค่าผ่าน App Blynk มาทุกครั้ง
  
  EEPROM.put(0, Ft_2); //เขียนค่า Ft_2 ที่เป็น Float ลงใน Address 0
  EEPROM.commit(); //จบการเขียน

  Ft = EEPROM.get(0, Ft_2);//อ่าน ค่า Ft_2 ที่เป็น Float จาก Address 0
  Serial.println(Ft);//แสดงค่า Ft ใน Serial Monitor
  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print("...... Sync Ft .....");
  lcd.setCursor(6, 3);
  lcd.print(Ft, 3);
  delay(2000);
  lcd.clear();

}


BLYNK_WRITE(V20)//Reset kWh Automatic  ตามวันที่  ที่ตั้งจาก Blynk เข้าเครื่อง

{
  Rst_D1 = param.asString();//รับค่าข้อความจาก Blynk เข้ามา เก็บในรูปแบบ String

  Rst_D2 = Rst_D1.toFloat();//แปลงค่าจาก String เป็น Float

  //ชุดนี้ทำขึ้นในกรณีที่ ไม่ได้เชื่อมต่อกับ App Blynk ก็ยังสามารถ Rst ค่า kWh ได้ แต่ทุกครั้งที่มีการเปลี่ยนค่า วัน เวลา  จะต้องถูกกำนดค่าผ่าน App Blynk มาทุกครั้ง
  EEPROM.put(20, Rst_D2); //เขียนค่า Rst_D2 ที่เป็น Float ลงใน Address 20
  EEPROM.commit(); //จบการเขียน

  Rst_D = EEPROM.get(20, Rst_D2);//อ่าน ค่า Rst_2 ที่เป็น Float จาก Address 20
  Serial.println(Rst_D);//แสดงค่า Rst ใน Serial Monitor
  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print("Sync Auto Rst By Day");
  lcd.setCursor(0, 2);
  lcd.print("     Day:  ");
  lcd.setCursor(10, 2);
  lcd.print(Rst_D, 0);
  delay(2000);
  lcd.clear();
}





BLYNK_WRITE(V21)//Reset kWh Automatic  ตามชั่วโมง  ที่ตั้งจาก Blynk เข้าเครื่อง

{
  Rst_H1 = param.asString();//รับค่าข้อความจาก Blynk เข้ามา เก็บในรูปแบบ String

  Rst_H2 = Rst_H1.toFloat();//แปลงค่าจาก String เป็น Float

  //ชุดนี้ทำขึ้นในกรณีที่ ไม่ได้เชื่อมต่อกับ App Blynk ก็ยังสามารถ Rst ค่า kWh ได้ แต่ทุกครั้งที่มีการเปลี่ยนค่า วัน เวลา  จะต้องถูกกำนดค่าผ่าน App Blynk มาทุกครั้ง
  EEPROM.put(30, Rst_H2); //เขียนค่า Rst_H2 ที่เป็น Float ลงใน Address 30
  EEPROM.commit(); //จบการเขียน

  Rst_H = EEPROM.get(30, Rst_H2);//อ่าน ค่า Rst_H2 ที่เป็น Float จาก Address 30
  Serial.println(Rst_H);//แสดงค่า Rst ใน Serial Monitor
  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print("Sync AutoRst By Hour");
  lcd.setCursor(0, 2);
  lcd.print("    Hour:  ");
  lcd.setCursor(10, 2);
  lcd.print(Rst_H, 0);
  delay(2000);
  lcd.clear();

}



BLYNK_WRITE(V22)//Reset kWh Automatic  ตามนาที  ที่ตั้งจาก Blynk เข้าเครื่อง

{
  Rst_M1 = param.asString();//รับค่าข้อความจาก Blynk เข้ามา เก็บในรูปแบบ String

  Rst_M2 = Rst_M1.toFloat();//แปลงค่าจาก String เป็น Float

  //ชุดนี้ทำขึ้นในกรณีที่ ไม่ได้เชื่อมต่อกับ App Blynk ก็ยังสามารถ Rst ค่า kWh ได้ แต่ทุกครั้งที่มีการเปลี่ยนค่า วัน เวลา  จะต้องถูกกำนดค่าผ่าน App Blynk มาทุกครั้ง
  EEPROM.put(40, Rst_M2); //เขียนค่า Rst_H2 ที่เป็น Float ลงใน Address 0
  EEPROM.commit(); //จบการเขียน

  Rst_M = EEPROM.get(40, Rst_M2);//อ่าน ค่า Rst_M2 ที่เป็น Float จาก Address 40
  Serial.println(Rst_M);//แสดงค่า Rst ใน Serial Monitor
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("   Sync Auto Rst    ");
  lcd.setCursor(0, 1);
  lcd.print("     By Minute      ");
  lcd.setCursor(0, 3);
  lcd.print("  Minute:  ");
  lcd.setCursor(10, 3);
  lcd.print(Rst_M, 0);
  delay(2000);
  lcd.clear();

}











BLYNK_WRITE(V11)//Reset kWh ภายใน 5 วินาที จาก Blynk
{

  int pinValue = param.asInt();
  if (pinValue == 1) {
    Blynkreset_kWh = 1;
  }
  if (pinValue == 0) {
    Blynkreset_kWh = 0;
  }
}







//การเชื่อมต่อ Blynk Server
class Task_1 : public Task {
  protected:
    void setup() { }


    void loop()  {

      //-----------------------------------------------------------------------------------------------------------//
      if (Blynk.connected())
      {
        Blynk.run();
        digitalWrite(D0, LOW);

      } else {

        digitalWrite(D0, LOW);
        delay(200);
        digitalWrite(D0, HIGH);
        delay(100);

      }
      //-----------------------------------------------------------------------------------------------------------//

      timer.run();//ให้เวลาของ Blynk ทำงาน

      delay(500);

    }
} Task_1_task;



//แสดงค่า ต่างๆ ของ PZEM ออกมา
class Task_2 : public Task {
  protected:
    void setup() { }


    void loop()  {

      float voltage = pzem.voltage();
      if (voltage != NAN) {
        //Serial.print("Voltage: "); Serial.print(voltage); Serial.println("V");
        lcd.setCursor(1, 0);
        lcd.print(voltage, 0);
        lcd.setCursor(4, 0);
        lcd.print("Vac");
        Blynk.virtualWrite(V1, voltage);
      } else {

      }


      float current = pzem.current();
      if (current != NAN) {
        //Serial.print("Current: "); Serial.print(current); Serial.println("A");
        lcd.setCursor(13, 0);
        lcd.print(current, 2);
        lcd.print(" A");
        Blynk.virtualWrite(V2, current);
      } else {

      }

      float power = pzem.power();
      if (current != NAN) {
        lcd.setCursor(0, 1);
        lcd.print(power, 2);
        lcd.setCursor(8, 1);
        lcd.print("W");


        Blynk.virtualWrite(V3, power);
      } else {

      }

      float energy = pzem.energy();
      if (current != NAN) {
        lcd.setCursor(10, 1);
        lcd.print(energy, 2);
        lcd.print(" kWh");
        Blynk.virtualWrite(V4, energy);
      } else {

      }

      float frequency = pzem.frequency();
      if (current != NAN) {
        lcd.setCursor(8, 0);
        lcd.print(frequency, 0);
        lcd.setCursor(10, 0);
        lcd.print("Hz");
        Blynk.virtualWrite(V5, frequency);
      } else {

      }

      float pf = pzem.pf();
      if (current != NAN) {

        Blynk.virtualWrite(V6, pf);
      } else {

      }
      delay(4000);

    }//loop



} Task_2_task;



//คิดค่าไฟฟ้า ตามการใช้งานที่บ้าน ของเรามากกว่า 150 หน่วย/เดือน ใครน้อยกว่านี้ก็คิดสูตรเอง

class Task_3 : public Task {
  protected:
    void setup() { }

    void loop()  {


      float energy = pzem.energy();

      // กดปุ่มที่หน้าเครื่อง 5 วินาที เพื่อ Reset kWh
      if (digitalRead(D3) == LOW) {
        delay(3000);
        pzem.resetEnergy();

      }

      // กดปุ่มจาก Blynk 5 วินาที เพื่อ Reset kWh กดค้างจนกว่า LED จะดับ
      if (Blynkreset_kWh == 1) {
        led_rst_kWh.on();
        delay(3000);
        pzem.resetEnergy();
        led_rst_kWh.off();
        pzem.resetEnergy();

      }
      if (Blynkreset_kWh == 0) {

        led_rst_kWh.off();

      }


      //  Reset kWh  แบบ Auto โดยการตั้งค่ามาจาก Blynk โดยเก็บไว้ใน EEPROM ของเครื่อง
      // ส่ง Email 1 ครั้งเท่านั้น
      if (Rst_D == rtc.day() && Rst_H == rtc.hour() &&  Rst_M == rtc.minute() && Email_status == 0) {
        pzem.resetEnergy();
        //Serial.println("reset kWh");มันจะ Reset ซ้ำ กันใน 1 นาทีนี้ และยกเลิกจนกว่าจะขึ้นนาทีใหม่
        String body = String("ค่าไฟฟ้าประจำเดือนที่ ") + (rtc.month() - 1) + String(" เป็นจำนวนเงิน ") + Electric_bill + String(" บาท ระบบทำการ Reset kWh = 0 แล้ว ***ให้ตรวจสอบค่า Ft ประจำเดือนด้วย***  By  PUYIOT");
        Blynk.email("xxxxxxxxxx@gmail.com", "แจ้งค่าไฟฟ้าประจำเดือน ", body);
        delay(500);
        Email_status = 1;
        Email_status1 = 0;
      }
      else if (Rst_D == rtc.day() && Rst_H == rtc.hour() &&  Rst_M == rtc.minute() && Email_status == 1) {
        delay(10);
        Email_status1 = 1;
      }

      if (Rst_M != rtc.minute() && Email_status1 == 1 ) {
        delay(10);
        Email_status = 0;
      }



      //1.2 อัตราปกติปริมาณการใช้พลังงานไฟฟ้าเกินกว่า 150 หน่วยต่อเดือน
      //ที่บ้านเราใช้ค่านี้ ประเภท 1125 คือ ใช้ไฟฟ้าเกิน 150 หน่วย/เดือน อัตราค่าบริการ 38.22 บาท/เดือน

      //1     -  150 หน่วยละ 3.2484  บาท

      if (energy >= 1 && energy <= 150) {
        kWh_1 = energy * 3.2484;
      }
      if (energy > 150) {
        kWh_1 = 150 * 3.2484;
      }


      //หน่วยที่  151    -  400 หน่วยละ 4.2218  บาท

      if (energy >= 151 && energy <= 400) {
        kWh_2 = (energy-150) * 4.2218;
      }
      if (energy > 400) {
        kWh_2 = 250 * 4.2218;
        //401        -- >   หน่วยละ 4.4217  บาท
        kWh_3 = (energy - 400) * 4.4217;
      }


      //รวมค่าใช้จ่ายทั้งหมด
      kWh_T = kWh_1 + kWh_2 + kWh_3;
      kWh_T1 = kWh_T + 38.22 + (energy * Ft);

      Electric_bill = kWh_T1 + (kWh_T1 * 0.07);

      lcd.setCursor(0, 3);
      lcd.print("B/M: ");
      lcd.setCursor(5, 3);
      lcd.print(Electric_bill , 2);
      lcd.setCursor(12, 3);
      //lcd.print("Bath");
      Blynk.virtualWrite(V7, Electric_bill);


      lcd.setCursor(13, 3);
      lcd.print(Rst_D, 0);
      //lcd.print("D");
      lcd.setCursor(15, 3);
      lcd.print(Rst_H, 0);
      lcd.setCursor(17, 3);
      lcd.print(":");
      //lcd.print("H");
      lcd.setCursor(18, 3);
      lcd.print(Rst_M, 0);

      delay(2000);

    }//loop






} Task_3_task;



//แสดง dd:mm:yy   hh:mm
class Task_4 : public Task {
  protected:
    void setup() { }

    void loop()  {
      rtc.refresh();
      int year_ = rtc.year() + 43; //+43 เพื่อให้ได้ปี พ.ศ. 63

      lcd.setCursor(0, 2);
      
//เติม 0 กรณีเป็นเลข วัน เดือน ชม. นาที วินาที ตัวเดียว
      if (rtc.day() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.day());
      }
      else lcd.print(rtc.day());
      lcd.print('-');


      if (rtc.month() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.month());
      }
      else lcd.print(rtc.month());
      lcd.print('-');

      lcd.print(year_);

      lcd.setCursor(12, 2);

      if (rtc.hour() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.hour());
      }
      else lcd.print(rtc.hour());

      lcd.print(':');



      if (rtc.minute() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.minute());
      }
      else lcd.print(rtc.minute());

      lcd.print(':');



      if (rtc.second() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.second());
      }
      else lcd.print(rtc.second());


      delay(1000);



    }
} Task_4_task;








//------------------------------------------------------------------------------------------------------------------------//
//*********************************************       void setup        **************************************************//
//------------------------------------------------------------------------------------------------------------------------//

void setup() {

  //-------IO NODE MCU Esp8266-------//
  pinMode(D0, OUTPUT);      //กำหนดโหมดใช้งานให้กับขา D0 เป็นขา สัญญาณไฟ ในสภาวะต่างๆ
  pinMode(D3, INPUT_PULLUP);//กำหนดโหมดใช้งานให้กับขา D3 เป็นขา กดปุ่ม ค้าง เพื่อตั้งค่า AP config


  // ให้ LED ทั้งหมดดับก่อน
  digitalWrite(D0, LOW);//ให้หลอด LED สีฟ้าดับก่อน

  //-------------------------------//


  Serial.begin(115200);
  //-------------------------------//

  EEPROM.begin(512);
  
  //ต้องการให้รู้ว่า ค่า Ft ที่เป็น Float ที่เก็บใน EEPROM มีค่า = ค่าที่ถูกป้อนมาจาก Bylnk จริงๆหรือปล่าว
  Ft = EEPROM.get(0, Ft_2);
  Serial.print("Ft in Void setup : ");
  Serial.println(Ft);
  
//ต้องการให้รู้ว่า ค่า วันที่ ที่เป็น Float ที่เก็บใน EEPROM มีค่า = ค่าที่ถูกป้อนมาจาก Bylnk จริงๆหรือปล่าว
  Rst_D = EEPROM.get(20, Rst_D2);
  Serial.print("Day in Void setup : ");
  Serial.println(Rst_D);
  
//ต้องการให้รู้ว่า ค่า ชั่วโมง ที่เป็น Float ที่เก็บใน EEPROM มีค่า = ค่าที่ถูกป้อนมาจาก Bylnk จริงๆหรือปล่าว
  Rst_H = EEPROM.get(30, Rst_H2);
  Serial.print("Hour in Void setup : ");
  Serial.println(Rst_H);
  
//ต้องการให้รู้ว่า ค่า นาที ที่เป็น Float ที่เก็บใน EEPROM มีค่า = ค่าที่ถูกป้อนมาจาก Bylnk จริงๆหรือปล่าว
  Rst_M = EEPROM.get(40, Rst_M2);
  Serial.print("Minute in Void setup : ");
  Serial.println(Rst_M);
  

  lcd.begin(20, 4);
  lcd.clear();




#ifdef ARDUINO_ARCH_ESP8266
  Wire.begin(4, 5); // GPIO4 = D2 and GPIO5 = D1 on ESP8266
#else
  Wire.begin();
#endif

  //rtc.set(0, 46, 11, 3, 3, 3, 20);//ใช้เฉพาะตั้งเวลา ถ้าจะตั้งก็ เอา // ออก เมื่อตั้งได้ตรงแล้วก็เอา // ใส่กลับไปเหมือนเดิม
  //  RTCLib::set(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year)







  //read configuration from FS json
  Serial.println("mounting FS...");//แสดงข้อความใน Serial Monitor

  if (SPIFFS.begin()) {
    Serial.println("mounted file system");//แสดงข้อความใน Serial Monitor

    if (SPIFFS.exists("/config.json")) {
      //file exists, reading and loading
      Serial.println("reading config file");//แสดงข้อความใน Serial Monitor
      File configFile = SPIFFS.open("/config.json", "r");

      if (configFile) {
        Serial.println("opened config file");
        size_t size = configFile.size();
        // Allocate a buffer to store contents of the file.
        std::unique_ptr<char[]> buf(new char[size]);

        configFile.readBytes(buf.get(), size);
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject(buf.get());
        json.printTo(Serial);
        if (json.success()) {
          Serial.println("\nparsed json");//แสดงข้อความใน Serial Monitor
          strcpy(blynk_token, json["blynk_token"]);

        } else {
          Serial.println("failed to load json config");//แสดงข้อความใน Serial Monitor
        }
      }
    }
  } else {
    Serial.println("failed to mount FS");//แสดงข้อความใน Serial Monitor
  }
  //end read



  //**************************        AP AUTO CONNECT   ********************************************//

  WiFiManagerParameter custom_blynk_token("blynk", "blynk token", blynk_token, 34);

  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;

  //set config save notify callback
  wifiManager.setSaveConfigCallback(saveConfigCallback);
  wifiManager.addParameter(&custom_blynk_token);


  for (int i = 5; i > -1; i--) {  // นับเวลาถอยหลัง 5 วินาทีก่อนกดปุ่ม AP Config
    digitalWrite(D0, HIGH);
    delay(500);
    digitalWrite(D0, LOW);
    delay(500);
    Serial.print (String(i) + " ");//แสดงข้อความใน Serial Monitor
  }


  if (digitalRead(D3) == LOW) {
    digitalWrite(D0, LOW);
    Serial.println("Button Pressed");//แสดงข้อความใน Serial Monitor

    wifiManager.resetSettings();//ให้ล้างค่า SSID และ Password ที่เคยบันทึกไว้


    // wifiManager.autoConnect(); ใช้สร้างชื่อแอคเซสพอยต์อันโนมัติจาก ESP + ChipID

    if (!wifiManager.autoConnect("PUYIOT AP CONFIG")) {
      Serial.println("failed to connect and hit timeout");//แสดงข้อความใน Serial Monitor
      delay(3000);
      //reset and try again, or maybe put it to deep sleep
      ESP.reset();
      delay(5000);

    }
  }



  Serial.println("Connected.......OK!)");//แสดงข้อความใน Serial Monitor
  strcpy(blynk_token, custom_blynk_token.getValue());


  //save the custom parameters to FS
  if (shouldSaveConfig) {
    Serial.println("saving config");//แสดงข้อความใน Serial Monitor
    DynamicJsonBuffer jsonBuffer;
    JsonObject& json = jsonBuffer.createObject();
    json["blynk_token"] = blynk_token;
    File configFile = SPIFFS.open("/config.json", "w");

    if (!configFile) {
      Serial.println("failed to open config file for writing");//แสดงข้อความใน Serial Monitor

    }
    json.printTo(Serial);
    json.printTo(configFile);
    configFile.close();
    //end save
  }

  //**************************    จบ    AP AUTO CONNECT   *****************************************//




  Serial.println("local ip"); //แสดงข้อความใน Serial Monitor
  delay(100);
  Serial.println(WiFi.localIP());//แสดงข้อความใน Serial Monitor
  Serial.println("gateway");
  delay(100);
  Serial.println(WiFi.gatewayIP());
  Serial.println("subnetMask");
  delay(100);
  Serial.println(WiFi.subnetMask());
  Serial.println("SSID");
  delay(100);
  Serial.println(WiFi.SSID());
  Serial.println("Password");
  delay(100);
  Serial.println(WiFi.psk());


  // Blynk.config(blynk_token);////เริ่มการเชื่อมต่อ Blynk Server แบบปกติ
  Blynk.config(blynk_token, server, port);
  ////เริ่มการเชื่อมต่อ Blynk Server*********สำหรับ Server local ที่แจกให้ เพิ่ม, server, port  แค่นี้จบ



  timer.setInterval(30000L, reconnecting);  //Function reconnect




  Scheduler.start(&Task_1_task);
  Scheduler.start(&Task_2_task);
  Scheduler.start(&Task_3_task);
  Scheduler.start(&Task_4_task);

  Scheduler.begin();




}


//------------------------------------------------------------------------------------------------------------------------//
//*********************************************   จบ  void setup        **************************************************//
//------------------------------------------------------------------------------------------------------------------------//



//------------------------------------------------------------------------------------------------------------------------//
//*********************************************       void Loop        ***************************************************//
//------------------------------------------------------------------------------------------------------------------------//


void loop() {



}

//------------------------------------------------------------------------------------------------------------------------//
//*********************************************      จบ void Loop       **************************************************//
//------------------------------------------------------------------------------------------------------------------------//





//------------------------------------------------------------------------------------------------------------------------//
void reconnecting()
{
  if (!Blynk.connected())
  {
    blynkIsDownCount++;
    BLYNK_LOG("blynk server is down! %d  times", blynkIsDownCount);
    Blynk.connect(5000);
  }
}


BLYNK_CONNECTED()
{

  Blynk.syncAll();//ให้ซิงค์ข้อมูลทั้งหมดล่าสุดจาก Blynk Server

}


มุ่งหวังให้คนไทย พัฒนา และสร้างนวัตกรรม IOT ใหม่ หรือจากข้อมูลเดิมที่มีอยู่แล้ว นำมาใช้ให้เกิดประโยชน์สูงกับตัวเอง และสังคม และก้าวต่อไปยังการทำ Product ด้วยฝีมือคนไทย ขอให้มีความสุขกับการทำงานนะครับ

1 Likes

ราตรีสวัสดิ์ที่รัก PUYOL ฉันมีข้อผิดพลาดนี้เมื่อรวบรวม
ESP8266_Pzem_004t_v3.0_v3: 28: 51: ข้อผิดพลาด: Scheduler.h: ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว

1 Likes

@GAMIX Hi where are you from

hi from mexico

1 Likes

Please capture picture error a paste to comment.

ฉันกำลังติดตั้งหลายไลบรารีรวมถึง ESP8266 ตารางเวลาและมันแสดงให้ฉันเห็นข้อผิดพลาดของการ์ด

ที่รักของฉัน Remy ใจดีมากสำหรับเวลาที่จะแสดงความคิดเห็นในข้อผิดพลาดการรวบรวมของฉันได้รับการแก้ไขแล้วและแสดงให้ฉันข้อผิดพลาดนี้โดยการเพิ่มห้องสมุดและรวบรวมมัน

Please paste full your code.

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include <FS.h>
#include <DNSServer.h>
#include <ArduinoJson.h>
#define BLYNK_MAX_SENDBYTES 1200
#include <BlynkSimpleEsp8266.h>
#include <EEPROM.h>


//------------------------------       Configuración en la sección Blynk        ------------------------------------------------//
#define BLYNK_DEBUG
#define BLYNK_PRINT Serial

int blynkIsDownCount = 0;

char blynk_token[34] = "8dwBaSBOE8htLFrK37JPAYB1hm8TaQkU";//Ingrese nuestro Blynk_token enviado por Blynk por correo electrónico al crear un nuevo proyecto.  8dwBaSBOE8htLFrK3217JPA1YB1hm8TaQkU"
BlynkTimer timer;

//----------------------------------  Determine el número de pin del nodo MCU ESP 8266.  --------------------------------------------//

#define D0 16 //Use el LED azul del MCU ESP8266 para tener una luz intermitente de acuerdo con el código escrito.

#define D3 0 // Úselo como un botón para acceder a la configuración AP de acuerdo con las necesidades del usuario.
//------------------------------------------------------------------------------------------------------------------------//

#include <Scheduler.h>   


// ------------------------Se puede utilizar la biblioteca de pantallas LCD. De esta manera, puede crear un personaje.---------------------------------//
#include <LCD.h>                          // Las líneas 1 y 2 se pueden cambiar.
#include <Wire.h>                         // Las líneas 1 y 2 se pueden cambiar.
#include <LiquidCrystal_I2C.h>            //Esta línea debe estar solo en la línea 3. Si va a las líneas 1 y 2, no se compilará.
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); //// Establezca la dirección del dispositivo de pantalla LCD con conexión I2C.
//------------------------------------------------------------------------------------------------------------------------//



bool shouldSaveConfig = false;

//callback notifying us of the need to save config
void saveConfigCallback () {
  Serial.println("Should save config");
  shouldSaveConfig = true;
}
//------------------------------------------------------------------------------------------------------------------------//


//-------------Para el servidor local distribuido, agregue **** solo 2 líneas--------------   (??????? Blynk Server ??????????????)  ----------//
char server[] = "oasiskit.com";
int port = 8080;
//------------------------------------------------------------------------------------------------------------------------//


#include <PZEM004Tv30.h>
PZEM004Tv30 pzem(12, 13); // 12(D6)=RX , 13(D7) = TX  :  TX->D6(GPIO012),RX->D7(GPIO013)
//La variable toma Ft de Blynk para almacenar en la EEPROM.
float Ft;
String Ft_1;
float Ft_2;

//La variable toma la fecha de Blynk y la almacena en la EEPROM.
float Rst_D;
String Rst_D1;
float Rst_D2;

//La variable toma el valor de hora de Blynk para almacenarlo en la EEPROM.
float Rst_H;
String Rst_H1;
float Rst_H2;

//La variable toma el valor minuto de Blynk para almacenarlo en la EEPROM.
float Rst_M;
String Rst_M1;
float Rst_M2;

//Variable para recibir facturas mensuales de electricidad.
float Electric_bill;
float kWh_1;
float kWh_2;
float kWh_3;
float kWh_T;
float kWh_T1;

//Presione el botón Restablecer kWh de Blynk.
int Blynkreset_kWh;
WidgetLED led_rst_kWh(V12);//Se ENCENDERÁ cuando mantenga presionado el botón durante 5 segundos


//RTC Library
#include "Arduino.h"
#include "Wire.h"
#include "uRTCLib.h"
// uRTCLib rtc;
uRTCLib rtc(0x68);

//Establezca la programación de correo electrónico solo una vez
int Email_status = 0;
int Email_status1 = 0;


BLYNK_WRITE(V10)

{
  Ft_1 = param.asString();//Heredar texto de Blynk en String

  Ft_2 = Ft_1.toFloat();//Convertir valor de cadena a flotante
  //Ingresamos el valor de Blynk como String = -0.116 para que Ft_1 lo obtenga.
  //Ft obtendrá la conversión de Ft_1 a Float = -0.12
  // Verás que el valor de Folat se redondea a 2 decimales, no mayor debido a la capacidad del dispositivo en sí.
  // causando que el costo se vuelva loco alrededor del 3.44%, causando la factura de electricidad / mes 3.44% menos que el valor real, aproximadamente 0.64 (150 unidades) - 2.14 (500 unidades) baht
  // Este proyecto no cambiará ningún valor porque el valor es ligeramente diferente (1 mes no usa menos de 500 unidades con seguridad)

  //Este conjunto se hizo en caso de que No está conectado a la aplicación Blynk, aún puede ver todos los valores correctamente Pero cada vez que se cambia el valor de Ft, la fecha, hora y minuto deben configurarse a través de la aplicación Blynk cada vez

  EEPROM.put(0, Ft_2); //Escriba el valor Ft_2 que es un flotante en la Dirección 0.
  EEPROM.commit(); //Terminar de escribir

  Ft = EEPROM.get(0, Ft_2);//Leer Ft_2 como flotante de la dirección 0
  Serial.println(Ft);//Mostrar Ft en el monitor serie
  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print("...... Sync Ft .....");
  lcd.setCursor(6, 3);
  lcd.print(Ft, 3);
  delay(2000);
  lcd.clear();

}


BLYNK_WRITE(V20)//Restablezca kWh automático para la fecha establecida desde Blynk en la máquina.
{
  Rst_D1 = param.asString();//Heredar texto de Blynk en String

  Rst_D2 = Rst_D1.toFloat();//Convertir valor de cadena a flotante
  
  //Este conjunto se hizo en caso de que No está conectado a la aplicación Blynk, todavía puede obtener el valor Rst kWh, pero cada vez que los cambios de fecha y hora deben configurarse a través de la aplicación Blynk
  EEPROM.put(20, Rst_D2); //Escriba el valor flotante Rst_D2 en la dirección 20.
  EEPROM.commit(); //Terminar de escribir

  Rst_D = EEPROM.get(20, Rst_D2);//Lea Rst_2, Flotante de la dirección 20.
  Serial.println(Rst_D);//Mostrar Rst en Serial Monitor
  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print("Sync Auto Rst By Day");
  lcd.setCursor(0, 2);
  lcd.print("     Day:  ");
  lcd.setCursor(10, 2);
  lcd.print(Rst_D, 0);
  delay(2000);
  lcd.clear();
}





BLYNK_WRITE(V21)//Restablezca kWh automático de acuerdo con las horas establecidas desde Blynk en la máquina.

{
  Rst_H1 = param.asString();//Heredar texto de Blynk en String
  Rst_H2 = Rst_H1.toFloat();//Convertir valor de cadena a flotante
  
  //Este conjunto se hizo en caso de que No está conectado a la aplicación Blynk, todavía puede obtener el valor Rst kWh, pero cada vez que los cambios de fecha y hora deben configurarse a través de la aplicación Blynk
  EEPROM.put(30, Rst_H2); //Escriba el valor Rst_H2 Float en la Dirección 30
  EEPROM.commit(); //Terminar de escribir
  
  Rst_H = EEPROM.get(30, Rst_H2);//Lea Rst_H2, Flotador de la dirección 30.
  Serial.println(Rst_H);//Mostrar Rst en Serial Monitor
  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print("Sync AutoRst By Hour");
  lcd.setCursor(0, 2);
  lcd.print("    Hour:  ");
  lcd.setCursor(10, 2);
  lcd.print(Rst_H, 0);
  delay(2000);
  lcd.clear();

}



BLYNK_WRITE(V22)//Restablezca el kWh automático de acuerdo con los minutos establecidos desde Blynk en la máquina.

{
  Rst_M1 = param.asString();//Heredar texto de Blynk en String

  Rst_M2 = Rst_M1.toFloat();//Convertir valor de cadena a flotante
  
  //Este conjunto se hizo en caso de que No está conectado a la aplicación Blynk, todavía puede obtener el valor Rst kWh, pero cada vez que los cambios de fecha y hora deben configurarse a través de la aplicación Blynk
  EEPROM.put(40, Rst_M2); //Escriba el valor Rst_H2 Float en la dirección 0
  EEPROM.commit(); //Terminar de escribir

  Rst_M = EEPROM.get(40, Rst_M2);//Leer Rst_M2, Flotar desde la dirección 40
  Serial.println(Rst_M);//Mostrar Rst en Serial Monitor
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("   Sync Auto Rst    ");
  lcd.setCursor(0, 1);
  lcd.print("     By Minute      ");
  lcd.setCursor(0, 3);
  lcd.print("  Minute:  ");
  lcd.setCursor(10, 3);
  lcd.print(Rst_M, 0);
  delay(2000);
  lcd.clear();

}











BLYNK_WRITE(V11)//Restablecer kWh en 5 segundos desde Blynk
{

  int pinValue = param.asInt();
  if (pinValue == 1) {
    Blynkreset_kWh = 1;
  }
  if (pinValue == 0) {
    Blynkreset_kWh = 0;
  }
}







//Blynk Server Connection
class Task_1 : public Task {
  protected:
    void setup() { }


    void loop()  {

      //-----------------------------------------------------------------------------------------------------------//
      if (Blynk.connected())
      {
        Blynk.run();
        digitalWrite(D0, LOW);

      } else {

        digitalWrite(D0, LOW);
        delay(200);
        digitalWrite(D0, HIGH);
        delay(100);

      }
      //-----------------------------------------------------------------------------------------------------------//

      timer.run();//Deja que Blynk tenga tiempo para trabajar.

      delay(500);

    }
} Task_1_task;



//Muestra los valores de PZEM.
class Task_2 : public Task {
  protected:
    void setup() { }


    void loop()  {

      float voltage = pzem.voltage();
      if (voltage != NAN) {
        //Serial.print("Voltage: "); Serial.print(voltage); Serial.println("V");
        lcd.setCursor(1, 0);
        lcd.print(voltage, 0);
        lcd.setCursor(4, 0);
        lcd.print("Vac");
        Blynk.virtualWrite(V1, voltage);
      } else {

      }


      float current = pzem.current();
      if (current != NAN) {
        //Serial.print("Current: "); Serial.print(current); Serial.println("A");
        lcd.setCursor(13, 0);
        lcd.print(current, 2);
        lcd.print(" A");
        Blynk.virtualWrite(V2, current);
      } else {

      }

      float power = pzem.power();
      if (current != NAN) {
        lcd.setCursor(0, 1);
        lcd.print(power, 2);
        lcd.setCursor(8, 1);
        lcd.print("W");


        Blynk.virtualWrite(V3, power);
      } else {

      }

      float energy = pzem.energy();
      if (current != NAN) {
        lcd.setCursor(10, 1);
        lcd.print(energy, 2);
        lcd.print(" kWh");
        Blynk.virtualWrite(V4, energy);
      } else {

      }

      float frequency = pzem.frequency();
      if (current != NAN) {
        lcd.setCursor(8, 0);
        lcd.print(frequency, 0);
        lcd.setCursor(10, 0);
        lcd.print("Hz");
        Blynk.virtualWrite(V5, frequency);
      } else {

      }

      float pf = pzem.pf();
      if (current != NAN) {

        Blynk.virtualWrite(V6, pf);
      } else {

      }
      delay(4000);

    }//loop



} Task_2_task;



//El cargo de electricidad se calculará de acuerdo con el uso en el hogar. Nuestras más de 150 unidades / mes. Cualquiera menos que esto puede pensar en la fórmula por nosotros mismos.
class Task_3 : public Task {
  protected:
    void setup() { }

    void loop()  {


      float energy = pzem.energy();

      // Presione el botón en la parte frontal del dispositivo durante 5 segundos para restablecer kWh.
      if (digitalRead(D3) == LOW) {
        delay(3000);
        pzem.resetEnergy();

      }

      // Presione el botón de Blynk durante 5 segundos para restablecer kWh. Mantenga presionado hasta que el LED se apague.
      if (Blynkreset_kWh == 1) {
        led_rst_kWh.on();
        delay(3000);
        pzem.resetEnergy();
        led_rst_kWh.off();
        pzem.resetEnergy();

      }
      if (Blynkreset_kWh == 0) {

        led_rst_kWh.off();

      }


      //  Reinicio automático kWh. La configuración es de Blynk, almacenada en la EEPROM de la máquina.
      // Envío de correo electrónico solo 1 vez.
      if (Rst_D == rtc.day() && Rst_H == rtc.hour() &&  Rst_M == rtc.minute() && Email_status == 0) {
        pzem.resetEnergy();
        //Serial.println ("restablecer kWh"); se restablecerá repetidamente en 1 minuto y se cancelará hasta que se cree un nuevo minuto.
        String body = String("La factura mensual de electricidad") + (rtc.month() - 1) + String(" Cantidad ") + Electric_bill + String(" Baht, el sistema tiene Reset Reset kWh = 0 *** *** Verifique el Ft *** mensual con By PUYIOT ");
        Blynk.email("mysmarthomepuy@gmail.com", " Notificación mensual de pago de electricidad ", body);
        delay(500);
        Email_status = 1;
        Email_status1 = 0;
      }
      else if (Rst_D == rtc.day() && Rst_H == rtc.hour() &&  Rst_M == rtc.minute() && Email_status == 1) {
        delay(10);
        Email_status1 = 1;
      }

      if (Rst_M != rtc.minute() && Email_status1 == 1 ) {
        delay(10);
        Email_status = 0;
      }



      //1.2 La tasa normal de consumo de electricidad es de más de 150 unidades por mes.
      //En nuestro país, este tipo de carga se utiliza para el tipo 1125, que consiste en usar electricidad más de 150 unidades / mes, la tasa es de 38,22 baht / mes.

      //1 - 150 baht 3.2484 baht por unidad
      
      if (energy >= 1 && energy <= 150) {
        kWh_1 = energy * 3.2484;
      }
      if (energy > 150) {
        kWh_1 = 150 * 3.2484;
      }


      //Unidades 151 - 400 por unidad 4.2218 baht

      if (energy >= 151 && energy <= 400) {
        kWh_2 = energy * 4.2218;
      }
      if (energy > 400) {
        kWh_2 = 250 * 4.2218;
        //401 -> 4.4217 baht por unidad
        kWh_3 = (energy - 400) * 4.4217;
      }


      //Gastos totales
      kWh_T = kWh_1 + kWh_2 + kWh_3;
      kWh_T1 = kWh_T + 38.22 + (energy * Ft);

      Electric_bill = kWh_T1 + (kWh_T1 * 0.07);

      lcd.setCursor(0, 3);
      lcd.print("B/M: ");
      lcd.setCursor(5, 3);
      lcd.print(Electric_bill , 2);
      lcd.setCursor(12, 3);
      //lcd.print("Bath");
      Blynk.virtualWrite(V7, Electric_bill);


      lcd.setCursor(13, 3);
      lcd.print(Rst_D, 0);
      //lcd.print("D");
      lcd.setCursor(15, 3);
      lcd.print(Rst_H, 0);
      lcd.setCursor(17, 3);
      lcd.print(":");
      //lcd.print("H");
      lcd.setCursor(18, 3);
      lcd.print(Rst_M, 0);

      delay(2000);

    }//loop






} Task_3_task;



//Mostrar dd:mm:yy   hh:mm
class Task_4 : public Task {
  protected:
    void setup() { }

    void loop()  {
      rtc.refresh();
      int year_ = rtc.year() + 43; //+43 para obtener el año 63

      lcd.setCursor(0, 2);

      //Agregue 0 en caso de número, día, mes, hora, minuto, segundo
      if (rtc.day() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.day());
      }
      else lcd.print(rtc.day());
      lcd.print('-');


      if (rtc.month() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.month());
      }
      else lcd.print(rtc.month());
      lcd.print('-');

      lcd.print(year_);

      lcd.setCursor(12, 2);

      if (rtc.hour() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.hour());
      }
      else lcd.print(rtc.hour());

      lcd.print(':');



      if (rtc.minute() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.minute());
      }
      else lcd.print(rtc.minute());

      lcd.print(':');



      if (rtc.second() < 10)
      {
        lcd.print("0");
        lcd.print(rtc.second());
      }
      else lcd.print(rtc.second());


      delay(1000);



    }
} Task_4_task;








//------------------------------------------------------------------------------------------------------------------------//
//*********************************************       void setup        **************************************************//
//------------------------------------------------------------------------------------------------------------------------//

void setup() {

  //-------IO NODE MCU Esp8266-------//
  pinMode(D0, OUTPUT);      //Establezca el modo de funcionamiento para el pin D0 para señalar el pin en varias condiciones.
  pinMode(D3, INPUT_PULLUP);//Establezca el modo activo en pin D3 a pin Mantenga presionado el botón para establecer la configuración AP.


  // Deje que todos los LED se apaguen primero.
  digitalWrite(D0, LOW);//Permita que el LED azul se apague primero.

  //-------------------------------//


  Serial.begin(115200);
  //-------------------------------//

  EEPROM.begin(512);

  //Quiere saber si el valor de Ft que es un Flotante almacenado en la EEPROM es el valor = el valor que realmente se ingresa desde Bylnk.
  Ft = EEPROM.get(0, Ft_2);
  Serial.print("Ft in Void setup : ");
  Serial.println(Ft);

  //Me gustaría saber si el valor de fecha en el Flotante almacenado en la EEPROM tiene el valor = el valor que realmente se ingresó desde Bylnk.
  Rst_D = EEPROM.get(20, Rst_D2);
  Serial.print("Day in Void setup : ");
  Serial.println(Rst_D);

  //Me gustaría saber que el valor horario del Flotador almacenado en la EEPROM tiene el valor = el valor que realmente se ingresa desde Bylnk.
  Rst_H = EEPROM.get(30, Rst_H2);
  Serial.print("Hour in Void setup : ");
  Serial.println(Rst_H);

  //Quiere saber si el valor minuto que es un flotante almacenado en la EEPROM es el valor = el valor que realmente se ingresa desde Bylnk
  Rst_M = EEPROM.get(40, Rst_M2);
  Serial.print("Minute in Void setup : ");
  Serial.println(Rst_M);


  lcd.begin(20, 4);
  lcd.clear();




#ifdef ARDUINO_ARCH_ESP8266
  Wire.begin(4, 5); // GPIO4 = D2 and GPIO5 = D1 on ESP8266
#else
  Wire.begin();
#endif

  //rtc.set(0, 46, 11, 3, 3, 3, 20);//Use solo el temporizador. Si desea configurarlo, tome // Desarmar. Una vez configurado correctamente, luego tome // poner como antes.
  //  RTCLib::set(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year)







  //read configuration from FS json
  Serial.println("mounting FS...");//Mostrar el mensaje en el monitor serie.

  if (SPIFFS.begin()) {
    Serial.println("mounted file system");//Mostrar el mensaje en el monitor serie.

    if (SPIFFS.exists("/config.json")) {
      //file exists, reading and loading
      Serial.println("reading config file");//Mostrar el mensaje en el monitor serie.
      File configFile = SPIFFS.open("/config.json", "r");

      if (configFile) {
        Serial.println("opened config file");
        size_t size = configFile.size();
        // Allocate a buffer to store contents of the file.
        std::unique_ptr<char[]> buf(new char[size]);

        configFile.readBytes(buf.get(), size);
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject(buf.get());
        json.printTo(Serial);
        if (json.success()) {
          Serial.println("\nparsed json");//Mostrar el mensaje en el monitor serie.
          strcpy(blynk_token, json["blynk_token"]);

        } else {
          Serial.println("failed to load json config");//Mostrar el mensaje en el monitor serie.
        }
      }
    }
  } else {
    Serial.println("failed to mount FS");//Mostrar el mensaje en el monitor serie.
  }
  //end read



  //**************************        AP AUTO CONNECT   ********************************************//

  WiFiManagerParameter custom_blynk_token("blynk", "blynk token", blynk_token, 34);

  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;

  //set config save notify callback
  wifiManager.setSaveConfigCallback(saveConfigCallback);
  wifiManager.addParameter(&custom_blynk_token);


  for (int i = 5; i > -1; i--) {  // Cuente 5 segundos antes de presionar el botón AP Config.
    digitalWrite(D0, HIGH);
    delay(500);
    digitalWrite(D0, LOW);
    delay(500);
    Serial.print (String(i) + " ");//Mostrar el mensaje en el monitor serie.
  }


  if (digitalRead(D3) == LOW) {
    digitalWrite(D0, LOW);
    Serial.println("Button Pressed");//Mostrar el mensaje en el monitor serie.

    wifiManager.resetSettings();//Borre el SSID y la contraseña que se guardó.


    // wifiManager.autoConnect(); Se usa para crear automáticamente puntos de acceso desde ESP + ChipID.

    if (!wifiManager.autoConnect("PUYIOT AP CONFIG")) {
      Serial.println("failed to connect and hit timeout");//Mostrar el mensaje en el monitor serie.
      delay(3000);
      //reset and try again, or maybe put it to deep sleep--reiniciar e intentar nuevamente, o quizás ponerlo en reposo profundo
      ESP.reset();
      delay(5000);

    }
  }



  Serial.println("Connected.......OK!)");//Mostrar el mensaje en el monitor serie.
  strcpy(blynk_token, custom_blynk_token.getValue());


  //save the custom parameters to FS
  if (shouldSaveConfig) {
    Serial.println("saving config");//Mostrar el mensaje en el monitor serie.
    DynamicJsonBuffer jsonBuffer;
    JsonObject& json = jsonBuffer.createObject();
    json["blynk_token"] = blynk_token;
    File configFile = SPIFFS.open("/config.json", "w");

    if (!configFile) {
      Serial.println("failed to open config file for writing");//Mostrar el mensaje en el monitor serie.
    }
    json.printTo(Serial);
    json.printTo(configFile);
    configFile.close();
    //end save
  }

  //**************************    TERMINA    AP AUTO CONNECT   *****************************************//




  Serial.println("local ip"); //Mostrar el mensaje en el monitor serie.
  delay(100);
  Serial.println(WiFi.localIP());//Mostrar el mensaje en el monitor serie.
  Serial.println("gateway");
  delay(100);
  Serial.println(WiFi.gatewayIP());
  Serial.println("subnetMask");
  delay(100);
  Serial.println(WiFi.subnetMask());
  Serial.println("SSID");
  delay(100);
  Serial.println(WiFi.SSID());
  Serial.println("Password");
  delay(100);
  Serial.println(WiFi.psk());


  // Blynk.config(blynk_token); //// Inicia una conexión normal del servidor Blynk.
  Blynk.config(blynk_token, server, port);
  ////Comience a conectarse a Blynk Server ********* para el servidor local que se entrega, servidor, puerto. Todo esto está hecho.



  timer.setInterval(30000L, reconnecting);  //Function reconnect




  Scheduler.start(&Task_1_task);
  Scheduler.start(&Task_2_task);
  Scheduler.start(&Task_3_task);
  Scheduler.start(&Task_4_task);

  Scheduler.begin();




}


//------------------------------------------------------------------------------------------------------------------------//
//*********************************************   TERMINA  void setup        **************************************************//
//------------------------------------------------------------------------------------------------------------------------//



//------------------------------------------------------------------------------------------------------------------------//
//*********************************************       void Loop        ***************************************************//
//------------------------------------------------------------------------------------------------------------------------//


void loop() {



}

//------------------------------------------------------------------------------------------------------------------------//
//*********************************************      Fin de void Loop       **************************************************//
//------------------------------------------------------------------------------------------------------------------------//





//------------------------------------------------------------------------------------------------------------------------//
void reconnecting()
{
  if (!Blynk.connected())
  {
    blynkIsDownCount++;
    BLYNK_LOG("blynk server is down! %d  times", blynkIsDownCount);
    Blynk.connect(5000);
  }
}


BLYNK_CONNECTED()
{

  Blynk.syncAll();//Sincronice todos los datos más recientes del servidor Blynk.
}

คำขอโทษสำหรับคนที่ฉันตอบว่าฉันเพิ่งรู้จักกับพอร์ทัลนี้และการแปลของฉันก็ซับซ้อน
คำขอโทษสำหรับคนที่ฉันตอบว่าฉันเพิ่งรู้จักกับพอร์ทัลนี้และการแปลของฉันก็ซับซ้อนรหัสของฉันก็เหมือนกันกับที่นี่ในพอร์ทัลESP8266_Pzem_004t_v3.0_v3.ino (23.6 KB)

i use library name : ESP8266Scheduler
you can Download here :

2 Likes

Your check code has "}" Is that correct?

image

ผมมีเรื่องแจ้งครับ จากกระทู้ นี้ ผมคิดว่ามี สมาชิก หรือผู้ที่ต้องการทำโปรเจคนี้ กำลังทำและลอง Code อยู่หรือปล่าวไม่แน่ใจ คือใน Code ที่ผมแชร์ไป มันมี Gmail ผมอยู่ด้วย ซึ่งตอนนี้ผมแก้เป็น xxxx@gmail.com แล้ว มันมี Gmail ผม ส่งเข้ามาหาครับ รายงานแจ้งค่าไฟประจำเดือน ซึ่งมันไม่ใช่ของผมครับ คืออยากแจ้งให้คนที่ทดลองทำอยู่ ทราบว่า งานคุณนะ OK แล้ว แต่ กรุณาเปลี่ยน Gmail เป็น Gmail ของคุณด้วยนะครับ :sweat_smile:
ส่วนหนึ่งขอน้อมรับความผิดพลาดนะครับที่ไม่ดูให้ดีก่อน

2 Likes

เมลมาจาก เมคซิโก รึป่าวครับพี่ 555 ดีที่ไม่เข้ารัวๆ

คิดได้ว่า จะหาความน่าเชื่อถือจาก PZEM ยังไง ก็เอาแบบง่ายๆ ที่คิดได้ดีกว่าครับ

วันที่ เวลา มิเตอร์การไฟฟ้า kWh PZEM kWh
6/3/2563 19.32 41608 (เกือบๆ 8) 21.978
6/3/2563 8.01 41604 (เกือบๆ4) 17.019
ค่าความต่าง 4 4.959

ค่าที่ได้แตกต่างกันเกือบ 1 หน่วย

ลองคิดหาสาเหตุ ค่าที่ผิดพลาดแบบง่ายๆ ดูก่อน

  1. เครื่องวัดของการไฟฟ้าเป็นแบบ Analog ส่วน PZEM เป็นแบบ Digital ก็ย่อมให้ค่าที่ออกมาแตกต่างกัน

  2. เครื่องวัดของการไฟฟ้าเป็นแบบ Analog มันบอกค่าจุดทศนิยมไม่ได้ ตำแหน่งสุดท้ายก็ต้องประมาณเอา

    31235

ในกรณีดังรูปหลักสุดท้าย อยู่ระหว่าง 3 กับ 4 ก็ต้องประมาณเอา ทำให้ค่าคลาดเคลื่อน กับแบบ Digital
3. ค่า Digital มากกว่าค่า Analog เป็นผลดีกับใคร :laughing:

เดี๋ยวพรุ่งนี้ จะเชคอีกทีครับ

Update ข้อมูล 14/03/2563

วันที่ เวลา มิเตอร์การไฟฟ้า kWh PZEM kWh
10/3/2563 9.19 41659 74.376
7/3/2563 8.46 41615 28.24
ค่าความต่าง 44 46.136
วันที่ เวลา มิเตอร์การไฟฟ้า kWh PZEM kWh
14/3/2563 8.3 41724 139.663
10/3/2563 9.19 41659 74.376
ค่าความต่าง 65 65.287

จะเห็นว่า ระหว่างเครื่องวัดแบบ Digital กับ Analog มีความใกล้เคียงกันมาก

3 Likes

พี่ทำแบบตารางเป็นด้วยหรอครับ ผมว่าผมยังไม่ได้ลงวิธีทำไว้นะ เยี่ยมไปเลยครับ :grin: