Why do I get stange value from this calculation ?

Hi.

I’m trying to get this code working but I’m having strange value for “u_mV”
I guess, it’s because I’m making division but even if I declare FLOAT, the result is also unwanted value.
It goes high, low, even negative for no apparent reason.

Here is the piece of code I’m suspecting to be wrong :

int calcul_tension_batterie_mV () {  //tout déclaré en int car les valeurs manipulées < 65536
const int R1_kOhm = 217 ;  //resistance du pont diviseur en kOhm
const int R2_kOhm = 100 ;    //resistance du pont diviseur en kOhm
int Vcc = readVcc();
int UPONT = analogRead (UPont);
//unsigned int result = (UPONT) * Vcc /1024 * (R1_kOhm + R2_kOhm)/R2_kOhm * 1000; 
  int result = (UPONT) * Vcc * 3;
  return result; // UBat en millivolts
}

Here is the entire code :

#define DEBUG

#define Led13 13
#define UPont A0 //meusure de tension au pont diviseur

#define Relai1 7
#define Pompe Relai1
#define PompeOn digitalWrite (Pompe, 0)
#define PompeOff digitalWrite (Pompe, 1)
#define EtatPompe !digitalRead (Pompe)

#define Relai2 6
#define Frigo Relai2
#define FrigoOn digitalWrite (Frigo, 0)
#define FrigoOff digitalWrite (Frigo, 1)
#define EtatFrigo !digitalRead(Pompe)

#define Relai3 5
#define Onduleur Relai3
#define OnduleurOn digitalWrite (Onduleur, 0)
#define OnduleurOff digitalWrite (Onduleur, 1)
#define EtatOnduleur !digitalRead(Onduleur)

long readVcc() {
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
  ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
  ADMUX = _BV(MUX5) | _BV(MUX0) ;
#else
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif  

  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring

  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
  uint8_t high = ADCH; // unlocks both

  long result = (high<<8) | low;

  result = 1121280L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000 (Ref 1.1V = 1.095V)
  return result; // Vcc in millivolts
}

int calcul_tension_batterie_mV () {  //tout déclaré en int car les valeurs manipulées < 65536
const int R1_kOhm = 217 ;  //resistance du pont diviseur en kOhm
const int R2_kOhm = 100 ;    //resistance du pont diviseur en kOhm
int Vcc = readVcc();
int UPONT = analogRead (UPont);
//unsigned int result = (UPONT) * Vcc /1024 * (R1_kOhm + R2_kOhm)/R2_kOhm * 1000; 
  int result = (UPONT) * Vcc * 3;
  return result; // UBat en millivolts
}
void blinkL13()
{
digitalWrite(Led13, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);               // wait for a second
  digitalWrite(Led13, LOW);    // turn the LED off by making the voltage LOW
  delay(1000); 
}
void AllumeFrigo()
{
  PompeOff;
  if (~EtatOnduleur)
  {
    OnduleurOn;
    delay(2000);
  }
  FrigoOn;
  //delay(10000);
}

void EteintFrigo()
{
  FrigoOff;
  if (~EtatPompe)
  {
    delay(500);
    OnduleurOff;
  }
}
  void AllumePompe()
  {
    if (~EtatOnduleur)
    {
      OnduleurOn;
      delay(1000);
    }
    PompeOn;
    delay(5000);
  }

  void EteintPompe()
  {
    PompeOff;
    if (~EtatFrigo)
    {
      delay(500);
      OnduleurOff;
    }
  }

  void setup() {
   #ifdef DEBUG
   Serial.begin(9600);
   #endif
    pinMode (Pompe, 1);
    pinMode (Frigo, 1);
    pinMode (Onduleur, 1);
    pinMode (UPont, 0);
    PORTD |= (1<<Onduleur) | (1<<Pompe) | (1<<Frigo); //Relais ouverts a 1 (fonctionnement des appareil à l'arrêt pour 1)
  } 


void loop() {
long u_mV = calcul_tension_batterie_mV ();
  #ifdef DEBUG
int  Vcc = readVcc();
int UPONT = (analogRead (UPont)) * readVcc() /1024;

  Serial.print("Vcc = ");
  Serial.print(Vcc);
  Serial.print("   UPONT = ");
  Serial.print(UPONT);
  Serial.print("   u_mV = ");
  Serial.print(u_mV);
  Serial.print("   Onduleur = ");
  Serial.print(EtatOnduleur);
  Serial.print("   Frigo = ");
  Serial.print(EtatFrigo);
  Serial.print("   Pompe = ");
  Serial.println(EtatPompe);
  #endif
  
  blinkL13();

  
  if (u_mV > 12600)
  {
    AllumeFrigo();
  }
  if (u_mV < 12000)
  {
    EteintFrigo();
  }
  if (u_mV > 14400)
  {
    AllumePompe();
  }
  if (u_mV < 12600)
  {
    EteintPompe();
  }
}

Integer division truncates, you need to use float, and it will work.

Consider

analogRead (A0) * 5 / 1024

This will yield an integer value from 0 to 4 only.

Alternatively

analogRead (A0) * 5.0 / 1024

Returns a correct floating point value from 0.0 to 4.995

No way, even with FLOAT, it gives me even negative value !

Here is the suspicious piece of code modified :

float calcul_tension_batterie_mV () {  //tout déclaré en int car les valeurs manipulées < 65536
const int R1_kOhm = 217 ;  //resistance du pont diviseur en kOhm
const int R2_kOhm = 100 ;    //resistance du pont diviseur en kOhm
int Vcc = readVcc();
int UPONT = analogRead (UPont);
float result = (UPONT) * Vcc /1024 * (R1_kOhm + R2_kOhm)/R2_kOhm * 1000; 
  //int result = (UPONT) * Vcc * 3;
  return result; // UBat en millivolts
}

Here the complete code.

#define DEBUG

#define Led13 13
#define UPont A0 //meusure de tension au pont diviseur

#define Relai1 2
#define Pompe Relai1
#define PompeOn digitalWrite (Pompe, 0)
#define PompeOff digitalWrite (Pompe, 1)
#define EtatPompe !digitalRead (Pompe)

#define Relai2 3
#define Frigo Relai2
#define FrigoOn digitalWrite (Frigo, 0)
#define FrigoOff digitalWrite (Frigo, 1)
#define EtatFrigo !digitalRead(Pompe)

#define Relai3 4
#define Onduleur Relai3
#define OnduleurOn digitalWrite (Onduleur, 0)
#define OnduleurOff digitalWrite (Onduleur, 1)
#define EtatOnduleur !digitalRead(Onduleur)

long readVcc() {
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
  ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
  ADMUX = _BV(MUX5) | _BV(MUX0) ;
#else
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif  

  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring

  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
  uint8_t high = ADCH; // unlocks both

  long result = (high<<8) | low;

  result = 1121280L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000 (Ref 1.1V = 1.095V)
  return result; // Vcc in millivolts
}

float calcul_tension_batterie_mV () {  //tout déclaré en int car les valeurs manipulées < 65536
const int R1_kOhm = 217 ;  //resistance du pont diviseur en kOhm
const int R2_kOhm = 100 ;    //resistance du pont diviseur en kOhm
int Vcc = readVcc();
int UPONT = analogRead (UPont);
float result = (UPONT) * Vcc /1024 * (R1_kOhm + R2_kOhm)/R2_kOhm * 1000; 
  //int result = (UPONT) * Vcc * 3;
  return result; // UBat en millivolts
}
void blinkL13()
{
digitalWrite(Led13, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);               // wait for a second
  digitalWrite(Led13, LOW);    // turn the LED off by making the voltage LOW
  delay(1000); 
}
void AllumeFrigo()
{
  PompeOff;
  if (~EtatOnduleur)
  {
    OnduleurOn;
    delay(2000);
  }
  FrigoOn;
  //delay(10000);
}

void EteintFrigo()
{
  FrigoOff;
  if (~EtatPompe)
  {
    delay(500);
    OnduleurOff;
  }
}
  void AllumePompe()
  {
    if (~EtatOnduleur)
    {
      OnduleurOn;
      delay(1000);
    }
    PompeOn;
    delay(5000);
  }

  void EteintPompe()
  {
    PompeOff;
    if (~EtatFrigo)
    {
      delay(500);
      OnduleurOff;
    }
  }

  void setup() {
   #ifdef DEBUG
   Serial.begin(9600);
   #endif
    pinMode (Pompe, 1);
    pinMode (Frigo, 1);
    pinMode (Onduleur, 1);
    pinMode (UPont, 0);
    PORTD |= (1<<Onduleur) | (1<<Pompe) | (1<<Frigo); //Relais ouverts a 1 (fonctionnement des appareil à l'arrêt pour 1)
  } 


void loop() {
float u_mV = calcul_tension_batterie_mV ();
  #ifdef DEBUG
int  Vcc = readVcc();
int UPONT = (analogRead (UPont)) * readVcc() /1024;

  Serial.print("Vcc = ");
  Serial.print(Vcc);
  Serial.print("   UPONT = ");
  Serial.print(UPONT);
  Serial.print("   u_mV = ");
  Serial.print(u_mV);
  Serial.print("   Onduleur = ");
  Serial.print(EtatOnduleur);
  Serial.print("   Frigo = ");
  Serial.print(EtatFrigo);
  Serial.print("   Pompe = ");
  Serial.println(EtatPompe);
  #endif
  
  blinkL13();

  
  if (u_mV > 12600)
  {
    AllumeFrigo();
  }
  if (u_mV < 12000)
  {
    EteintFrigo();
  }
  if (u_mV > 14400)
  {
    AllumePompe();
  }
  if (u_mV < 12600)
  {
    EteintPompe();
  }
}

Sounds like you're overflowing during integer calculations. Try switching your int variables to longs.

int Vcc = readVcc();
int UPONT = analogRead (UPont);
float result = (UPONT) * Vcc

Vcc is in millivolts, we can expect that value to be in the thousands range. UPONT will be 0 to 1023, which means the first calculation could easily be in the millions., well beyond the range of an int.

"long" doesn't make any progress.

Vcc = 5050 UPONT = 3457 u_mV = -9000 Onduleur = 0 Frigo = 0 Pompe = 0 Vcc = 5050 UPONT = 3452 u_mV = -9000 Onduleur = 0 Frigo = 0 Pompe = 0 Vcc = 5050 UPONT = 3457 u_mV = -9000 Onduleur = 0 Frigo = 0 Pompe = 0 Vcc = 5050 UPONT = 3457 u_mV = 3000 Onduleur = 0 Frigo = 0 Pompe = 0 Vcc = 5050 UPONT = 3447 u_mV = 19000 Onduleur = 0 Frigo = 0 Pompe = 0 Vcc = 5050 UPONT = 3457 u_mV = -9000 Onduleur = 1 Frigo = 1 Pompe = 1 Vcc = 5050 UPONT = 3467 u_mV = 19000 Onduleur = 0 Frigo = 0 Pompe = 0 Vcc = 5073 UPONT = 3442 u_mV = -25000 Onduleur = 1 Frigo = 1 Pompe = 1 Vcc = 5050 UPONT = 3457 u_mV = 3000 Onduleur = 0 Frigo = 0 Pompe = 0 Vcc = 5050 UPONT = 3447 u_mV = 3000 Onduleur = 0 Frigo = 0 Pompe = 0 Vcc = 5050 UPONT = 3457 u_mV = -25000 Onduleur = 0 Frigo = 0 Pompe = 0

u_mV still completely wrong with long instead of float !

hary:
“long” doesn’t make any progress.

Vcc = 5050 UPONT = 3457 u_mV = -9000 Onduleur = 0 Frigo = 0 Pompe = 0
Vcc = 5050 UPONT = 3452 u_mV = -9000 Onduleur = 0 Frigo = 0 Pompe = 0
Vcc = 5050 UPONT = 3457 u_mV = -9000 Onduleur = 0 Frigo = 0 Pompe = 0
Vcc = 5050 UPONT = 3457 u_mV = 3000 Onduleur = 0 Frigo = 0 Pompe = 0
Vcc = 5050 UPONT = 3447 u_mV = 19000 Onduleur = 0 Frigo = 0 Pompe = 0
Vcc = 5050 UPONT = 3457 u_mV = -9000 Onduleur = 1 Frigo = 1 Pompe = 1
Vcc = 5050 UPONT = 3467 u_mV = 19000 Onduleur = 0 Frigo = 0 Pompe = 0
Vcc = 5073 UPONT = 3442 u_mV = -25000 Onduleur = 1 Frigo = 1 Pompe = 1
Vcc = 5050 UPONT = 3457 u_mV = 3000 Onduleur = 0 Frigo = 0 Pompe = 0
Vcc = 5050 UPONT = 3447 u_mV = 3000 Onduleur = 0 Frigo = 0 Pompe = 0
Vcc = 5050 UPONT = 3457 u_mV = -25000 Onduleur = 0 Frigo = 0 Pompe = 0

u_mV still completely wrong with long instead of float !

I didn’t say replace float with long. I said replace one of your ints with long.

Multiply 5050 by 3457. Then compare the result to the maximum amount that can be stored in an int (16-bits).

Brute force: change all your ints to float

I've already tried everything , float, double, long...

I've spend all the afternoon with this !

Everything is working except this "u_mV" I can' get the proper value !

I just don't know what else to try !

Have you tried this too?

UPONT * (float(Vcc) /1024.0) * ( (float(R1_kOhm + R2_kOhm)/float(R2_kOhm)) * 1000.0);

hary: I just don't know what else to try !

Try posting updated code when you try something, because we have no idea if you've actually implemented it correctly (unlikely).

Is this a standard voltage divider? It should be r2/(r1+r2) not (r1+r2)/r2.

KeithRB: Is this a standard voltage divider? It should be r2/(r1+r2) not (r1+r2)/r2.

Except that the code is calculating the input voltage given the measured output voltage, not calculating the ADC value given the battery voltage!

Just to let you know I've done this to get "u_mV" :

const int R1_kOhm = 217 ;  //resistance du pont diviseur en kOhm
  const int R2_kOhm = 86 ;    //resistance du pont diviseur en kOhm 86
  long Vcc = readVcc();
  int UPONT = analogRead (A0);
  float tension_de_pont = UPONT * Vcc /1024;
  float u_mV = tension_de_pont * (R1_kOhm + R2_kOhm)/R2_kOhm;

I don't really understand the reason it didn't work and the reason it now works ! Indeed, it seems to have something to do with double and float but I got it working only making some tries, not understanding what I was doing !

Your original code started by multiplying two ints:

int Vcc = readVcc();
float result = (UPONT) * Vcc

Your updated code multiplies an int and a long:

long Vcc = readVcc();
  float tension_de_pont = UPONT * Vcc

The intermediate result (after the first multiplication) is treated as the larger of the two types. In the original code, it was trying to store UPONT * Vcc in an int. Considering your values of 5050 and 3457, the result is too big to store in an int, which can only store a number up to 32,767. In the newer code, the intermediate result is stored in a long, which has a maximum value 2,147,483,647. That is plenty large enough to store 5050 * 3467.