Medidor de Potencia con Pzem-004T V3

Buen día Comunidad, les comparto un proyecto que realicé hace un tiempito, que me encontré con poca información o muy dispersa y creo que a alguien le puede servir. Parte del contenido fue tomado de otras personas o librerías.

Se trata de una implementación de Datalogger de variables eléctricas utilizando un modulo Pzem-004T-V3 utilizando un Arduino Uno, un modulo SD y un RTC DS1302.

La necesidad de realizar este equipo surge a raíz de la necesidad de contar con curvas diarias de consumo de un usuario (vivienda, establecimiento o pequeña instalación aislada) para realizar un análisis de mejora de eficiencia y optimización de un sistema de generación aislada a base de paneles solares y/o pequeños aerogeneradores. La definición de los materiales fue una elección de disponibilidad y económica. El datalogger propuesto se basa en una placa PZEM004T-V3, la cual realiza la medición de variables eléctricas y las envía a un Arduino UNO, este las almacena en una SD. Para conocer fecha y hora de la toma de los datos se utiliza un RTC (Real Time Clock) que mantiene la hora por más que el equipo se encuentre apagado o desenergizado.


Código:
#include <PZEM004Tv30.h>
#include <stdio.h>
#include <DS1302.h>
#include <SPI.h>
#include <SD.h>

PZEM004Tv30 pzem(9,10);  /// use Serial // el pin 9 va al Tx del Pzem y el 10 al Rx del Pzem

DS1302 rtc(8, 7, 6); // rtc(kCePin, kIoPin, kSclkPin) //rtc(pin reset, pin data, pin Clock)
// SD Card por defecto utilia SPI bus, como:
//MOSI - pin 11; MISO - pin 12; CLK - pin 13; CS - pin 4.


// Delcaración de Variables que se van a escribir en el SD
float voltage_sd =0;
float current_sd =0;
float power_sd =0;
float energy_sd =0;
float frecuency_sd =0;
float pf_sd =0;

// Variables auxiliares que se usan en el promedio
float vol =0;
float cur =0;
float pot =0;

// Variables auxiliares para el manejo de los tiempos
unsigned long lastMillis = 0;
unsigned long lastMillisprom = 0;
unsigned long nUnsignedLong = 4294967295;  //Cifra máxima almacenable en un unsigned long
unsigned long tiempo = 10000; //Tiempo de esctitura en la SD en ms.

Time t = rtc.time();

const int chipSelect = 4; // selecciona el SD
File dataFile;

// Variables auxiliares para el Tamaño del Archivo guardado en SD
unsigned long Archivosize = 3500000; // Tamaño del archivo que guardamos, en Byte.
unsigned long Actualsize = 0;


int Contador = 0;
char filename[16];
int i = 0;


void setup()
{
 pinMode(3, OUTPUT);
 digitalWrite(3, LOW); //parpadeo de escritura ok
 pinMode(2, OUTPUT); //Led de no reconocimiento de la SD

 
//No  Debug console    
// Serial.begin(9600);              // Note : Do not use Serial0 port, for serial debugging!
// Serial.print("Initializing SD card...");

// see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
 //   Serial.println("Card failed, or not present");
    digitalWrite(2, HIGH); //Led de no reconocimiento de la SD
    return;
  }
  else {
 //   Serial.println("card initialized.");
    digitalWrite(2, LOW); //Led de no reconocimiento de la SD
  }
 
// Creamos el fichero de registro
  for (i = 0; i < 100; i++)
     {
        sprintf(filename, "data%03d.csv", i);
 //       Serial.print("Filename:"); Serial.println(filename);
        if (!SD.exists(filename)) {     // Si no existe el fichero, lo creamos
 //             Serial.println(SD.exists(filename));
              dataFile = SD.open(filename, FILE_WRITE);
 //             Serial.println(dataFile);
              if (dataFile) {
 //               Serial.println("Escribiendo encabezado en el .csv");
                dataFile.println("Fecha, Hora, Tensión[V], Corriente[I], Potencia[W], Energía[kW/h], Frecuencia[Hz], FP");
              }
              Actualsize = dataFile.size();
              dataFile.close();
           
              break;  // leave the loop!
        }
        else{
          dataFile = SD.open(filename);
          Actualsize = dataFile.size();
          dataFile.close();
        }
       
//        Serial.println(Actualsize);
        if (SD.exists(filename)&& Actualsize<Archivosize) {     // Si existe el fichero y pesa menos de Archivosize, lo abrimos y usamos
              break;  // leave the loop!  
        }
     }
  vol = vol + pzem.voltage();
}


void loop(){

  if ( (nUnsignedLong - millis()) > tiempo ){   //evitamos overflow de millis
  if (millis() - lastMillis > tiempo) {
   lastMillis = millis();

//Toma el valor del Reloj-RTC
    Time t = rtc.time();

//Abrimos el archivo en la SD
  dataFile = SD.open(filename, FILE_WRITE);
  // if the file is available, write to it:
  if (dataFile) {                                 //prueba, ver que no lea si no hay SD
    digitalWrite(3, HIGH); //parpadeo de ok
    digitalWrite(2, LOW); //Led de no reconocimiento de la SD
   
// Read meter PZEM
    float v = vol/Contador;
    float i = cur/Contador;
    float p = pot/Contador;  
    float e = pzem.energy();
    float fr = pzem.frequency();
    float pf = pzem.pf();

    vol = 0;
    cur = 0;
    pot = 0;
    Contador = 0;
   
// Asigna los valores leídos si son mayor a cero, como medida de una lectura correcta
   if(v  >= 0.0){voltage_sd =v;}    //V
   if(i  >= 0.0){current_sd =i;}    //A                                                                                                                    
   if(p  >= 0.0){power_sd =p;}      //W
   if(e  >= 0.0){energy_sd =e;}     //kWh
   if(fr >= 0.0){frecuency_sd =fr;} //Hz
   if(pf >= 0.0){pf_sd =pf;}     //Factor de Potencia

// Escribir en la SD en CSV
    // Escribimos la fecha en la SD - AAAA/MM/DD
    dataFile.print(t.yr);
    dataFile.print("/");
    dataFile.print(t.mon);
    dataFile.print("/");
    dataFile.print(t.date);
    dataFile.print(","); //esta coma separa la fecha de la hora al momento de imporar el .csv
         
    // Escribimos la hora en la SD - hh:mm:ss
    dataFile.print(t.hr);
    dataFile.print(":");
    dataFile.print(t.min);
    dataFile.print(":");
    dataFile.print(t.sec);
    dataFile.print(",");
   
    // Escribimos la V , I , W , kWh , Hz , FP
    dataFile.print(v);
    dataFile.print(",");
    dataFile.print(i);
    dataFile.print(",");
    dataFile.print(p);
    dataFile.print(",");
    dataFile.print(e);
    dataFile.print(",");
    dataFile.print(fr);
    dataFile.print(",");
    dataFile.println(pf);

    Actualsize = dataFile.size();
    // Cerramos el archivo para la escritura en la SD
    dataFile.close();
/*  
//imprimimos la en el puerto serie
    Serial.print("Fecha:");
    Serial.print(t.yr);
    Serial.print(t.mon);
    Serial.print(t.date);
           
    Serial.print(" Hora:");
    Serial.print(t.hr); Serial.print(":");
    Serial.print(t.min); Serial.print(":");
    Serial.print(t.sec);Serial.print(" ");
    Serial.print("V=");
    Serial.print(v);
    Serial.print("V, I=");
    Serial.print(i);          
    Serial.print("A, P=");
    Serial.print(p);
    Serial.print("W, E=");
    Serial.println(e);

*/
    digitalWrite(3, LOW); //parpadeo de ok
    lastMillisprom = millis();
   }
 
// if the file isn't open, pop up an error:
   else {
//    Serial.println("error opening datalog.txt");
    digitalWrite(2, HIGH); //Led de no reconocimiento de la SD
    delay (2000);
    SD.begin(chipSelect);
    dataFile = SD.open(filename);
    Actualsize = dataFile.size();
    dataFile.close();  
   }
 }

// Toma de datos para promedio
 if (millis() - lastMillisprom > tiempo/6) {
  lastMillisprom = millis();
  vol = vol + pzem.voltage();
  cur = cur + pzem.current();
  pot = pot + pzem.power();
  Contador = Contador +1;
 }
 
 }
 else {
  delay(5000); //asegurarnos de que millis() vuelva a cero y arrancar de nuevo. Para evitar el overflow de la funcion millis
  lastMillis = millis();
 }

 if (Actualsize >= Archivosize){   //partimos el archivo por pesar más de Archivosize
 for (i=i; i < 100; i++)
     {
        sprintf(filename, "data%03d.csv", i);
 //       Serial.print("Filename:"); Serial.println(filename);
        if (!SD.exists(filename)) {     // Si no existe el fichero, lo creamos
 //             Serial.println(SD.exists(filename));
              dataFile = SD.open(filename, FILE_WRITE);
 //             Serial.println(dataFile);
              if (dataFile) {
//                Serial.println("Escribiendo encabezado");
                dataFile.println("Fecha, Hora, Tensión[V], Corriente[I], Potencia[W], Energía[kW/h], Frecuencia[Hz], FP");
              }
              Actualsize = dataFile.size();
              dataFile.close();
           
              break;  // leave the loop!
        }
     }
 }
}


Para configurar el RTC, el siguiente:

Código:
// Example sketch for interfacing with the DS1302 timekeeping chip.
//
// Copyright (c) 2009, Matt Sparks
// All rights reserved.
//
// http://quadpoint.org/projects/arduino-ds1302
#include <stdio.h>
#include <DS1302.h>

namespace {

// Set the appropriate digital I/O pin connections. These are the pin
// assignments for the Arduino as well for as the DS1302 chip. See the DS1302
// datasheet:
//
//   http://datasheets.maximintegrated.com/en/ds/DS1302.pdf
const int kCePin   = 8;  // Chip Enable
const int kIoPin   = 7;  // Input/Output
const int kSclkPin = 6;  // Serial Clock

// Create a DS1302 object.
DS1302 rtc(kCePin, kIoPin, kSclkPin);

String dayAsString(const Time::Day day) {
  switch (day) {
    case Time::kSunday: return "Sunday";
    case Time::kMonday: return "Monday";
    case Time::kTuesday: return "Tuesday";
    case Time::kWednesday: return "Wednesday";
    case Time::kThursday: return "Thursday";
    case Time::kFriday: return "Friday";
    case Time::kSaturday: return "Saturday";
  }
  return "(unknown day)";
}

void printTime() {
  // Get the current time and date from the chip.
  Time t = rtc.time();

  // Name the day of the week.
  const String day = dayAsString(t.day);

  // Format the time and date and insert into the temporary buffer.
  char buf[50];
  snprintf(buf, sizeof(buf), "%s %04d-%02d-%02d %02d:%02d:%02d",
           day.c_str(),
           t.yr, t.mon, t.date,
           t.hr, t.min, t.sec);

  // Print the formatted string to serial so we can see the time.
  Serial.println(buf);
}

}  // namespace

void setup() {
  Serial.begin(9600);

  // Initialize a new chip by turning off write protection and clearing the
  // clock halt flag. These methods needn't always be called. See the DS1302
  // datasheet for details.
  rtc.writeProtect(false);
  rtc.halt(false);

  // Make a new time object to set the date and time.
  //   t(Año, Mes, Día, Hora, Minuto, Segundos, mombre del día);
  Time t(2021, 7, 15, 8, 42, 50, Time::kThursday); //colocar la fecha y hora al momento de cargar los datos. El programa tarda aproximadamente 10seg en cargarse.

  // Set the time and date on the chip.
  rtc.time(t);
}

// Loop and print the time every second.
void loop() {
  printTime();
  delay(1000);
}
 

Adjuntos

  • Datalogger Medidas Eléctricas.pdf
    629 KB · Visitas: 5
Arriba