Medidor de corriente para implantación en robots

Buenos días, es mi primer post en este foro, pero llevo bastante tiempo informándome de dudas con antiguos posts y doy mi más sincera enhorabuena a todos aquellos que intentan ayudar constantemente a todos los usuarios de este foro.

Bueno vamos al grano, estoy haciendo un medidor de corriente para conectarlo a la salida de la batería del robot y comprobar el consumo total en cada instante de tiempo para saber la autonomía que le queda, el porcentaje de batería en cada momento y el consumo acumulado hasta el momento.

Estoy usando baterías LiFePO4 de 8 o 16 celdas (según robot) y como bien sabréis, las baterías no tienen una curva de tensión de carga-descarga en las que se pueda calcular fácilmente el consumo y la autonomía, ya que desde el 80% hasta el 20% aproximadamente entrega la misma tensión. Por tanto, usando un medidor de corriente CZH-LABS D-1085 de 50A (https://images-na.ssl-images-amazon.com/images/I/81fxkdlqZML.pdf) calculo una media de 1000 medidas de corriente (pasadas a mVoltios por el medidor) cada 10 segundos. Con esta parte del programa no hay ningún problema.

El problema viene cuando quiero enchufarlo a cargar y quiero que el programa diferencie entre un pico de tensión por frenada o perturbación y un pico de tensión cuando la batería está 100% cargada.

La forma en la que he pensado abordar este problema es activando un timer de 10 segundos mientras la entrada analógica de tensión (TensionIN = 1023) sea máxima. Si el timer cumple los 10 segundos registrando tensión máxima, dado que mi robot no va a hacer nunca una frenada de 10 segundos porque el terreno va a ser 100% llano en la totalidad de sus situaciones, saber que es porque está 100% cargado. Si no cumple ese tiempo (10 segundos) sabremos que el pico ha sido puntual.

En el caso de que la batería esté al 100%, quiero que siga midiendo pero muestre un 100% de batería y un 0.00 Ah de consumo hasta que la señal de tensión no sea máxima, que eso es fácil de hacer pero os lo digo para que tengáis más detalles.

En el caso de que el pico sea por frenada y no cumpla los 10 segundos, quiero que siga midiendo y que siga el bucle de forma normal.

Habréis notado que soy algo nuevo en esto :cold_sweat: así que mis dudas concretas son las siguientes:

  • ¿Cómo debo estructurar la parte del programa de la tensión? ¿Con un "while" para tensión máxima y una activación del timer el dicho "while" y despues "if" para cada caso? ¿Dónde lo pongo?

  • ¿Cómo puedo hacer para que en esos 10 segundos del timer, el programa siga midiendo normalmente?

Puede que sean dudas sencillas pero es mi "primer" programa así de extenso y me está costando estructurarlo bien. Me haríais un gran favor si me ordenárais la cabeza un poco.

MUCHIIIIIISIMAS GRACIAS DE ANTEMANO!

Espero haberme explicado bien, soy muy nuevo en este mundo comparado con vosotros pero intentaré ayudar en lo que pueda y sin ninguna duda, me mantendré informado de muchos posts que me sirvan de ayuda.

Un saludo

PD: Os facilito la parte del programa que tengo.

PD2: No sé si he insertado bien el código, no he encontrado el botón de "Insertar Código".

#include <EEPROM.h>

const int n = 100;  // Número de veces a medir

float D1; 
float D2; // transformación a corriente
int indice = 0; // indice de medida, 0-100
float totalD2 = 0; // total 100 medidas
float mediaD2 = 0; // media de las 100 medidas
const int eeAddress = 0; 
float mediatotalD2 = 0;
int contador = 0;
float autonomia;
float consumo = 0;
int intervalo = 3600000;
float levelbattery = 15; // en Ah
float tempshs;
float porcentaje = 0;

unsigned long Tiempoprevio = 0;

union Float_byte {
  float datoF;
  byte datoB[4];
} unionFB;

const int CurrentIN = A0; // Entrada analógica 0
const int TensionIN = A1; // Entrada analógica 1

void setup() {
  
  Serial.begin(9600) ;    // Inicializa comunicación serie
  //analogReadResolution(8) ; // Entrada analógica 8 bits (1024)
  //porcentaje = EEPROM.read(eeAddress); // función para rescatar el 
  // último dato una vez reiniciado

  unionFB.datoF = 0.0;
  unionFB.datoB[0] =  EEPROM.read(0);
  unionFB.datoB[1] =  EEPROM.read(1);
  unionFB.datoB[2] =  EEPROM.read(2);
  unionFB.datoB[3] =  EEPROM.read(3);
}

void loop() {
  unsigned long Tiempo = millis();

  int lect;

    if (indice < n) 
  for (indice = 0; indice < n; indice++) {
    delay(10); // para que lo haga en 10 segundos aprox
    lect = analogRead(CurrentIN)-512; //Porque la medida tiene un offset de 2,5V (512 de 1023)
    D1 = lect/10.23;
    D2 = D1 * 125.0/100;   // Valor en Amperios (1V = 25A), (1A = 0.04V)
    totalD2 = totalD2 + D2; // acumulador de las medidas
   
    }
    else if (indice >= n && mediatotalD2 == 0) { // función para ejecutar las medias
       mediaD2 = totalD2 / n; // media de las 100 medidas
       mediatotalD2 = (mediatotalD2 + mediaD2); // sumatorio de medias
       indice = 0; // retorno del indice a 0
       totalD2 = 0; // retorno del total a 0
       contador++; // incremento de uno del contador para sacar el total cada 10 iteraciones
       
    }
    else if (indice >= n && mediatotalD2 != 0) {
    mediaD2 = totalD2 / n; // media de las 100 medidas
    mediatotalD2 = (mediatotalD2 + mediaD2) / 2; // media de las medias
    indice = 0; 
    totalD2 = 0; 
    contador++;

     }
  if (contador == 10) { 
    contador = 0; 
    totalD2 = 0; 
    consumo = (mediatotalD2 * Tiempo) / 3600000; // Cantidad de corriente respecto al tiempo
    autonomia = levelbattery - consumo; 
      // guardar un float en la EEPROM en las posiciones 0-3
    unionFB.datoF = consumo;
    EEPROM.write(0, unionFB.datoB[0]);
    EEPROM.write(1, unionFB.datoB[1]);
    EEPROM.write(2, unionFB.datoB[2]);
    EEPROM.write(3, unionFB.datoB[3]);
    porcentaje = (autonomia/maxbattery)*100;
  
    //EEPROM.write(eeAddress, autonomia); // guardamos la capacidad
    // para poder tener registrado el último dato cuando 
    //se vuelva a iniciar el arduino
    tempshs = Tiempo/3600000.00;
   
    }
}

Os paso un fragmento de más o menos lo que quiero hacer, aunque no esté escrito correctamente en código

  • ¿Cómo puedo ponerle un máximo al timer pero que a su vez, cuando la señal deje de ser 1023, se pare y pueda ir tanto a un "if" como a otro?

Muchas gracias

  while (TensionIN == 1023) {
    TimerON(10000);

    if(Timer == 10000 && TensionIN == 1023) {
      Porcentaje = 100;
      Consumo = 0;
    }
  }
  if (Timer < 10000 && TensionIN != 1023) {
    medidas; //medidas corresponde a todo el calculo de medidas mostrado en el post anterior
    // las 1000 medidas, las medias y el cálculo del consumo y la batería
  }

Segun tu afirmación una situación es que tendras tensión máxima por 10 segundos y la otra ? Es un simple pico o sea un valor máximo y nada mas?

Eso es, si la tensión es máxima más de 10 segundos es porque el robot está totalmente cargado y el cargador todavía está conectado, si no mantiene la tensión máxima durante los 10 segundos, será porque el robot está sufriendo un pico de tensión debido a una frenada brusca, ya sea de 1ms, 5s o cualquier otro instante de tiempo menor que 10 segundos.

Supongo esto porque la superficie por la que va a trasladarse el robot es totalmente llana y no pasarán más de 10 segundos haciendo una frenada.

Realmente lo hago así para que el porcentaje de la batería no suba más del 100%.

Una vez el programa sabe que está más de 10 segundos a máxima tensión, imprime:

  • Nivel de batería: 100.0%

  • Consumo actual: 0.0 Ah

Hasta que la tensión sea diferente a máxima.

He hecho el programa con máquina de estados, posiblemente se entienda mejor así.

#include <EEPROM.h>

const int n = 100;  // Número de veces a medir
const int CurrentIN = A0; // Entrada analógica 0
const int TensionIN = A1; // Entrada analógica 1

float D1, D2; // voltaje de salida del sensor, 0-1023, pasado a 0-100 // transformación a corriente
int indice = 0; // indice de medida, 0-100
float totalD2 = 0; // total 100 medidas
float mediaD2 = 0; // media de las 100 medidas
const int eeAddress = 0; 
float mediatotalD2 = 0.0;
int contador = 0;
float autonomia;
float consumo = 0.0;
int lect;
float maxbattery = 15; // en Ah
float tempshs;
float porcentaje = 0.0;
unsigned long Tiempofull = 0;
int Tiempoprevio = 0;
int laststate = 0;
int currentstate;

union Float_byte {
  float datoF;
  byte datoB[4];
} unionFB;


void setup() {
  Serial.begin(9600) ;    // Inicializa comunicación serie
  //analogReadResolution(8) ; // Entrada analógica 8 bits (1024)
  //porcentaje = EEPROM.read(eeAddress); // función para rescatar el 
  // último dato una vez reiniciado

  unionFB.datoF = 0.0;
  unionFB.datoB[0] =  EEPROM.read(0);
  unionFB.datoB[1] =  EEPROM.read(1);
  unionFB.datoB[2] =  EEPROM.read(2);
  unionFB.datoB[3] =  EEPROM.read(3);

  Serial.print("Consumo guardado de la bateria: "); // Salidas de info por pantalla
  Serial.println(unionFB.datoF);
  maxbattery = maxbattery - unionFB.datoF;
  Serial.print("Nivel de la bateria: "); // Salidas de info por pantalla
  Serial.println(maxbattery);
}

void loop() {

  int state = 0;

  switch (state) {

    case 0: {
      if (TensionIN >= 1023) state = 1;
      if (TensionIN < 1023) state = 4;
    } 
    break;

    case 1: {
      Tiempoprevio = millis(); 
      state = 2; 
    } 
    break;
  
    case 2: {
      if ((TensionIN >= 1023) && (millis() - Tiempoprevio < 10000)) { 
          Calculo();
      } 
      if (TensionIN < 1023) state = 4;
      else if ((TensionIN >= 1023) && (millis() - Tiempoprevio >= 10000)) state = 3;
    } 
    break;

    case 3: {
      if (TensionIN >= 1023)
      porcentaje = 100.0;
      Serial.print("Porcentaje de la bateria: ");
      Serial.print(porcentaje);
      Serial.println(" %");
      consumo = 0.0;
      Serial.print("Consumo actual: ");
      Serial.print(consumo);
      Serial.println(" Ah"); 
      if (TensionIN < 1023) state = 4;
      }
    break;

    case 4: {
      Calculo();
      if (TensionIN >= 1023) state = 1;
      } 
      break;
      }
    
    currentstate = state;
    if(laststate != currentstate)
    Serial.print("Estado ");
    Serial.print(currentstate);
    Serial.print(" en proceso");
    laststate = currentstate;
  }

void Calculo() {

  unsigned long Tiempo = millis();// contador en tiempo real (milisegundos),
  // desborda en 47,9 días seguidos, así podemos contar el doble de tiempo en positivo
  
   if (indice < n) 
    for (indice = 0; indice < n; indice++) {
    delay(10); // sin delay hacemos 1000 medidas cada 250ms,
    // para ajustarlo a 1s le ponemos 100 retardos de 7.5ms
    lect = analogRead(CurrentIN)-512; //Porque la medida tiene un offset de 2,5V (512 de 1023)
    //lect = XX; para forzar la entrada y comporbar si itera bien
    //D1 = map(lect, 0, 1023, 0, 100);     // Valor normalizado (0..100)
    D1 = lect/10.23;
    D2 = D1 * 125.0/100;   // Valor en Amperios (1V = 25A), (1A = 0.04V)
    //El máximo será 5V y por lo tanto 125 A.
    totalD2 = totalD2 + D2; // acumulador de las medidas
    // indice = indice + 1; // indice de posición de la medida
    
    //Serial.print("Lectura: "); // Salidas de información por pantalla
    //Serial.print(lect);
    //Serial.print("\t Indice: ");
    //Serial.println(indice);
    //Serial.print("D1 = ");
    //Serial.print(D1);
    //Serial.print("\t D2 = ") ;
    //Serial.print(D2);
    //Serial.print("\t Total = ");
    //Serial.println(totalD2);
    }
    else if (indice >= n && mediatotalD2 == 0) { // función para ejecutar las medias
       mediaD2 = totalD2 / n; // media de las 100 medidas
       mediatotalD2 = (mediatotalD2 + mediaD2); // sumatorio de medias
       indice = 0; // retorno del indice a 0
       totalD2 = 0; // retorno del total a 0
       contador++; // incremento de uno del contador para sacar el total cada 10 iteraciones
       Serial.print("Media total: "); // Salidas de información por pantalla
       Serial.println(mediatotalD2);
    }
    else if (indice >= n && mediatotalD2 != 0) {
    mediaD2 = totalD2 / n; // media de las 100 medidas
    mediatotalD2 = (mediatotalD2 + mediaD2) / 2; // media de las medias
    indice = 0; // retorno del indice a 0
    totalD2 = 0; // retorno del total a 0
    contador++; // incremento de uno del contador para sacar
    //el total cada 10 iteraciones
    Serial.print("Media total: " ); // Salidas de información por pantalla
    Serial.println(mediatotalD2);
    //Serial.print("Contador: ");
    //Serial.println(contador);
     }
  if (contador == 10) { // final del ciclo
    contador = 0; // retorno del contador a 0
    totalD2 = 0; // retorno del total a 0
    consumo = (mediatotalD2 * Tiempo) / 3600000; // Cantidad de corriente respecto al tiempo
    autonomia = maxbattery - consumo; // Esta variable corresponde al consumo guardado
    //autonomia = unionFB.datoF - consumo;
      // guardar un float en la EEPROM en las posiciones 0-3
    unionFB.datoF = consumo;
    EEPROM.write(0, unionFB.datoB[0]);
    EEPROM.write(1, unionFB.datoB[1]);
    EEPROM.write(2, unionFB.datoB[2]);
    EEPROM.write(3, unionFB.datoB[3]);
    porcentaje = (autonomia/maxbattery)*100;
  
    //EEPROM.write(eeAddress, autonomia); // guardamos la capacidad
    // para poder tener registrado el último dato cuando 
    //se vuelva a iniciar el arduino
    tempshs = Tiempo/3600000.00;
    Serial.print("Tiempo transcurrido: ");  // Salidas de información por pantalla
    Serial.print(Tiempo);
    Serial.println(" ms");
    Serial.print("Tiempo transcurrido en horas: ");  // Salidas de información por pantalla
    Serial.print(tempshs);
    Serial.println(" horas");
    Serial.print("Consumo actual: ");
    Serial.println(consumo);
    Serial.print("Bateria restante: "); 
    Serial.print(autonomia,4);
    Serial.println(" Ah");
    Serial.print("Porcentaje de la bateria: ");
    Serial.print(porcentaje,4);
    Serial.println(" %");
    }
}

Una duda que tengo también sobre este programa es como definir inicialmente que vaya al state = 0;

Si me ayudáis me haríais un gran favor.

Muchas gracias de antemano