Leonel José Henriquez Orellana vor 5 Jahren
Commit
46ff53f269

+ 564 - 0
DENMARK_V1.4_SDM630/DENMARK_V1.4_SDM630.ino

@@ -0,0 +1,564 @@
+#include <FS.h>
+#include <ESP8266WiFi.h>
+#include <WiFiClient.h>
+#include <PubSubClient.h>
+#include <SDM.h>
+#include <SoftwareSerial.h>
+#include <ArduinoJson.h>
+#include <RTClib.h>
+#include <Wire.h>
+#include <SPI.h>
+#include <SD.h>
+#include <EEPROM.h>
+#include <WiFiManager.h> 
+#include <ESP8266WebServer.h>
+#include <DNSServer.h>
+
+File myFile;
+#define ESP8266
+int band =1;
+int band_day =1;
+int dia_next;
+int mes_next;
+int dia_nextEEPROM;
+int mes_nextEEPROM;
+char t2_EEPROM[15];
+int anho_EEPROM;
+int dia_EEPROM;
+int mes_EEPROM;
+int diaEEPROM;
+int linea1_EEPROM;
+RTC_DS3231 rtc;
+
+char meterID[10];
+
+//Configuracion contador
+SoftwareSerial swSerSDM;
+SDM sdm(swSerSDM, 9600, 0, SWSERIAL_8N1, 2, 16);
+
+
+// Servidor MQTT
+char   SERVER[50]   = "denmark.inverlec.solar"; // denmark.inverlec.solar
+int    SERVERPORT   = 31010;
+
+
+WiFiClient espClient;
+PubSubClient client(espClient);
+
+// Callback al recibir mensaje por MQTT
+void callback(char* topic, byte* payload, unsigned int length) {
+  Serial.print("Message arrived [");
+  Serial.print(topic);
+  Serial.print("] ");
+  for (int i = 0; i < length; i++) {
+    Serial.print((char)payload[i]);
+  }
+  Serial.println();
+}
+
+//funcion para mandar a mqqt la lectura cada 10s, 15 min y guardar en memoria SD
+int denmark() {
+
+  char bufout[10]; 
+  //Variable JSON que se mandara a mqtt aproximadamente cada 10 segundos 
+  char t1[15];
+  char t2 [15];
+  char t3[15];
+  DateTime now = rtc.now();
+  sprintf(t1, "%02d-%02d-%02d %02d:%02d:%02d",  now.year(), now.month(), now.day(),   now.hour(), now.minute(), now.second());  //Extrae del rtc hora y fecha
+  sprintf(t2, "%02d-%02d-%02d ",  now.year(), now.month(), now.day());  //Extrae del rtc hora y fecha
+  sprintf(t3, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
+  
+  DynamicJsonDocument JSONencoder(1024);
+  JSONencoder["date"] = t1;//Manda al JSON la variable t
+  JSONencoder["serialNumber"] = meterID;//numero serial de el medidor
+  delay(50);
+  JsonObject measures = JSONencoder.createNestedObject("measures");//Anidamos al JSON la variable measures
+  float voltage = sdm.readVal(SDM630_VOLTAGE_AVERAGE);
+  measures["Readings: Volts Average"] = String(voltage);
+  delay(50);
+  float voltage_a = sdm.readVal(SDM630_VOLTAGE1);
+  measures["Readings: Volts A-N"] = String(voltage_a);
+  delay(50);
+  float voltage_b = sdm.readVal(SDM630_VOLTAGE2);
+  measures["Readings: Volts B-N"] = String(voltage_b);
+  delay(50);
+  float voltage_c = sdm.readVal(SDM630_VOLTAGE3);
+  measures["Readings: Volts C-N"] = String(voltage_c);
+  delay(50);
+  float current = sdm.readVal(SDM630_CURRENT_AVERAGE);
+  measures["Readings: I A"] = String(current);
+  delay(50);
+  float current_a = sdm.readVal(SDM630_CURRENT1);
+  measures["Readings: I A"] = String(current_a);
+  delay(50);
+  float current_b = sdm.readVal(SDM630_CURRENT2);
+  measures["Readings: I B"] = String(current_b);
+  delay(50);
+  float current_c = sdm.readVal(SDM630_CURRENT3);
+  measures["Readings: I C"] = String(current_c);
+  delay(50);
+  float frequency = sdm.readVal(SDM630_FREQUENCY);
+  measures["Readings: Frequency"] = String(frequency);
+  delay(50);
+  float power = sdm.readVal(SDM630_POWERTOTAL);
+  measures["Readings: Watts Total"] = String(power);
+  delay(50);  
+  float power_a = sdm.readVal(SDM630_POWER1);
+  measures["Readings: Watts A"] = String(power_a);
+  delay(50);
+  float power_b = sdm.readVal(SDM630_POWER2);
+  measures["Readings: Watts B"] = String(power_b);
+  delay(50);
+  float power_c = sdm.readVal(SDM630_POWER3);
+  measures["Readings: Watts C"] = String(power_c);
+  delay(50);
+  float received = sdm.readVal(SDM630_IMPORT_ACTIVE_ENERGY)*1000; 
+  measures["Scaled Energy: Wh received"] = String(received); 
+  delay(50);
+  float received_a = sdm.readVal(SDM630_IMPORT1)*1000; 
+  measures["Scaled Energy: Wh received A"] = String(received_a); 
+  delay(50);
+  float received_b = sdm.readVal(SDM630_IMPORT2)*1000; 
+  measures["Scaled Energy: Wh received B"] = String(received_b); 
+  delay(50);
+  float received_c = sdm.readVal(SDM630_IMPORT3)*1000; 
+  measures["Scaled Energy: Wh received C"] = String(received_c); 
+  delay(50);
+  float delivered = sdm.readVal(SDM630_EXPORT_ACTIVE_ENERGY)*1000;
+  measures["Scaled Energy: Wh delivered"] = String(delivered);
+  delay(50);
+  float delivered_a = sdm.readVal(SDM630_EXPORT1)*1000;
+  measures["Scaled Energy: Wh delivered A"] = String(delivered_a);
+  delay(50);
+  float delivered_b = sdm.readVal(SDM630_EXPORT2)*1000;
+  measures["Scaled Energy: Wh delivered B"] = String(delivered_b);
+  delay(50);
+  float delivered_c = sdm.readVal(SDM630_EXPORT3)*1000;
+  measures["Scaled Energy: Wh delivered C"] = String(delivered_c);
+  delay(50);
+  float wh_net = (received-delivered);
+  float wh_net_a = (received_a - delivered_a);
+  float wh_net_b = (received_b - delivered_b);
+  float wh_net_c = (received_c - delivered_c);
+  measures["Scaled Energy: Wh Total"] = String(wh_net) ;
+  measures["Scaled Energy: Wh Total A"] = String(wh_net_a);
+  measures["Scaled Energy: Wh Total B"] = String(wh_net_b);
+  measures["Scaled Energy: Wh Total C"] = String(wh_net_c);
+  delay(50);  
+  float wh_total = (received + delivered);
+  measures["Scaled Energy: Wh Net"] = String(wh_total);
+  delay(50);
+  float wh_total_a = sdm.readVal(SDM630_TOTAL_ENERGY1)*1000;
+  measures["Scaled Energy: Wh Net A"] = String(wh_total_a);
+  delay(50);
+  float wh_total_b = sdm.readVal(SDM630_TOTAL_ENERGY2)*1000;
+  measures["Scaled Energy: Wh Net A"] = String(wh_total_b);
+  delay(50);
+  float wh_total_c = sdm.readVal(SDM630_TOTAL_ENERGY3)*1000;
+  measures["Scaled Energy: Wh Net C"] = String(wh_total_c);
+  delay(50);
+  char JSONmessageBuffer[1000];
+  serializeJson(JSONencoder, JSONmessageBuffer);
+if (( now.day() == dia_nextEEPROM || diaEEPROM ==0 ||(now.day()>dia_nextEEPROM & mes_nextEEPROM == now.month())) & band_day==1) 
+  {
+    if (diaEEPROM == 0) {
+       EEPROM.write(2,2);
+       diaEEPROM =2;
+       EEPROM.commit();
+    }
+
+  dia_next= now.day() + 7;
+  mes_next = now.month();
+  
+//Estos if es para el no superen el valor de 29, 30 o 31 dependiendo el mes 
+  if ((now.month()==1 || now.month()==3 || now.month()==5 || now.month()==7 || now.month()==8 || now.month()==10 || now.month()==12) & (dia_next>31)){
+    dia_next= dia_next -31;
+    mes_next = now.month() + 1;
+  }
+  else   if ((dia_next>30)&(now.month()==4 || now.month()==6 || now.month()==9 || now.month()==11)){
+    dia_next= dia_next -30;
+    mes_next = now.month() + 1;
+  }
+  else   if ((dia_next>29)&(now.month()==2 & ((now.year()==2020 || now.year()==2024 || now.year()==2028 || now.year()==2032 || now.year()==2036)))){
+    dia_next= dia_next -29;
+    mes_next = now.month() + 1;
+   }
+  else if ((dia_next>28)&(now.month()==2)){
+    dia_next= dia_next -28;
+    mes_next = now.month() + 1;
+    }
+  
+  //Guardando variables EEPROM
+  EEPROM.write(6,dia_next);
+  EEPROM.write(0,mes_next);
+  EEPROM.write(3,now.day());
+  EEPROM.write(4,now.year()-2000);//si toma mas de tres dijitos me escribe otra cosa
+  EEPROM.write(5,now.month()); 
+  EEPROM.write(7,0);//Variable linea 1
+  dia_EEPROM = now.day();
+  anho_EEPROM = now.year()-2000;
+  mes_EEPROM = now.month(); 
+  dia_nextEEPROM = dia_next;
+  mes_nextEEPROM = mes_next;
+  linea1_EEPROM = 0;
+  EEPROM.commit();
+  band_day=2;
+  }
+  else   if (( now.day() != dia_nextEEPROM & diaEEPROM !=0) & band_day==2)
+  {
+  band_day=1;
+  }
+ 
+
+  if ((now.minute()==00 || now.minute()==15 || now.minute()==30 || now.minute()==45) & band==1) {
+//if ((now.minute()==00 || now.minute()==05 || now.minute()==10 || now.minute()==15 || now.minute()==20 || now.minute()==25 || now.minute()==30 || now.minute()==35 || now.minute()==40 || now.minute()==45 || now.minute()==50 || now.minute()==55) & band==1) {
+   //Variables que seran guardadas en el logging en formato csv
+  String linea1=("date,serialNumber,Voltaje promedio(V),Voltale A-N(V),Voltale B-N(V),Voltale C-N(V),Corriente promedio(A),Corriente A(A),Corriente B(A),Corriente C(A),Frecuencia (Hz),Potencia total(W),Potencia A(W),Potencia B(W),Potencia C(W),Energia Neta total(Wh),Energia Neta A(Wh),Energia Neta B(Wh),Energia Neta C(Wh),Energia Importada total(Wh),Energia Importada A(Wh),Energia Importada B(Wh),Energia Importada C(Wh),Energia Exportada total(Wh),Energia Exportada A(Wh),Energia Exportada B(Wh),Energia Exportada C(Wh),Energia Total (Wh),Energia Total A(Wh),Energia Total B(Wh),Energia Total (Wh) C");    
+  delay(50);
+  String measurescsv1 = (String(t1) + "," + String(meterID) + ","+ String(voltage) + "," +  String(voltage_a) + ","); 
+  delay(50);
+  String measurescsv2 = (String(voltage_b) + "," + String(voltage_c) + "," + String(current) + "," + String(current_a) + ","); 
+  delay(50);
+  String measurescsv3 = (String(current_b) + "," + String(current_c) + "," +  String(frequency) + "," +  String(power) + ",");
+  delay(50); 
+  String measurescsv4 = (String(power_a) + "," + String(power_b) + "," + String(power_c) + "," +  String(wh_total) + ",");
+  delay(50);
+  String measurescsv5 = (String(wh_total_a) + "," + String(wh_total_b) + "," + String(wh_total_c) + "," + String(received) + ",");
+  delay(50);
+  String measurescsv6 = (String(received_a) + "," + String(received_b) +  "," + String(received_c) + "," +  String(delivered) + ",");
+  delay(50);
+  String measurescsv7 = (String(delivered_a) + "," +  String(delivered_b) + "," +  String(delivered_c) + "," + String(wh_net) + ",");
+  delay(50);
+  String measurescsv8 = (String(wh_net_a) + "," + String(wh_net_b) + "," + String(wh_net_c));
+  delay(50);
+  String measurescsv = (measurescsv1 + measurescsv2 + measurescsv3 + measurescsv4 + measurescsv5 + measurescsv6 + measurescsv7 + measurescsv8);
+  Serial.println(measurescsv);
+  sprintf(t2_EEPROM, "%02d-%02d-20%02d.csv",dia_EEPROM,mes_EEPROM,anho_EEPROM);
+
+  myFile = SD.open(t2_EEPROM, FILE_WRITE);
+  // if the file opened okay, write to it:
+  if (myFile) {
+    if (linea1_EEPROM ==0) {
+       EEPROM.write(7,1);     //Solo inicialmente valdra cero o cuando inicie una nueva medicion cada 7 dias
+       linea1_EEPROM = 1;
+       myFile.println(linea1); //Imprime en el csv la linea 1
+       EEPROM.commit();
+    }
+  myFile.println(measurescsv); //imprime en el csv los parametros medidos
+  // close the file:
+  myFile.close();
+  Serial.println("done.");
+  } else {
+  // if the file didn't open, print an error:
+  Serial.println("error opening .csv");
+  }
+
+//Variable JSON la cual se mandara al mqtt cada 15 minuntos  
+  DynamicJsonDocument var_login(1500);
+  var_login["date"] = (String(t2) + String(t3));
+  var_login["serialNumber"] = String(meterID);
+  JsonArray feeds = var_login.createNestedArray("measures");
+ /* JsonObject feed1=feeds.createNestedObject();
+  feed1["tipo"] = "Voltaje";
+  feed1["fase"] = "Total";
+  feed1["lectura"] = voltage;
+  feed1["unidad"] = "V";*/
+  JsonObject feed1_a=feeds.createNestedObject();
+  feed1_a["tipo"] = "Voltaje";
+  feed1_a["fase"] = "A";
+  feed1_a["lectura"] = String(voltage_a);
+  //feed1_a["unidad"] = "V";
+  JsonObject feed1_b=feeds.createNestedObject();
+  feed1_b["tipo"] = "Voltaje";
+  feed1_b["fase"] = "B";
+  feed1_b["lectura"] = String(voltage_b);
+  //feed1_b["unidad"] = "V";
+  JsonObject feed1_c=feeds.createNestedObject();
+  feed1_c["tipo"] = "Voltaje";
+  feed1_c["fase"] = "C";
+  feed1_c["lectura"] = String(voltage_c);
+  //feed1_c["unidad"] = "V";
+ /* JsonObject feed2=feeds.createNestedObject();
+  feed2["tipo"] = "Corriente";
+  feed2["fase"] = "Total";
+  feed2["lectura"] = current;
+  feed2["unidad"] = "A";*/
+  JsonObject feed2_a=feeds.createNestedObject();
+  feed2_a["tipo"] = "Corriente";
+  feed2_a["fase"] = "A";
+  feed2_a["lectura"] = String(current_a);
+  //feed2_a["unidad"] = "A";
+  JsonObject feed2_b=feeds.createNestedObject();
+  feed2_b["tipo"] = "Corriente";
+  feed2_b["fase"] = "B";
+  feed2_b["lectura"] = String(current_b);
+  //feed2_b["unidad"] = "A";
+  JsonObject feed2_c=feeds.createNestedObject();
+  feed2_c["tipo"] = "Corriente";
+  feed2_c["fase"] = "C";
+  feed2_c["lectura"] = String(current_c);
+  //feed2_c["unidad"] = "A";
+  JsonObject feed3=feeds.createNestedObject();
+  feed3["tipo"] = "Frecuencia";
+  feed3["fase"] = "Total";
+  feed3["lectura"] = String(frequency);
+  //feed3["unidad"] = "Hz";
+ /* JsonObject feed4=feeds.createNestedObject();
+  feed4["tipo"] = "Potencia";
+  feed4["fase"] = "Total";
+  feed4["lectura"] = power;
+  feed4["unidad"] = "W";*/
+  JsonObject feed4_a=feeds.createNestedObject();
+  feed4_a["tipo"] = "Potencia";
+  feed4_a["fase"] = "A";
+  feed4_a["lectura"] = String(power_a);
+  //feed4_a["unidad"] = "W";
+  JsonObject feed4_b=feeds.createNestedObject();
+  feed4_b["tipo"] = "Potencia";
+  feed4_b["fase"] = "B";
+  feed4_b["lectura"] = String(power_b);
+  //feed4_b["unidad"] = "W";
+  JsonObject feed4_c=feeds.createNestedObject();
+  feed4_c["tipo"] = "Potencia";
+  feed4_c["fase"] = "C";
+  feed4_c["lectura"] = String(power_c);
+  //feed4_c["unidad"] = "W";
+  JsonObject feed5=feeds.createNestedObject();
+  feed5["tipo"] = "Energy: Wh Total";
+  feed5["fase"] = "Total";
+  feed5["lectura"] = String(wh_total);
+  //feed5["unidad"] = "Wh";
+  JsonObject feed5_a=feeds.createNestedObject();
+  feed5_a["tipo"] = "Energy: Wh Total";
+  feed5_a["fase"] = "A";
+  feed5_a["lectura"] = String(wh_total_a);
+  //feed5_a["unidad"] = "Wh";
+  JsonObject feed5_b=feeds.createNestedObject();
+  feed5_b["tipo"] = "Energy: Wh Total";
+  feed5_b["fase"] = "B";
+  feed5_b["lectura"] = String(wh_total_b);
+  //feed5_b["unidad"] = "Wh";
+  JsonObject feed5_c=feeds.createNestedObject();
+  feed5_c["tipo"] = "Energy: Wh Total";
+  feed5_c["fase"] = "C";
+  feed5_c["lectura"] = String(wh_total_c);
+  //feed5_c["unidad"] = "Wh";
+  /*JsonObject feed6=feeds.createNestedObject();
+  feed6["tipo"] = "Energy: Wh received";
+  feed6["fase"] = "Total";
+  feed6["lectura"] = received;
+  feed6["unidad"] = "Wh";*/
+  JsonObject feed6_a=feeds.createNestedObject();
+  feed6_a["tipo"] = "Energy: Wh received";
+  feed6_a["fase"] = "A";
+  feed6_a["lectura"] = String(received_a);
+  //feed6_a["unidad"] = "Wh";
+  JsonObject feed6_b=feeds.createNestedObject();
+  feed6_b["tipo"] = "Energy: Wh received";
+  feed6_b["fase"] = "B";
+  feed6_b["lectura"] = String(received_b);
+  //feed6_b["unidad"] = "Wh";
+  JsonObject feed6_c=feeds.createNestedObject();
+  feed6_c["tipo"] = "Energy: Wh received";
+  feed6_c["fase"] = "C";
+  feed6_c["lectura"] = String(received_c);
+  //feed6_c["unidad"] = "Wh";
+  /*JsonObject feed7=feeds.createNestedObject();
+  feed7["tipo"] = "Energy: Wh delivered";
+  feed7["fase"] = "Total";
+  feed7["lectura"] = delivered;
+  feed7["unidad"] = "Wh";*/
+  JsonObject feed7_a=feeds.createNestedObject();
+  feed7_a["tipo"] = "Energy: Wh delivered";
+  feed7_a["fase"] = "A";
+  feed7_a["lectura"] = String(delivered_a);
+  //feed7_a["unidad"] = "Wh"; 
+  JsonObject feed7_b=feeds.createNestedObject();
+  feed7_b["tipo"] = "Energy: Wh delivered";
+  feed7_b["fase"] = "B";
+  feed7_b["lectura"] = String(delivered_b);
+  //feed7_b["unidad"] = "Wh";
+  JsonObject feed7_c=feeds.createNestedObject();
+  feed7_c["tipo"] = "Energy: Wh delivered";
+  feed7_c["fase"] = "C";
+  feed7_c["lectura"] = String(delivered_c);
+  //feed7_c["unidad"] = "Wh"; 
+  
+  char JSONmessageBuffer2[1300];
+  serializeJson(var_login, JSONmessageBuffer2);
+  Serial.println(JSONmessageBuffer2);
+  char topic2[20];
+  sprintf(topic2,"denmark/log/%s",meterID);
+  Serial.println(topic2);  
+  if (client.publish(topic2, JSONmessageBuffer2) == true) { //update when server is changed
+ 
+  Serial.println("Success sending message");
+  } else {
+  Serial.println("Error sending message");
+  }
+  band = 2;//Me limita que solo la primera medicion tomada en los minutos 0, 15, 30, 45 sean mandados al mqtt y al logging
+  
+}
+  else if ((now.minute()!=00 & now.minute()!=15 & now.minute()!=30 & now.minute()!=45) & band==2)
+  //else   if ((now.minute()!=00 & now.minute()!=05 & now.minute()!=10 & now.minute()!=15 & now.minute()!=20 & now.minute()!=25 & now.minute()!=30 & now.minute()!=35 & now.minute()!=40 & now.minute()!=45 & now.minute()!=50 & now.minute()!=55) & band==2)
+  {
+  band=1; 
+  }
+  char topic1[10];
+  sprintf(topic1,"denmark/tr/%s",meterID);
+  Serial.println(topic1);
+  if (client.publish(topic1, JSONmessageBuffer) == true) { //update when server is changed
+  Serial.println("Success sending message");
+  } else {
+  Serial.println("Error sending message");
+  }
+  Serial.print("JSON: ");
+  Serial.println(JSONmessageBuffer);
+  
+}
+
+
+
+//Reconeccion al servidor de mqtt 
+void reconnect() {
+  // Loop until we're reconnected
+ //  while (!client.connected()) { //Creo que si comentareo este while en lugar de quedarse probando que conecte solo lo haria una vez
+    Serial.print("Attempting MQTT connection...");
+    // Create a random client ID
+    String clientId = "ESP8266Client";
+ 
+    // Attempt to connect
+    if (client.connect(clientId.c_str())) {
+      Serial.println("connected");// Mostrar mensaje de exito a MQTT      
+      // Once connected, publish an announcement...
+      StaticJsonDocument<300> JSONEncoder;
+      JSONEncoder['status'] = "true";
+      char JSONmessageBuffer[100];
+      serializeJson(JSONEncoder, JSONmessageBuffer);
+      //client.publish("testTopic", JSONmessageBuffer);
+      //client.publish("testTopic2", JSONmessageBuffer);
+      // ... and resubscribe
+      //client.subscribe("inTopic");
+    } else {
+      Serial.print("Conexión MQTT Falló, rc=");// Mostrar mensaje de fallo a MQTT   
+      Serial.print(client.state());
+      Serial.println(" try again in 5 seconds");
+      // Wait 5 seconds before retrying
+      delay(5000);
+    }
+  //}
+}
+
+
+void setup() {
+  // put your setup code here, to run once:
+  
+  Serial.begin(115200);  
+  //initialize serial
+  EEPROM.begin(512);
+  Wire.begin();
+  rtc.begin();
+  if (! rtc.begin()) {
+    Serial.println("Couldn't find RTC");
+    while (1);
+  }
+
+  if (rtc.lostPower()) {
+    Serial.println("RTC lost power, lets set the time!");
+    // following line sets the RTC to the date & time this sketch was compiled
+    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
+  }
+
+  sdm.begin(); 
+  delay(10);
+
+  WiFiManagerParameter custom_output("Meter ID", "Meter ID", meterID, 10);
+    
+  //WiFiManager
+  //Local intialization. Once its business is done, there is no need to keep it around
+  WiFiManager wifiManager;
+
+  //reset settings - for testing
+  //wifiManager.resetSettings();
+
+/*
+  //start-block2
+  IPAddress _ip = IPAddress(192, 168, 98, 164);
+  IPAddress _gw = IPAddress(192, 168, 98, 254);
+  IPAddress _sn = IPAddress(255, 255, 255, 0);
+  //end-block2
+  
+  wifiManager.setSTAStaticIPConfig(_ip, _gw, _sn);
+*/
+ wifiManager.addParameter(&custom_output);
+  //tries to connect to last known settings
+  //if it does not connect it starts an access point with the specified name
+  //here  "AutoConnectAP" with password "password"
+  //and goes into a blocking loop awaiting configuration
+
+
+  if (!wifiManager.autoConnect("DENMARK", "123456789")) {
+    Serial.println("failed to connect, we should reset as see if it connects");
+    delay(3000);
+    ESP.reset();
+    delay(5000);
+  }
+
+  //if you get here you have connected to the WiFi
+  Serial.println("connected :)");
+
+//Guarda la variable IDMeter
+  if (EEPROM.read(8) == 0) {
+    strcpy(meterID, custom_output.getValue());//extrae el valor ingresado en la app
+    EEPROM.write(8,1); //band para que no cambie el valor de IDMeter
+    EEPROM.put(9,meterID); //guarda el valor de IDMeter y no se puede cambiar a menos que se borren las variable eeprom
+    EEPROM.commit(); 
+  }
+
+  Serial.println("local ip");
+  Serial.println(WiFi.localIP());
+    
+  delay(1000);
+  Serial.println("Creando cliente MQTT...");
+  client.setServer("denmark.inverlec.solar", 31010);
+  client.setCallback(callback);
+
+  if (!SD.begin(10)) {
+    Serial.println("initialization SD card failed!");
+    return;
+  }
+  else {
+  Serial.println("initialization  SD card done.");
+  }
+
+///Set variables en EEPROM
+  EEPROM.get(9,meterID);
+  diaEEPROM = EEPROM.read(2);// define que ya fue corrida al menos una vez el programa
+  dia_EEPROM = EEPROM.read(3);//variable de dia de la carpeta actual csv
+  anho_EEPROM = EEPROM.read(4);//variable de año de la carpeta actual csv  
+  mes_EEPROM = EEPROM.read(5);  //variable de mes de la carpeta actual csv  
+  dia_nextEEPROM = EEPROM.read(6);//variable la cual contiene el siguiente dia en cual se creara la carpeta csv  
+  linea1_EEPROM = EEPROM.read(7);//Esta variable es para limitar que el encabezado se mande solo una vez al crar el csv 
+  mes_nextEEPROM = EEPROM.read(0);//variable la cual contiene el siguiente mes en cual se creara la carpeta csv
+  /*Serial.println(mes_nextEEPROM);
+  Serial.println(meterID);
+  Serial.println(dia_nextEEPROM);
+  Serial.println(anho_EEPROM);
+  Serial.println(mes_EEPROM);
+*/
+}
+
+void loop() {
+  
+  //Loop reconexion a Mqtt
+  {
+  if (!client.connected()) {
+  reconnect();
+  }
+  client.loop();
+  }
+   
+  denmark();
+  delay(7920);
+}

+ 395 - 0
DENMARK_V1_SDM120/DENMARK_V1_SDM120.ino

@@ -0,0 +1,395 @@
+#include <FS.h>
+#include <ESP8266WiFi.h>
+#include <WiFiClient.h>
+#include <PubSubClient.h>
+#include <SDM.h>
+#include <SoftwareSerial.h>
+#include <ArduinoJson.h>
+#include <RTClib.h>
+#include <Wire.h>
+#include <SPI.h>
+#include <SD.h>
+#include <EEPROM.h>
+#include <WiFiManager.h> 
+#include <ESP8266WebServer.h>
+#include <DNSServer.h>
+
+File myFile;
+#define ESP8266
+int band =1;
+int band_day =1;
+int dia_next;
+int mes_next;
+int dia_nextEEPROM;
+int mes_nextEEPROM;
+char t2_EEPROM[15];
+int anho_EEPROM;
+int dia_EEPROM;
+int mes_EEPROM;
+int diaEEPROM;
+int linea1_EEPROM;
+RTC_DS3231 rtc;
+
+char meterID[10];
+
+//Configuracion contador
+SoftwareSerial swSerSDM;
+SDM sdm(swSerSDM, 2400, 16, SWSERIAL_8N1, 0, 2);
+
+
+// Servidor MQTT
+char   SERVER[50]   = "192.168.100.5"; // denmark.inverlec.solar
+int    SERVERPORT   = 31010;
+
+
+WiFiClient espClient;
+PubSubClient client(espClient);
+
+// Callback al recibir mensaje por MQTT
+void callback(char* topic, byte* payload, unsigned int length) {
+  Serial.print("Message arrived [");
+  Serial.print(topic);
+  Serial.print("] ");
+  for (int i = 0; i < length; i++) {
+    Serial.print((char)payload[i]);
+  }
+  Serial.println();
+}
+
+//funcion para mandar a mqqt la lectura cada 10s, 15 min y guardar en memoria SD
+int denmark() {
+
+  char bufout[10]; 
+  //Variable JSON que se mandara a mqtt aproximadamente cada 10 segundos 
+  StaticJsonDocument<500> JSONencoder;//Se define variable JSON
+  char t1[15];
+  DateTime now = rtc.now();
+  sprintf(t1, "%02d-%02d-%02d %02d:%02d:%02d",  now.year(), now.month(), now.day(),   now.hour(), now.minute(), now.second());  //Extrae del rtc hora y fecha
+  JSONencoder["date"] = t1;//Manda al JSON la variable t
+  JSONencoder["serialNumber"] = meterID;//numero serial de el medidor
+  delay(50);
+  JsonObject measures = JSONencoder.createNestedObject("measures");//Anidamos al JSON la variable measures
+  float voltage = sdm.readVal(SDM120C_VOLTAGE);
+  measures["Readings: Volts A-N"] = voltage;
+  delay(50);
+  float current = sdm.readVal(SDM120C_CURRENT);
+  measures["Readings: I A"] = current;
+  delay(50);
+  float frequency = sdm.readVal(SDM120C_FREQUENCY);
+  measures["Readings: Frequency"] = frequency;
+  delay(50);  
+  float power = sdm.readVal(SDM120C_POWER);
+  measures["Readings: Watts A"] = power;
+  delay(50);
+  float wh_total = sdm.readVal(SDM120C_TOTAL_ACTIVE_ENERGY)*1000;
+  measures["Scaled Energy: Wh Net"] = wh_total;
+  delay(50);
+  float received = sdm.readVal(SDM120CT_IMPORT_ACTIVE_ENERGY)*1000; 
+  measures["Scaled Energy: Wh received"] = received; 
+  delay(50);
+  float delivered = sdm.readVal(SDM120CT_EXPORT_ACTIVE_ENERGY)*1000;
+  measures["Scaled Energy: Wh delivered"] = delivered;
+  delay(50);
+  float wh_net = (received-delivered);
+  measures["Scaled Energy: Wh Total"] = wh_net ;
+  delay(50);  
+  
+  char JSONmessageBuffer[300];
+  serializeJson(JSONencoder, JSONmessageBuffer);
+  
+if (( now.day() == dia_nextEEPROM || diaEEPROM ==0 ||(now.day()>dia_nextEEPROM & mes_nextEEPROM == now.month())) & band_day==1) 
+  {
+    if (diaEEPROM == 0) {
+       EEPROM.write(2,2);
+       diaEEPROM =2;
+       EEPROM.commit();
+    }
+
+  dia_next= now.day() + 7;
+  mes_next = now.month();
+  
+//Estos if es para el no superen el valor de 29, 30 o 31 dependiendo el mes 
+  if ((now.month()==1 || now.month()==3 || now.month()==5 || now.month()==7 || now.month()==8 || now.month()==10 || now.month()==12) & (dia_next>31)){
+    dia_next= dia_next -31;
+    mes_next = now.month() + 1;
+  }
+  else   if ((dia_next>30)&(now.month()==4 || now.month()==6 || now.month()==9 || now.month()==11)){
+    dia_next= dia_next -30;
+    mes_next = now.month() + 1;
+  }
+  else   if ((dia_next>29)&(now.month()==2 & ((now.year()==2020 || now.year()==2024 || now.year()==2028 || now.year()==2032 || now.year()==2036)))){
+    dia_next= dia_next -29;
+    mes_next = now.month() + 1;
+   }
+  else if ((dia_next>28)&(now.month()==2)){
+    dia_next= dia_next -28;
+    mes_next = now.month() + 1;
+    }
+  
+  //Guardando variables EEPROM
+  EEPROM.write(6,dia_next);
+  EEPROM.write(0,mes_next);
+  EEPROM.write(3,now.day());
+  EEPROM.write(4,now.year()-2000);//si toma mas de tres dijitos me escribe otra cosa
+  EEPROM.write(5,now.month()); 
+  EEPROM.write(7,0);//Variable linea 1
+  dia_EEPROM = now.day();
+  anho_EEPROM = now.year()-2000;
+  mes_EEPROM = now.month(); 
+  dia_nextEEPROM = dia_next;
+  mes_nextEEPROM = mes_next;
+  linea1_EEPROM = 0;
+  EEPROM.commit();
+  band_day=2;
+  }
+  else   if (( now.day() != dia_nextEEPROM & diaEEPROM !=0) & band_day==2)
+  {
+  band_day=1;
+  }
+ 
+ 
+//  if ((now.minute()==00 || now.minute()==15 || now.minute()==30 || now.minute()==45) & band==1)
+if ((now.minute()==00 || now.minute()==05 || now.minute()==10 || now.minute()==15 || now.minute()==20 || now.minute()==25 || now.minute()==30 || now.minute()==35 || now.minute()==40 || now.minute()==45 || now.minute()==50 || now.minute()==55) & band==1) {
+   //Variables que seran guardadas en el logging en formato csv
+  String linea1=("date,serialNumber,Voltaje (V),Corriente (A),Frecuencia (Hz),Potencia (W),Energia Neta (Wh),Energia Importada (Wh),Energia Exportada (Wh),Energia Total (Wh)");    
+  String measurescsv = String(t1) + "," + String(meterID)+","+String(voltage) + "," +  String(current) + "," +  String(frequency) + "," +  String(power) + "," +  String(wh_total) + "," +  String(received) + "," +  String(delivered) + "," +  String(wh_net);
+  Serial.println(measurescsv);
+
+  sprintf(t2_EEPROM, "%02d-%02d-20%02d.csv",dia_EEPROM,mes_EEPROM,anho_EEPROM);
+  myFile = SD.open(t2_EEPROM, FILE_WRITE);
+  // if the file opened okay, write to it:
+  if (myFile) {
+    if (linea1_EEPROM ==0) {
+       EEPROM.write(7,1);     //Solo inicialmente valdra cero o cuando inicie una nueva medicion cada 7 dias
+       linea1_EEPROM = 1;
+       myFile.println(linea1); //Imprime en el csv la linea 1
+       EEPROM.commit();
+    }
+  myFile.println(measurescsv); //imprime en el csv los parametros medidos
+  // close the file:
+  myFile.close();
+  Serial.println("done.");
+  } else {
+  // if the file didn't open, print an error:
+  Serial.println("error opening .csv");
+  }
+
+//Variable JSON la cual se mandara al mqtt cada 15 minuntos  
+  StaticJsonDocument<800> var_login;//Se define variable JSON
+  var_login["date"] = t1;
+  var_login["serialNumber"] = meterID;
+  JsonArray feeds = var_login.createNestedArray("measures");
+  JsonObject feed1=feeds.createNestedObject();
+  feed1["tipo"] = "Voltaje";
+  feed1["fase"] = "A";
+  feed1["lectura"] = voltage;
+  feed1["unidad"] = "V";
+  JsonObject feed2=feeds.createNestedObject();
+  feed2["tipo"] = "Corriente";
+  feed2["fase"] = "A";
+  feed2["lectura"] = current;
+  feed2["unidad"] = "A";
+  JsonObject feed3=feeds.createNestedObject();
+  feed3["tipo"] = "Frecuencia";
+  feed3["fase"] = "A";
+  feed3["lectura"] = frequency;
+  feed3["unidad"] = "Hz";
+  JsonObject feed4=feeds.createNestedObject();
+  feed4["tipo"] = "Potencia";
+  feed4["fase"] = "A";
+  feed4["lectura"] = power;
+  feed4["unidad"] = "W";
+  JsonObject feed5=feeds.createNestedObject();
+  feed5["tipo"] = "Energy: Wh Total";
+  feed5["fase"] = "A";
+  feed5["lectura"] = wh_total;
+  feed5["unidad"] = "Wh";
+  JsonObject feed6=feeds.createNestedObject();
+  feed6["tipo"] = "Energy: Wh received";
+  feed6["fase"] = "A";
+  feed6["lectura"] = received;
+  feed6["unidad"] = "Wh";
+  JsonObject feed7=feeds.createNestedObject();
+  feed7["tipo"] = "Energy: Wh delivered";
+  feed7["fase"] = "A";
+  feed7["lectura"] = delivered;
+  feed7["unidad"] = "Wh";  
+  JsonObject feed8=feeds.createNestedObject();
+  feed8["tipo"] = "Energy: Wh Net";
+  feed8["fase"] = "A";
+  feed8["lectura"] = wh_net;
+  feed8["unidad"] = "Wh";   
+  char JSONmessageBuffer2[800];
+  serializeJson(var_login, JSONmessageBuffer2);
+  Serial.println(JSONmessageBuffer2);
+ 
+  char topic2[20];
+  sprintf(topic2,"denmark/log/%s",meterID);
+  Serial.println(topic2);  
+  if (client.publish(topic2, JSONmessageBuffer2) == true) { //update when server is changed
+ 
+  Serial.println("Success sending message");
+  } else {
+  Serial.println("Error sending message");
+  }
+  band = 2;//Me limita que solo la primera medicion tomada en los minutos 0, 15, 30, 45 sean mandados al mqtt y al logging
+  }
+//  else if ((now.minute()!=00 & now.minute()!=15 & now.minute()!=30 & now.minute()!=45) & band==2)
+  else   if ((now.minute()!=00 & now.minute()!=05 & now.minute()!=10 & now.minute()!=15 & now.minute()!=20 & now.minute()!=25 & now.minute()!=30 & now.minute()!=35 & now.minute()!=40 & now.minute()!=45 & now.minute()!=50 & now.minute()!=55) & band==2)
+  {
+  band=1; 
+  }
+  char topic1[10];
+  sprintf(topic1,"denmark/tr/%s",meterID);
+  Serial.println(topic1);
+  if (client.publish(topic1, JSONmessageBuffer) == true) { //update when server is changed
+  Serial.println("Success sending message");
+  } else {
+  Serial.println("Error sending message");
+  }
+  Serial.print("JSON: ");
+  Serial.println(JSONmessageBuffer);
+}
+
+
+
+//Reconeccion al servidor de mqtt 
+void reconnect() {
+  // Loop until we're reconnected
+ //  while (!client.connected()) { //Creo que si comentareo este while en lugar de quedarse probando que conecte solo lo haria una vez
+    Serial.print("Attempting MQTT connection...");
+    // Create a random client ID
+    String clientId = "ESP8266Client";
+ 
+    // Attempt to connect
+    if (client.connect(clientId.c_str())) {
+      Serial.println("connected");// Mostrar mensaje de exito a MQTT      
+      // Once connected, publish an announcement...
+      StaticJsonDocument<300> JSONEncoder;
+      JSONEncoder['status'] = "true";
+      char JSONmessageBuffer[100];
+      serializeJson(JSONEncoder, JSONmessageBuffer);
+      //client.publish("testTopic", JSONmessageBuffer);
+      //client.publish("testTopic2", JSONmessageBuffer);
+      // ... and resubscribe
+      //client.subscribe("inTopic");
+    } else {
+      Serial.print("Conexión MQTT Falló, rc=");// Mostrar mensaje de fallo a MQTT   
+      Serial.print(client.state());
+      Serial.println(" try again in 5 seconds");
+      // Wait 5 seconds before retrying
+      delay(5000);
+    }
+  //}
+}
+
+
+void setup() {
+  // put your setup code here, to run once:
+  
+  Serial.begin(115200);  
+  //initialize serial
+  EEPROM.begin(512);
+  Wire.begin();
+  rtc.begin();
+  if (! rtc.begin()) {
+    Serial.println("Couldn't find RTC");
+    while (1);
+  }
+
+  if (rtc.lostPower()) {
+    Serial.println("RTC lost power, lets set the time!");
+    // following line sets the RTC to the date & time this sketch was compiled
+    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
+  }
+
+  sdm.begin(); 
+  delay(10);
+
+  WiFiManagerParameter custom_output("Meter ID", "Meter ID", meterID, 10);
+    
+  //WiFiManager
+  //Local intialization. Once its business is done, there is no need to keep it around
+  WiFiManager wifiManager;
+
+  //reset settings - for testing
+  //wifiManager.resetSettings();
+
+
+  //start-block2
+  IPAddress _ip = IPAddress(192, 168, 98, 164);
+  IPAddress _gw = IPAddress(192, 168, 98, 254);
+  IPAddress _sn = IPAddress(255, 255, 255, 0);
+  //end-block2
+  
+  wifiManager.setSTAStaticIPConfig(_ip, _gw, _sn);
+
+ wifiManager.addParameter(&custom_output);
+  //tries to connect to last known settings
+  //if it does not connect it starts an access point with the specified name
+  //here  "AutoConnectAP" with password "password"
+  //and goes into a blocking loop awaiting configuration
+  if (!wifiManager.autoConnect("DENMARK", "123456789")) {
+    Serial.println("failed to connect, we should reset as see if it connects");
+    delay(3000);
+    ESP.reset();
+    delay(5000);
+  }
+
+  //if you get here you have connected to the WiFi
+  Serial.println("connected :)");
+
+//Guarda la variable IDMeter
+  if (EEPROM.read(8) == 0) {
+    strcpy(meterID, custom_output.getValue());//extrae el valor ingresado en la app
+    EEPROM.write(8,1); //band para que no cambie el valor de IDMeter
+    EEPROM.put(9,meterID); //guarda el valor de IDMeter y no se puede cambiar a menos que se borren las variable eeprom
+    EEPROM.commit(); 
+  }
+
+  Serial.println("local ip");
+  Serial.println(WiFi.localIP());
+    
+  delay(1000);
+  Serial.println("Creando cliente MQTT...");
+  client.setServer("192.168.100.5", 31010);
+  client.setCallback(callback);
+
+  if (!SD.begin(10)) {
+    Serial.println("initialization SD card failed!");
+    return;
+  }
+  else {
+  Serial.println("initialization  SD card done.");
+  }
+
+///Set variables en EEPROM
+  EEPROM.get(9,meterID);
+  diaEEPROM = EEPROM.read(2);// define que ya fue corrida al menos una vez el programa
+  dia_EEPROM = EEPROM.read(3);//variable de dia de la carpeta actual csv
+  anho_EEPROM = EEPROM.read(4);//variable de año de la carpeta actual csv  
+  mes_EEPROM = EEPROM.read(5);  //variable de mes de la carpeta actual csv  
+  dia_nextEEPROM = EEPROM.read(6);//variable la cual contiene el siguiente dia en cual se creara la carpeta csv  
+  linea1_EEPROM = EEPROM.read(7);//Esta variable es para limitar que el encabezado se mande solo una vez al crar el csv 
+  mes_nextEEPROM = EEPROM.read(0);//variable la cual contiene el siguiente mes en cual se creara la carpeta csv
+  /*Serial.println(mes_nextEEPROM);
+  Serial.println(meterID);
+  Serial.println(dia_nextEEPROM);
+  Serial.println(anho_EEPROM);
+  Serial.println(mes_EEPROM);
+*/
+}
+
+void loop() {
+  
+  //Loop reconexion a Mqtt
+  {
+  if (!client.connected()) {
+  reconnect();
+  }
+  client.loop();
+  }
+   
+  denmark();
+  delay(8370);
+}

BIN
Documento oficial.pdf