mandulaj / PZEM-004T-v30

Arduino library for the Updated PZEM-004T v3.0 Power and Energy meter
MIT License
259 stars 109 forks source link

PZEM-004T v3.0 Power and Energy meter" To save data to SD CARD #4

Closed pye222 closed 4 years ago

pye222 commented 5 years ago

"PZEM-004T v3.0 Power and Energy meter" is used very well.

But there is one thing that is not good.

It wants to store the data value in SD CARD. SD CARD stores data by SPI communication.

It works well if only one SD card is connected to Arduino. Or "PZEM-004T v3.0 Power and Energy meter".

The problem is that if both are connected, it will not work properly. Is there any way?

===============================code ==============================

// SD card attached MOSI - pin 11, MISO - pin 12, CLK - pin 13, CS - pin 10

include

PZEM004Tv30 pzem(7, 8);

include

const int chipSelect = 10; String inputString; boolean stringComplete = false; boolean doFlag = false; boolean closeFlag = false; boolean recFlag = false; long idx = 0; File myFile;

void setup() {

pinMode(SS, OUTPUT); Serial.begin(57600); // see if the card is present and can be initialized:

if (!SD.begin(chipSelect)) { Serial.println("Card failed, or not present"); // don't do anything more: while (1) ; } Serial.println("I'm Ready"); }

void loop() {

while (Serial.available()) { // get the new byte: char inChar = (char)Serial.read();

if (inChar != '\r' && inChar != '\n') {
  inputString += inChar;
  stringComplete = true;
} else {
  if (stringComplete) {
    Serial.println(inputString);

    if (inputString.equals("on")) {
      // Serial.println("on_ok");
      doFlag = true;
    } else if (inputString.equals("off")) {
      //  Serial.println("off_ok");
      doFlag = false;
      closeFlag = true;
    }
    inputString = "";
    stringComplete = false;
  }
}

}

if (doFlag) {

if ( recFlag == false) {
  Serial.println("card initialized.");
  char filename[] = "LOGGER00.TXT";
  for (uint8_t i = 0; i < 1000; i++) {
    filename[6] = i / 10 + '0';
    filename[7] = i % 10 + '0';
    if (! SD.exists(filename)) {
      Serial.println(filename);
      myFile = SD.open(filename, FILE_WRITE);  // only open a new file if it doesn't exist
      break;  // leave the loop!
    }
  }
  if (myFile) {
    recFlag = true;
  }
  if (! myFile) {
    Serial.println("couldnt create file");
  }
}

    float voltage = pzem.voltage();

    if (voltage != NAN) {
      Serial.print("Voltage: "); Serial.print(voltage); Serial.println("V");
    } else {
      Serial.println("Error reading voltage");
    }

Serial.println(voltage);
myFile.println(voltage);
myFile.flush();
delay(2000);

} else if (closeFlag) { Serial.println("gps recording stop"); recFlag = false; doFlag = false; closeFlag = false; myFile.flush(); myFile.close(); } }

Alfonso-Lijo commented 4 years ago

I have a same problem! Now ! tri to put a external power supply.

Alfonso-Lijo commented 4 years ago

I just change the PZEM004T.h line 34 (#define PZEM_DEFAULT_ADDR 0x0F to 0x001 and work now!

Alfonso-Lijo commented 4 years ago

This library needs tweaking! I've managed to keep up communication when I add the RTC and LCD control libraries, but sending anything to the LCD just ends communication!

Alfonso-Lijo commented 4 years ago

Success! I was able to perform all the steps using Arduino Mega! The reason is quite obvious! Arduino Uno has only one serial port and simulates other serials on digital pins! But the arduino Mega has 5 independent serial ports! But attention: I do not know if I made a mistake or is a failure in the library, but only managed from the HardSerial example! Follow my code! I use an RTC1307, an SD card reader and a 20x4 LCD on my Datalogger!

/* ** SD - Se comunica por SPI MOSI -> pino 51 MISO -> pino 50 SCK -> pino 52 CS (SS) -> Pino 53

Relógio RTC DS1307 - Se comunica por I2C no endereço 0x68 SDA -> pino 20 SCL -> pino 21

LCD se comunica por IC2 no endereço 0x27 SDA -> pino 20 SCL -> pino 21

Coleta de dados do Pzem 004 Pinos de comunicação Pzem (módulo Max485 RS 232 para Modbus) Pino 14 TX fio verde Pino 15 RX fio branco

Liga Backligth do LCD pino digital 3 aciona o backligth */

include // Biblioteca de configuração do LCD

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Inicializa o display no endereco 0x27

include //Uso a biblioteca SdFat no lugar da original do arduino pois é mais otimizada.

SdFat SD; //Linha necessária para compatibilizar o programa escrito para SD.h (biblioteca original) const int CS_SD = 53; //Pino do cartão SD

include //Biblioteca para controle do RTC DS1307

include //Biblioteca do monitor de energia Pzem

PZEM004Tv30 pzem(&Serial3); // TX(Verde),RX(Branco) conectados para RX,TX do PZEM

// Constantes variadas

const unsigned long intervaloLeitura = 2000; //inervalo de leitura [milisegundos] const unsigned long intervaloEscritaSD = 300; //intervalo de escrita de dados no SD [milisegundos] boolean temSD = 0; // Variável para indicar presença de cartão SD const int intervaloSincro = 300; //Intervalo de sincronismo do arduino com o RTC [Seg.]. Sincronismo do RTC //----------------------------------------------------------------------------------------------------------------

void setup() { lcd.begin (20, 4); //definição do LCD 20 colunas e 4 linhas Serial.begin(115200); //Inicia a comunicação em 9600 kbps

//Inicializa cartão SD // verifica se o SD está presente e pode ser inicializado: if (!SD.begin(CS_SD)) { temSD = 0; //Cartão SD não detectado Serial.print("Cartão SD não detectado"); Serial.println(); } else { temSD = 1; //Cartão SD encontrado Serial.print("Cartão está presente"); Serial.println(); } //Inicializa o RTC e contagem de tempo setTime(RTC.get()); setSyncProvider(RTC.get); //Estabelecemos o RTC como provedor do tempo setSyncInterval(intervaloSincro); // Atualizamos seguindo intervalo definido }

void loop() { //iniciamos com as leituras dos dados do PZEM float voltage = pzem.voltage(); if (voltage != NAN) { Serial.print("Voltage: "); Serial.print(voltage); Serial.println("V"); } else { Serial.println("Error reading voltage"); }

float current = pzem.current(); if (current != NAN) { Serial.print("Current: "); Serial.print(current); Serial.println("A"); } else { Serial.println("Error reading current"); }

float power = pzem.power(); if (current != NAN) { Serial.print("Power: "); Serial.print(power); Serial.println("W"); } else { Serial.println("Error reading power"); }

float energy = pzem.energy(); if (current != NAN) { Serial.print("Energy: "); Serial.print(energy, 3); Serial.println("kWh"); } else { Serial.println("Error reading energy"); }

float frequency = pzem.frequency(); if (current != NAN) { Serial.print("Frequency: "); Serial.print(frequency, 1); Serial.println("Hz"); } else { Serial.println("Error reading frequency"); }

float pf = pzem.pf(); if (current != NAN) { Serial.print("PF: "); Serial.println(pf); } else { Serial.println("Error reading power factor"); }

static unsigned long millisPrevio = intervaloLeitura; static unsigned long millisPrevio2 = intervaloEscritaSD; static String dados; unsigned long millisActual = millis();

// Enviamos os dados de hora e leituras do Pzem para o LCD trocando a cada 5 segundos

lcd.clear(); lcd.setBacklight(HIGH); lcd.setCursor(0, 0); lcd.print("Usina AlfonsoLijo"); lcd.setCursor(0, 1); lcd.print("Capacidade 2.9 kWp"); lcd.setCursor(0, 2); lcd.print ("Data:"); lcd.setCursor(6, 2); lcd.print(timeLabel1()); //funciona lcd.setCursor(0, 3); lcd.print("Hora:"); lcd.setCursor(6, 3); lcd.print(timeLabel2()); delay(15000); lcd.clear(); lcd.setCursor(0, 0); lcd.print(voltage); lcd.setCursor(7, 0); lcd.print("V"); lcd.setCursor(10, 0); lcd.print(current); lcd.setCursor(17, 0); lcd.print("A"); lcd.setCursor(0, 1); lcd.print(power); lcd.setCursor(17, 1); lcd.print("kW"); lcd.setCursor(0, 2); lcd.print(energy); lcd.setCursor(17, 2); lcd.print("kWh"); lcd.setCursor(0, 3); lcd.print(frequency); lcd.setCursor(7, 3); lcd.print("Hz"); lcd.setCursor(10, 3); lcd.print(pf); lcd.setCursor(17, 3); lcd.print("PF"); delay(15000);

if (millisActual - millisPrevio >= intervaloLeitura) { //Executa as siguintes ações só se é cumprido o intervalo

dados = timeLabel();           // coloca a marca de tempo no String dados
dados = dados + ", " + String(voltage);  //vai adicionando os valores medidos na string
dados = dados + ", " + String(current);
dados = dados + ", " + String(power);
dados = dados + ", " + String(energy);
dados = dados + ", " + String(frequency);
dados = dados + ", " + String(pf);
Serial.println(dados);            //mostra resultado na porta serial para simples consulta

millisPrevio = millisActual;

}

if (millisActual - millisPrevio2 >= intervaloEscritaSD * 60UL) { //se cumprido o intervalo especificado, procede a gravação dos dados if (gravardadosSD(dados)) { //grava os dados no SD - se houver erro, retorna 1 } millisPrevio2 = millisActual; } }

//função que devolve um String com a Hora/data atual //Adiciona um '0' na esquerda nos valores de uma casa decimal e formata a hora/data

String timeLabel() { time_t t = now(); String tempoAtual = String(); tempoAtual = year(t); if (month(t) < 10) tempoAtual = String(tempoAtual + '-' + '0' + month(t)); else tempoAtual = String(tempoAtual + '-' + month(t)); if (day(t) < 10) tempoAtual = String(tempoAtual + '-' + '0' + day(t)); else tempoAtual = String(tempoAtual + '-' + day(t)); tempoAtual = String(tempoAtual + ','); if (hour(t) < 10) tempoAtual = String(tempoAtual + '0' + hour(t)); else tempoAtual = String(tempoAtual + hour(t)); tempoAtual = String(tempoAtual + ':'); if (minute(t) < 10) tempoAtual = String(tempoAtual + '0' + minute(t) + ':'); else tempoAtual = String(tempoAtual + minute(t) + ':'); if (second(t) < 10) tempoAtual = String(tempoAtual + '0' + second(t)); else tempoAtual = String(tempoAtual + second(t)); return tempoAtual; }

String timeLabel1() { time_t t = now(); String tempoAtual1 = String(); tempoAtual1 = String(tempoAtual1 + day(t) + '-'); tempoAtual1 = String(tempoAtual1 + month(t) + '-'); tempoAtual1 = String(tempoAtual1 + year(t)); return tempoAtual1; }

String timeLabel2() { time_t t = now(); String tempoAtual2 = String(); if (hour(t) < 10) tempoAtual2 = String(tempoAtual2 + '0' + hour(t)); else tempoAtual2 = String(tempoAtual2 + hour(t)); tempoAtual2 = String(tempoAtual2 + ':'); if (minute(t) < 10) tempoAtual2 = String(tempoAtual2 + '0' + minute(t) + ':'); else tempoAtual2 = String(tempoAtual2 + minute(t)); // if (second(t) < 10) // tempoAtual2 = String(tempoAtual2 + '0' + second(t)); // else // tempoAtual2 = String(tempoAtual2 + second(t)); return tempoAtual2; }

// Função que registra as medições feitas em um arquivo "meulog.cvs" // Se o arquivo existir, os dados serão adicionados e, se não forem criados, os mesmos // Recebe uma String com o formato a ser gravado // Retorna 0 se correu bem e 1 se houve um erro

boolean gravardadosSD(String data) { time_t t = now(); String date = String(); //Geramos um string para usar de nome de arquivo com a data do dia. date = year(t); if (month(t) < 10) date = String(date + '0' + month(t)); else date = String(date + month(t)); if (day(t) < 10) date = String(date + '0' + day(t)); else date = String(date + day(t)); date = String(date + ".csv"); char fileName[13]; date.toCharArray(fileName, 13); //convertemos o string para uma array de caracteres (exigido pela biblioteca do SD)

if (!temSD) { return 1; //termina a função se o SD não esta inicializado } if (SD.exists(fileName)) { //Se existe o arquivo "fileName" o abrimos e adicionamos os dados. File meulog = SD.open(fileName, FILE_WRITE); if (meulog) { //Se o arquivo se abre corretamente escrevemos nele ... meulog.println(data); meulog.close(); return 0; } else { return 1; //Não se conseguiu abrir o arquivo ... } } else { //se não existe o archivo "meulog.cvs" criamos um novo e gravamos os dados ... File meulog = SD.open(fileName, FILE_WRITE); if (meulog) { //Se o arquivo é aberto corretamente gravamos os dados ... meulog.println(F("Data, Hora, Volt, Amp, Watt, kWh, Hz, FP")); meulog.println(data); meulog.close(); return 0; } else { return 1; } } }

Alfonso-Lijo commented 4 years ago

Thank you so much mandulaj for the beautiful work!

pye222 commented 4 years ago

I'm glad that it works well with mega. I did software serial communication with two Arduinos. And one side PZEM-004T v3.0 was installed and data was transferred to another Arduino. Other Arduinos were used by connecting Sd card, LCD, and RTC

Alfonso-Lijo commented 4 years ago

I am a beginner in Arduino! I am trying to improve my code so that the data sent to the LCD does not interfere with the data recording! As it stands today, LCD display time directly interferes with the amount of recorded data! Could someone help me sort this out so that I can record more data on the SD card while keeping the display time of the data on the LCD? I would be very grateful!

Alfonso-Lijo commented 4 years ago

Hello! It works and very well! I made a three phase datalogger reading three Pzem in serial 1, 2 and 3. Without any interference or failure I am logging the data every second! Fantastic! Thank you very much!

Em dom, 8 de set de 2019 às 06:04, pye222 notifications@github.com escreveu:

I'm glad that it works well with mega. I did software serial communication with two Arduinos. And one side PZEM-004T v3.0 was installed and data was transferred to another Arduino. Other Arduinos were used by connecting Sd cards, LCDs, and RTCs.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/mandulaj/PZEM-004T-v30/issues/4?email_source=notifications&email_token=AM6FWZPCRT6OIOKR3LJJ4WDQIS52BA5CNFSM4H7LQ3H2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD6FLMNA#issuecomment-529184308, or mute the thread https://github.com/notifications/unsubscribe-auth/AM6FWZKJ4QDZLOTXMEUK773QIS52BANCNFSM4H7LQ3HQ .

mandulaj commented 4 years ago

@Alfonso-Lijo, thanks for using my library, I wasn't expecting many people to do so. I was having similar issues and honestly I have no idea how to make it work more elegantly on an UNO . If you have any suggestions, please feel free to open up a pull request!

RB3rg commented 4 years ago

@mandulaj I would not waste time with software serial, we all know it is a workaround for old devices. Most of all new devices (esp32, stm32) have more than one HW Serial. In the real-life, no one will (or should) put in production a device for monitoring power using software serial. It ends up most of the people using SW Serial it's bench test.

mandulaj commented 4 years ago

@pye222 Is this still something that needs work? Have you found a way to fix or some kind of work around?

Alfonso-Lijo commented 4 years ago

It's working within my needs and I haven't made any more changes! Thanks!

mandulaj commented 4 years ago

Ok, in that case I will close this issue.