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.
Para configurar el RTC, el siguiente:
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);
}