/* Author: Ariq */
/* Comment this out to disable prints and save space */
#define BLYNK_PRINT Serial
#define BLYNK_DEBUG
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

char auth[] = "YourAPI-keyfromBlynk";
char ssid[] = "YourWi-fi";
char pass[] = "YourWi-fiPassword";

int updateRate = 4;
bool stats = 1;
bool notifsend = 0;
bool firstStart = 1;
uint32_t limitkwh = 0;
BlynkTimer timer;

/* General */                                       
/* 1- AC Voltage Measurement */
int inputVoltageValue = 230;                

/* 2- AC Current Measurement */
        
//Analog Pin on NodeMCU ESP8266 is A0
int CurrentAnalogInputPin = A0;            
                                                    
                                                    
// CTTurn is the ratio of primary and seconday coil of CT
// You can see this on SCT-013 datasheet
float CTTurn = 2000;
                                                                                                        
float currentRead  = 0;               /* to read the current value from a sample*/
float currentLastSample  = 0;         /* Use for counting time for current sample */
float currentSum   = 0;               /* accumulation of current reading samples */
float sampelCount = 0;                /* to count number of sample. */
float currentMean ;                   /* to calculate the current average values from all samples*/ 
float RMSCurrentMean ;                /* square roof of currentMean*/
float FinalRMSCurrent ;               /* all raw readsings are calculated and convert to expected curent reading*/

float currentOffset = 0.00;
                                                    
int   OffsetRead = 0;             /* To switch between functions for auto callibation purpose */
float offsetcurrentSum= 0;        /* Accumulated / Sum value use for currentOffset2 purpose*/
float offsetcurrentSumMean = 0;   /* Is the average value use for currentOffset2 purpose*/
        
/*AC Power Measurement */
float apparentPower =   0;        /* recorded by multiplying RMS voltage and RMS current*/

/*Daily Energy Measurement*/
float finalEnergyValue = 0;       /* total accumulate energy */
float accumulateEnergy = 0;       /* accumulate of energy readings*/

BLYNK_CONNECTED() {
  if(firstStart){
    Blynk.syncAll();
    firstStart = 0;
  }
}

BLYNK_WRITE(V10){
  finalEnergyValue = param.asFloat()/1.352;
  //Serial.println(finalEnergyValue);
}

BLYNK_WRITE(V2){
  limitkwh = param.asInt();
  notifsend = 0;
  //Serial.println(limitkwh);
}

//Reset Button
BLYNK_WRITE(V3){
  bool rstSoft = param.asInt();
  if(rstSoft){
    resetValue();
  }
  //Serial.println(limitkwh);
}

void powerUsage()
{
  Blynk.virtualWrite(V0,apparentPower);
}

void sendkwh()
{
  //Preventing send message when already notified
  if(finalEnergyValue*1.352 > limitkwh && !notifsend){
    Blynk.notify("Alat mencapai batas penggunaan listrik");
    notifsend = 1;
  }
  Blynk.virtualWrite(V10, finalEnergyValue*1.352);
}

void setup()
{
  //Debug console
  Serial.begin(9600);
  OffsetRead = 1;
  
  //Calibrate sensor reading
  while(stats != 0){
    calibrate();
    yield();
  }
  
  resetValue();
  Serial.println(currentOffset);
  Serial.println("Finish Calibrating");
  Blynk.begin(auth, ssid, pass,"blynk-cloud.com",8080);
  Blynk.notify("Device started");
  
  // Setup some function to be called periodically
  timer.setInterval(10000L, powerUsage);
  timer.setInterval(30000L, sendkwh);
  timer.setInterval(updateRate, hitung);
}

void loop()
{
  Blynk.run();
  timer.run();
}

void calibrate(){
  if(millis() >= currentLastSample + 1){
    sampelArus();
    sampelCount++;
  }                                                               
  if(sampelCount == 5000){
    finalArus();
    currentOffset = -offsetcurrentSumMean;
    sampelCount = 0;
    stats = 0;
  }
}

void hitung(){
  sampelArus();
  sampelCount++;
  if(sampelCount == 1000/updateRate){
    finalArus();
    apparentPower = inputVoltageValue*FinalRMSCurrent;
    accumulateEnergy = apparentPower/3600;
    finalEnergyValue = finalEnergyValue + accumulateEnergy;
    sampelCount = 0;
  }
}

void sampelArus(){
  currentRead = analogRead(CurrentAnalogInputPin)-512 + currentOffset;
  currentSum = currentSum + sq(currentRead);
  offsetcurrentSum = offsetcurrentSum + currentRead ;
}

void finalArus(){
  offsetcurrentSumMean = offsetcurrentSum/sampelCount;
  currentMean = currentSum/sampelCount;
  RMSCurrentMean = sqrt(currentMean);
  FinalRMSCurrent = (((RMSCurrentMean / 1024) * 3.3) / 412) * CTTurn;
  FinalRMSCurrent = FinalRMSCurrent;
  currentSum = 0;
}


void resetValue(){
  currentRead  = 0;               /* to read the current value from a sample*/
  currentLastSample  = 0;               /* Use for counting time for current sample */
  currentSum   = 0;               /* accumulation of current reading samples */
  sampelCount = 0;               /* to count number of sample. */
  currentMean = 0 ;                         /* to calculate the current average values from all samples*/ 
  RMSCurrentMean = 0;                      /* square roof of currentMean*/
  FinalRMSCurrent = 0;
  powersampelCount = 0;
  energysampelCount = 0;
  finalEnergyValue = 0;
}
