Buenas a todos, estoy tratando de hacer un odómetro, horómetro y calcular diversas cosas con Arduino Mega. Guardarlas en la EEPROM cuando apago, y volver a recuperarlas cuando prendo el Arduino.
Cómo puedo detectar que el Arduino se apaga? Pienso usar un circuito con un capacitor de 1000uF y una resistencia para mantenerlo prendido un tiempo y que el Arduino mediante una interrupción guarde algunas variables.
Desde ya gracias,
Saludos,
Agustín.
Una posible solución es que intercales un diodo antes del capacitor, tomes la señal antes del diodo (hacé un divisor de tensión si hace falta) y con el flanco de bajada (falling) generes la interrupción.
Algo como ésto

Saludos
Perfecto. Lo voy a probar a ver si me sale.
Consulta, tengo pensado alimentar el Arduino con dos baterías 18650 en serie a través del conector jack. Para hacer la lectura a través de algún pin, debería utilizar una resistencia para bajar de esos aprox 7V a 5V, no?
Por otro lado, a la interrupción la llamaría de la manera ¨común¨:
attachInterrupt(digitalPinToInterrupt(3), Apagado, FALLING);
Sería correcto así?
Edito: Vi que algunos usan las interrupciones en el setup, y otros en el loop, qué sería lo recomendable?
Si, si, poné un divisor.
Basado en el esquema de arriba: R1 = 10K, R2 = 15K.
No te olvides que las baterías llegan hasta unos 4.2V cuando están recién cargadas, por eso los valores que te sugiero.
En cuanto al attachInterrupt(), sí, es correcta esa definición si usas el pin 3.
Y dónde se aplica depende del uso.
Si sólo necesitas definirlo una vez y la interrupción "corre" constantemente, la definís en setup(), pero si la vas a activar/desactivar varias veces entonces va a tener que estar en el loop().
Por ej. tengo un código que pone el arduino en modo sleep, para "despertarlo" uso una interrupción que se dispara con un pulsador que además tiene otros usos, entonces antes de ponerlo a "dormir" hago el attach y cuando "despierta" hago el detach porque ya no quiero que responda a la interrupción hasta que vuelva a tener que entrar en sleep. En ese caso lo hago en el loop()
En tu caso, entiendo que va en el setup().
Genial, voy a usar esos valores de resistencia. Muchas gracias por el dato.
Entiendo la diferencia entre usarlo en los dos lugares, lo voy a usar en el setup si.
Armando el código y las direcciones para el guardado me surgió una duda: Está bien como definí las direcciones de cada uno de los lugares para después leerlos? El código está algo así:
attachInterrupt(digitalPinToInterrupt(2), Pulse_Event, RISING); // Interrupción para el cálculo de RPM.
attachInterrupt(digitalPinToInterrupt(3), Apagado, FALLING); //Interrupción para detectar el apagado
EEPROM.get(Dirtiempo, Thoras); //obtengo el valor de tiempo anterior en horas
Dirdist += sizeof(Thoras); //calculo la direccion siguiente
EEPROM.get(Dirdist, Diskm); //obtengo el valor de la distancia anterior en km
Direnergia += sizeof(Diskm); //calculo la direccion siguiente
EEPROM.get(Direnergia, EkwH); //obtengo el valor de la energía anterior en kwh
Dircombus += sizeof(EkwH); //calculo la direccion siguiente
EEPROM.get(Dircombus, Combtotal); //obtengo el valor del combustible total en L
Ahora, esto está dentro del setup. Para escribir los datos dentro de la EEPROM tengo que volver a obtener cada una de las direcciones en el loop? Para así poder guardar de esta forma:
void Apagado (){
EEPROM.update (Dirtiempo, Thoras);
EEPROM.update (Dirdist, Diskm);
EEPROM.update (Direnergia, EkwH);
EEPROM.update (Dircombus, Combtotal);
}
Acá me agarraste, no me he metido todavía con la eeprom, pero hay montones de artículos que podés leer para confirmar si estás haciendo las cosas bien, pegales una mirada.
Saludos
Ahh okey okey, no hay problema. Lo voy a investigar un poco más.
Una última cosa, tengo una variable que acumula valores (kilómetros), y necesito que cada 100km entre a alguna función (if o for) para hacer un breve cálculo de combustible. Se me ocurrió hacerlo teniendo en cuenta si es divisible y con un contador, puede funcionar?
if (Disconsumo/100 == multiplo){
Combconsumido = consumo*multiplo;
multiplo++;
}
Combtotal = Combtotal - Combconsumido;
Disconsumo está en km, multiplo arranca en 1.
Combtotal sería la variable que se monitorea y después se guarda en la eeprom.
Edito: Calcular el consumo cada 100km no sería muy acertado, yo lo tendría que ver más en "tiempo real". Se me ocurre hacer algo similar, pero cada 10km, funcionaría?
Edito2: Si ya estoy calculando la distancia recorrida en cada loop, puedo calcular el consumo en base a eso y tenerlo en tiempo real (o aproximado). Dejo el código y el resto de la explicación por si algún otro le sirve.
Te estaba por sugerir algo como eso. Perfecto.
Me quedé pensando que tal vez no sea buena idea grabar los datos desde la interrupción por lo que tarda (aunque no haría nada más que eso porque ya se apagaría).
Tal vez, si tu código no es demasiado largo, puedas directamente leer el estado del pin 3 y cuando está LOW guardas todo y entras a un loop infinito
while(1);
hasta que se apague. Y no necesitas la interrupción.
Saludos
Puede ser una buena idea también, lo voy a probar. Mañana voy a probar lo de guardar los datos antes de que se apague, y recuperarlos después con el circuito y las direcciones que puse.
Actualizo, encontré mejor solución en guardar las variables a partir de un botón manual antes de apagar el Arduino (el encendido y apagado lo manejaría yo).
Por otro lado, creo que voy a hacer el guardado y recuperación de las variables en una SD, así las puedo tener disponibles en la PC también.
Ahora, tengo una duda al momento de guardar y leer los datos en la SD. Al momento de pulsar el botón, entraría en una función así:
if (analogRead(botonguardar)==LOW) {
Horas = SD.open("Horas.txt");
if (Horas) {
Horas.print(Thoras);
Horas.close();
}
Dist = SD.open("Dist.txt");
if (Dist) {
Dist.print(Diskm);
Dist.close();
}
Energia = SD.open("Energia.txt");
if (Energia) {
Energia.print(EkwH);
Energia.close();
}
Combust = SD.open("Combust.txt");
if (Combust) {
Combust.print(Combtotal);
Combust.close();
}
}
Por qué guardo cada valor en un archivo distinto? Para que sea más fácil la lectura (a mi entender), que la haría en el setup de esta forma:
Horas = SD.open("Horas.txt");
if (Horas) {
while(Horas.available()){
Thoras = Horas.read();
}
Horas.close();
}
Dist = SD.open("Dist.txt");
if (Dist) {
while(Dist.available()){
Diskm = Dist.read();
}
Dist.close();
}
Energia = SD.open("Energia.txt");
if (Energia) {
while(Energia.available()){
EkwH = Energia.read();
}
Energia.close();
}
Combust = SD.open("Combust.txt");
if (Combust) {
while(Combust.available()){
Combtotal = Combust.read();
}
Combust.close();
}
Por qué leo de esa forma? Porque necesito que cada vez que inicie el arduino siga contando desde el último valor que guardó. No me termina de cerrar el método que utilizo, pero quiero saber si es correcto.
Ojo con las cosas que propones porque con una SD no se si tienes tiempo para guardar los datos. Lo has probado?
Lo que hiciste con la EEPROM esta bien.
Hola Surbyte, cambié la metodología de guardado y no va a ser al momento de apagar con una interrupción, sino que voy a utilizar un botón externo para guardar en el tiempo que sea necesario y después apagaría todo.
No lo pude probar todavía, pero hoy lo voy a hacer.
Gracias!
Actualizo: Probé el código con la SD y no funciona del todo bien. No lee ni empieza del valor que está guardado en el txt, sino que empiezan en 48, 50 y 55. No pude descubrir por qué. Si elimino los valores del txt desde la pc, empiezan todos de 0 (está bien). Pero si cargo valores (a través del código), veo en la pc que se cargaron bien, y vuelvo a correr el código para que los lea, veo esto en el monitor serie.
Horas: 48
Distancia: 55
E cinetica: 50
Combustible: 48.00
Utilizo una función seek para colocar el puntero en 0, pero a pesar de eso no logro hacer que lea bien los valores. Otra idea que vi, es eliminar el txt una vez que lo lea, a traves de un remove(), o a través de un O_TRUNC. Pero no termino de entender bien cómo funcionan ambos.
A mi me parece que
Horas = '0'
Distancia = '7'
E cinetica = '2'
Combustible = '0'
Porque SD.read() lee un byte que en definitiva es un char.
Tenés que "rearmar" el número como si lo leyeras por serial cuando lo tipeas en la consola.
Además, creo, que tendrías que usar .println o en su defecto una coma u otro separador como final del número, sino no sabés donde termina (salvo que guardes un solo dato, obvio).
Tené en cuenta que guardar/leer datos del archivo es prácticamente igual a hacerlo por puerto serie.
Saludos
Entiendo. Cómo podría rearmar el número? Teniendo en cuenta que guardo un sólo dato, la idea es despúes sobrescribir ese dato en la próxima porque no necesito una lista, sino sólo el último valor.
En la web vas a encontrar varias formas de hacer la conversión.
No es muy complicada.
Saludos
PD: No es por mala voluntad que no te lo explico y te hago buscar, es para que logres hacerlo solo.
Buenas, actualizo: finalmente logré obtener los valores de la sd y guardar en una variable, adjunto un corto ejemplo por si a alguien mas le sirve:
File Horas;
char Horaschar[3];
byte index = 0;
void setup() {
Horas = SD.open("Horas.txt");
if (Horas) {
while(Horas.available()){
Horaschar[index] = Horas.read();
index++;
}
Thoras = atof(Horaschar);
Horas.close();
}
}
En este caso estoy convirtiendo a un float, pero asumo que para otra variable el proceso es el mismo, cambiaría el atof no más.
Gracias gatul y Surbyte por la ayuda.
¡Bien!
Y sí, puedes usar atoi() para convertir a entero.
Saludos y ¡bien hecho!
1 Like