TODOPIC
Otros Microcontroladores / Dispositivos programables => Arduino => Mensaje iniciado por: nachin27 en 01 de Mayo de 2019, 18:52:08
-
Hola gente del foro, soy nuevo en el mundo arduino. Estoy armando un datalogger de tension y corriente. Lo monte con un arduino uno, tiene un reloj DS3231 y un lector de tarjetas sd. Funciona todo perfecto, me guarda los datos en la sd con fecha y hora. El tema es que quise agregarle un modulo bluetooth para leer los datos grabados en la sd y no logro hacerlo andar de ninguna manera. Espero me puedan ayudar. Muchas gracias de antemano. Saludos.
/*
***Energy Data Logger***
Data logger para monitoreo de consumo de energía electrica.
mide tensión y corriente para calcular el consumo de energía, y registra los datos en una trajeta SD
Copyright (C) 2018 Javier D'Ambra
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/* Conexiones de hardware / uso de pines
Placa Arduino Uno
Pines 0 y 1 -> Conexión serie/USB, para comunicación con PC (programación, control, etc.)
SD - Se comunica por SPI
MOSI -> pin 11
MISO -> pin 12
CLK -> pin 13
CS (SS) -> Pin 10
Reloj RTC DS3231 - Se comunica por I2C/Wire
SDA -> pin A4
SCL -> pin A5
Monitoreo de señales analógicas
Tensión: //para medir tensión se debe conectar un transformador ac/ac adecuado y calibrar los parámetros.
pin A2
Corriente: //para poder medir todos los parámetros electricos, se debe sensar corriente con un tranf de corriente SCT-013 100a.
pin A3
Senales de info/control
LED "latido" -> pin 5
LED "errorSD" -> pin 6
Pines reservados para futuras mejoras
LED (reservado) -> pin 4
Boton "arriba" |
Boton "abajo" |
Boton "izquierda" | -> array (teclado analógico) conectado a A1
Boton "derecha" |
Boton "enter" |
*/
// Librerías que serán incluídas
#include <SPI.h> //Comunicación con periferícos SPI
#include <Wire.h> //Comunicación I2C
#include <SdFat.h> //Usamos la librería SdFat en lugar de la de arduino por estar mas optimizada - Manejo de tarjeta SD - https://github.com/greiman/SdFat
SdFat SD; //Línea necesaria para compatibizar el programa escrito para SD.h (libreria original)
#include <TimeLib.h> //Gestion de fecha, hora, etc. - https://www.pjrc.com/teensy/td_libs_Time.html
#include <DS1307RTC.h> //Librería para control del reloj RTC (DS1307 ó DS3231) - https://www.pjrc.com/teensy/td_libs_DS1307RTC.html
#include <EmonLib.h> //Librería "EMON" (Energy Monitor) con funciones para medir tensión y corriente y calcular parámetros de energía - https://openenergymonitor.org/
#include <SoftwareSerial.h>
// Declaración de etiquetas para los pines utilizados
// pines reservados para futuras mejoras
//const int pinLedReservado = 4; // LED reservado
//const int pinTeclado = A1; // Pin para teclado analógico
// Pin para selección de la SD
const int CS_SD = 10;
// Pin para led´s
const int pinLedLatido = 5; // "latido"
const int pinErrorSD = 6; // "errorSD"
// Pines analógicos, señales analógicas
const int pinTension = 2; // Pin (analógico) para la medición (Valor de tensión) de la energía eléctrica.
const int pinCorriente = 3; // Pin (analógico) para la medición (Valor de Corriente) de la energía eléctrica.
// Constantes varias
const unsigned long intervaloLectura = 2000; //inervalo de lectura de sensores [milisegundos]
const unsigned long intervaloEscrituraSD = 100; //intervalo de escritura de datos en la SD [seg]
// Variables globales
EnergyMonitor emon1; //Crea instancia de la librería Emon para medición parámetros eléctricos.
// Variable para indicar presencia de tarjeta SD
boolean presenciaSD = 0;
// Sincro de reloj
const int intervaloSincro = 300; //Intervalo de sincronización del reloj arduino con el RTC [Seg.].
SoftwareSerial BluetoothSerial(2, 3); //RX, TX
File nrg_log;
//----------------------------------------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
BluetoothSerial.begin(9600); // Initialise BlueTooth
//inicialización mediciones
emon1.voltage(pinTension, 230.00, 1.6); // Voltage: input pin, calibration, phase_shift
emon1.current(pinCorriente, 60.6); // Current: input pin, calibration.
//inicialización de pines reservados a futuro (conectados en placa de control)
//pinMode(pinLedReservado, OUTPUT);
//Establecer pines digitales necesarios como salida
pinMode(pinLedLatido, OUTPUT); //salida donde se conecta el led "latido"
pinMode(pinErrorSD, OUTPUT); //salida donde se conecta el led "errorSD"
//Inicializa tarjeta SD
// ver si la SD está presente y puede inicializarse:
if (!SD.begin(10)) {
presenciaSD = 0; //tarjeta no detectada
digitalWrite(pinErrorSD, HIGH); //activa led error SD
}
else {
presenciaSD = 1; //tarjeta detectada
digitalWrite(pinErrorSD, LOW); // apaga led error SD
}
//Icicializamos el reloj y el tiempo
setTime(RTC.get());
ajustarReloj(); //funcion para ajustar el reloj por consola de ser necesario
setSyncProvider(RTC.get); //Establecemos el reloj RTC como proveedor del tiempo
setSyncInterval(intervaloSincro); // lo actualizamos segun intervalo definido
}
void loop() {
static unsigned long millisPrevio = intervaloLectura;
static unsigned long millisPrevio2 = intervaloEscrituraSD;
static float energia = 0;
static float energiaParcial = 0;
static boolean ledLatido = LOW;
static String datos;
unsigned long millisActual = millis();
if (millisActual - millisPrevio >= intervaloLectura){ //Ejecuta las siguientes acciones solo si se cumple el intervalo
Serial.println(F("Fecha/Hora; Potencia activa; Potencia aparente; Tensión; Corriente; Factor de potencia"));
Serial.print(timeLabel());
Serial.print(" ");
emon1.calcVI(20,1000); // Calcula todos los parametros. No. de medias ondas (cruces), time-out
emon1.serialprint(); // Imprime todas las variables (Pot real, pot aparente, Vrms, Irms, factor de potencia)
energiaParcial = energiaParcial + ((emon1.realPower * (intervaloLectura/1000))/3600000); //calculamos la energía acumulada (desde el ultimo grabacion SD) expresado en KWh.
energia = energia + ((emon1.realPower * (intervaloLectura/1000))/3600000); //calculamos la energía acumulada (desde el ultimo reset) expresado en KWh.
Serial.print(F("\nEnergía consumida: "));
Serial.println(energia, 4);
datos = timeLabel(); // coloca la marca de tiempo en el String datos
datos = datos + ", " + String(emon1.Vrms, 2); //va agregando todos los valores medidos al string
datos = datos + ", " + String(emon1.Irms, 2);
datos = datos + ", " + String(emon1.realPower, 1);
datos = datos + ", " + String(emon1.powerFactor, 2);
datos = datos + ", " + String(emon1.apparentPower, 1);
datos = datos + ", " + String(energiaParcial, 4);
datos = datos + ", " + String(energia, 4);
if (ledLatido == LOW) {
ledLatido = HIGH;
}
else {
ledLatido = LOW;
}
digitalWrite(pinLedLatido, ledLatido);
millisPrevio = millisActual;
}
if (millisActual - millisPrevio2 >= intervaloEscrituraSD * 1000UL){ //si se cumple el intervalo especificado, procede a grabar los datos
if (grabarDatosSD(datos)) { //graba los datos en la SD - si hubo error al grabar devuelve 1
digitalWrite(pinErrorSD, HIGH); //como hubo error e grabado prende el led error SD
}
energiaParcial = 0; //Vuelve a cero el contadordeenergía que integra entre períodos de grabación.
millisPrevio2 = millisActual;
}
}
//función que devuelve un String con la Hora/fecha actual
//Agrega el '0' a la izquierda en los valores de una cifra y le da formato a la hora/fecha
String timeLabel() {
time_t t = now();
String tiempoActual = String('0');
tiempoActual = year(t);
if (month(t) < 10)
tiempoActual = String(tiempoActual + '-' + '0' + month(t));
else
tiempoActual = String(tiempoActual + '-' + month(t));
if (day(t) < 10)
tiempoActual = String(tiempoActual + '-' + '0' + day(t));
else
tiempoActual = String(tiempoActual + '-' + day(t));
tiempoActual = String(tiempoActual + ' ');
if (hour(t) < 10)
tiempoActual = String(tiempoActual + '0' + hour(t));
else
tiempoActual = String(tiempoActual + hour(t));
tiempoActual = String(tiempoActual + ':');
if (minute(t) < 10)
tiempoActual = String(tiempoActual + '0' + minute(t) + ':');
else
tiempoActual = String(tiempoActual + minute(t) + ':');
if (second(t) < 10)
tiempoActual = String(tiempoActual + '0' + second(t));
else
tiempoActual = String(tiempoActual + second(t));
return tiempoActual;
}
//Función que graba en un archivo "nrg_log.txt" las mediciones realizadas
//Si el archivo existe se agregan datos, y si no se crea el mismo
//Recibe un String con el formato que se va a grabar
//Devuelve 0 si salio bien y 1 si hubo error
boolean grabarDatosSD(String data) {
time_t t = now();
String date = String('0'); //Generamos un string para usar de nombre de archivo con la fecha del día.
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 + ".txt");
char fileName[13];
date.toCharArray(fileName, 13); //convertimos el string a array de caracteres (así lo precisa la lib SD)
if (!presenciaSD) {
return 1; //termina la función si la SD no esta inicializada
}
if (SD.exists(fileName)) { //Si existe el archivo "fileName" lo abrimos y le agregamos datos etc.
File nrg_log = SD.open(fileName, FILE_WRITE);
if (nrg_log) { //Si el archivo se abre correctamente escribimos en el...
nrg_log.println(data);
nrg_log.close();
digitalWrite(pinErrorSD, HIGH); //
delay(100); //
digitalWrite(pinErrorSD, LOW); //
delay(50); // Doble parpadeo de LED para indicar que grabamos a la SD
digitalWrite(pinErrorSD, HIGH); //
delay(100); //
digitalWrite(pinErrorSD, LOW); //
return 0;
}
else {
return 1; //No se abrió el archivo...
}
}
else { //si no existe el archivo "nrg_log.cvs" lo creamos y escribimos su encabezado antes de los datos...
File nrg_log = SD.open(fileName, FILE_WRITE);
if (nrg_log) { //Si el archivo se abre correctamente escribimos en el...
nrg_log.print(F("Archivo de registro de mediciones de energía eléctrica. Creado: "));
nrg_log.println(timeLabel());
nrg_log.println('\n');
nrg_log.println(F("Fecha/Hora, Tensión, Corriente, Potencia activa, Factor de potencia, Potencia aparente, Energía de intervalo, Energía consumida total"));
nrg_log.println(data);
nrg_log.close();
digitalWrite(pinErrorSD, HIGH); //
delay(100); //
digitalWrite(pinErrorSD, LOW); //
delay(50); // Doble parpadeo de LED para indicar que grabamos a la SD
digitalWrite(pinErrorSD, HIGH); //
delay(100); //
digitalWrite(pinErrorSD, LOW); //
// re-open the file for reading:
File nrg_log = SD.open("fileName.txt");
if (nrg_log) {
//Serial1.println("fileName.txt:");
// read from the file until there's nothing else in it:
while (nrg_log.available()) {
char reading[10] = "";
char fileName = nrg_log.read();
int idx = 0;
bool somethingWasRead = false;
while (fileName != -1 && fileName != 13 && fileName != 10){
reading[idx] = fileName;
fileName = nrg_log.read();
idx++;
somethingWasRead = true;
}
reading[idx++]='\0';
//reading[idx] = 10;
if (somethingWasRead){
BluetoothSerial.write(reading);
delayMicroseconds(500);
//BTSerial.flush();
}
}
// close the file:
nrg_log.close();
} else {
// if the file didn't open, print an error:
Serial.println("error opening fileName.txt");
}
}
else {
return 1;
}
}
}[code]
/* Archivo de funciones auxiliares para manejo del chip RTC
parte del proyecto Energy Data Logger
*/
//Función para ajustar y chequear el reloj (RTC) de ser necesario.
void ajustarReloj(){
unsigned long millisPrevio = millis();
Serial.print(F("La fecha/hora seteada es: "));
Serial.println(timeLabel());
Serial.println(F("Si desea modificarla presione \"y\""));
Serial.println(F("Si desea chequear el RTC presione \"c\""));
while (millis() - millisPrevio < 10000UL) { //esperamos un tiempo la orden de entrar a la consola
if (Serial.available() > 0) {
char input = Serial.read();
if ((input == 'y')||(input == 'Y')) {
setearTiempo();
break;
}
else if((input == 'c')||(input == 'C')){
chequearRTC();
break;
}
else {
Serial.println(F("\nOpción incorecta..."));
}
}
}
Serial.println(F("Arrancando..."));
delay(5000);
return;
}
//Función que setea el tiempo en el RTC
//-------------------------------------
//Basado en el ejemplo SetSerial.ino de la librería DS3232RTC de Jack Christensen.
//https://github.com/JChristensen/DS3232RTC
//setea fecha/hora ingresando a travez del monitor serial lo siguiente:
// año,mes,dia,hora,minuto,segundo,
//el año puede ser de dos o cuatro dígitos.
//la coma final anula el timeout de un segundo permitiendo setaer el RTC de manera mas precisa
//no se realizan verificaciones de formato o validación.
void setearTiempo(){
time_t t;
tmElements_t tm;
Serial.println(F("Ingrese la nueva fecha/hora [aaaa,mm,dd,HH,mm,ss,]: "));
// chequear la entrada para setear el RTC, longitud minima 12, ej. yy,m,d,h,m,s
while(1){ //esperamos hasta que lleguen datos...
if (Serial.available() >= 12) {
// note that the tmElements_t Year member is an offset from 1970,
// but the RTC wants the last two digits of the calendar year.
// use the convenience macros from the Time Library to do the conversions.
int y = Serial.parseInt();
if (y >= 100 && y < 1000){
Serial.println(F("Error: el año se debe ingresar con dos o cuatro digitos!"));
}
else {
if (y >= 1000)
tm.Year = CalendarYrToTm(y);
else // (y < 100)
tm.Year = y2kYearToTm(y);
tm.Month = Serial.parseInt();
tm.Day = Serial.parseInt();
tm.Hour = Serial.parseInt();
tm.Minute = Serial.parseInt();
tm.Second = Serial.parseInt();
t = makeTime(tm);
RTC.set(t); // use the time_t value to ensure correct weekday is set
setTime(t);
Serial.print(F("La nueva fecha/hora es: "));
Serial.println(timeLabel());
// dump any extraneous input
while (Serial.available() > 0) Serial.read();
return;
}
}
}
}
//Función que chequea el estado del RTC
void chequearRTC(){
time_t t;
t = RTC.get();
if (t){
setTime(t);
Serial.print(F("Chip RTC OK \nLa fecha/hora es: "));
Serial.println(timeLabel());
}
else {
if (RTC.chipPresent()) {
Serial.println(F("El RTC se encuentra detenido, resetee el micro y ajuste la fecha/hora"));
}
else {
Serial.println(F("Error de lectura del RTC! chequear circuito."));
}
}
delay(10000);
return;
}[/code]