analogRead wrong values

Hi,

i built an energy power meter. It works fine, but I would also like to measure the battery voltage (the plus is wired to A3) in order to know when to charge it. The problem is that the analogRead returns wrong values. I even used A2 wired to the negative of the battery (which of course is the same as GND of the Arduino) to compute a difference, but I still get values like 4716mV when the real value (measured with a multimeter) is 3.95V. Below is a piece of the code.

[...]
  display.print("Battery: ");
  battery_voltage = map(analogRead(A3) - analogRead(A2), 0, 1023, 0, 5000);
  display.print(battery_voltage);
  display.println(" mV");
  display.print("Reference: ");
  ref_voltage = map(analogRead(A2), 0, 1023, 0, 5000);
  display.print(ref_voltage);
  display.println(" mV");
[...]

I tried changing to A7, doing multiple samples, etc. Nothing worked. Maybe the problem could be the reference voltage? The LiPo battery 3.7V gets stepped up to 5V and then goes to the Arduino NANO.

The schematic is pretty straightforward, as I wired the plus of the battery directly to A3 and the GND is in common with everything.

Here it is the full code.

// Sketch for Arduino NANO

#include <Wire.h>
#include <Adafruit_INA219.h>
#include <Adafruit_SSD1306.h>
#include <SPI.h>
#include "SdFat.h"

SdFat SD;

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
Adafruit_INA219 ina219;

unsigned long previousMillis = 0;
byte interval = 10;

bool sd_status = 1;

float max_curr;
float max_load;
float max_pow;

float shuntvoltage = 0;
float busvoltage = 0;
float current_mA = 0;
float loadvoltage = 0;
float energy = 0;
float power_mW = 0;
float res = 0;

int battery_voltage = 0;
int ref_voltage = 0;

File AllFile;

void setup() {
  pinMode(A3, INPUT);
  pinMode(A2, INPUT);
  
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);    //Accendo il display
    
  ina219.begin();   //Inizio con la calibrazione più sicura 
  
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);

  display.setCursor(0,0);
  display.println("  POWER METER by JS");
  display.println("Calibration: OK!");
  display.print("Battery: ");
  battery_voltage = map(analogRead(A3) - analogRead(A2), 0, 1023, 0, 5000);
  display.print(battery_voltage);
  display.println(" mV");
  display.print("Reference: ");
  ref_voltage = map(analogRead(A2), 0, 1023, 0, 5000);
  display.print(ref_voltage);
  display.println(" mV");
  display.display();
  delay(10000);
}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    ina219values();
    
    if (SD.begin(10)) {
      sd_status = 1;
      AllFile = SD.open("ALL.txt", FILE_WRITE);
      if (AllFile) {
        AllFile.print(millis());
        AllFile.print(",");
        AllFile.print(loadvoltage);
        AllFile.print(",");
        AllFile.println(current_mA);      
        AllFile.close();
      }
    } else sd_status = 0;
 
    displaydata();
  }
}

void displaydata() {   //Stampo tutto sul display 128x64
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  
  display.setCursor(0,0);
  display.print(loadvoltage);
  display.print(" V  ");
  if (current_mA > 1000) {
    display.print(current_mA/1000);
    display.println(" A");
  } else {
    display.print(current_mA);
    display.println(" mA");
  }
    
  if (power_mW > 1000) {
    display.print(power_mW/1000);
    display.print(" W  ");
  } else {
    display.print(power_mW);
    display.print(" mW  ");
  }
  if (res > 1000) {    //Stampo la resistenza e la converto così da non occupare troppo schermo
    display.print(res/1000);
    display.println(" kohm");
  } else {
    display.print(res);
    display.println(" ohm");
  }

  if (energy > 1000) {   //Stampo l'energia e la converto così da non occupare troppo schermo
    display.print(energy/1000);
    display.print(" Wh  ");
  } else {
    display.print(energy);
    display.print(" mWh  ");
  }
  display.print("t: ");
  display.print(millis()/1000);
  display.println(" s");
  
  display.print(max_curr/1000);    //Stampo la massima corrente e potenza
  display.print(" A  ");
  display.print(max_pow/1000);
  display.print(" W"); 
  if (sd_status == 0) display.print(" SD!");
  else display.print(" SD OK");
  display.display();
}

void ina219values() {
  if ((current_mA < 500) && (current_mA != 0)) {   //Calibrazione costante
    if (current_mA < 200) ina219.setCalibration_16V_400mA();
    else ina219.setCalibration_32V_1A();
  }
  else ina219.begin();
  
  shuntvoltage = ina219.getShuntVoltage_mV();   //Ottengo i vari dati da INA219
  busvoltage = ina219.getBusVoltage_V();
  current_mA = ina219.getCurrent_mA();
  loadvoltage = busvoltage + (shuntvoltage / 1000);

  res = abs(loadvoltage*1000/current_mA);
  power_mW = loadvoltage*current_mA;
  energy = energy + loadvoltage * current_mA / 3600;

  if (max_curr < current_mA) max_curr = current_mA;   //Calcolo massima corrente, tensione e potenza
  if (max_load < loadvoltage) max_load = loadvoltage;
  if (max_pow < power_mW) max_pow = power_mW;
}

Back of the PCB.

You should never connect a battery or power source directly to an I/O pin. It may be blown.

So if I use a 10k resistor in between it should be ok? Or the problem may be that the analog pin is blown?

Generally, a resistor is the right idea. But a good answer to your question requires looking at a full schematic of the system.

One worry - on some processors, doing an analog read of two different pins requires a time delay between them, or there will be erroneous readings... You're doing that with A3 and A2... taking the analog value of a grounded pin is kind of silly anyway. Try removing that and measuring only the battery positive pin (yes, with a resistor in series).

1 Like

Without a good analog voltage reference, the analogRead() can not be used to measure a voltage.

The default analog reference is set to VCC (the 5V pin).
When you use analogRead() to measure a voltage and the 5V is lower than 5.000V, then the Arduino thinks that the measured voltage with analogRead() is higher.

To measure a voltage with a changing VCC (the 5V pin), you need to use the internal reference voltage of 1.1V. That means you might need a voltage divider to measure the battery. That 1.1V can be 1.0V to 1.2V, so you have to determine the actual voltage for each Arduino board.

How is the step-up to 5V done ? With a cheap noisy module ? Is that the board on the back side ?

There is a trick to measure the VCC. By measuring the internal 1.1V with the 5V as reference and turning around the calculating, it is possible to calculate what the 5V really is.
Search for "arduino secret voltmeter" or "arduino measure own vcc".

The Arduino Nano can measure from 0 to 5V with 5mV resolution. When doing things right and with some calibrating and tuning and taking the average of many samples, it is really possible to achieve that.

I know it's silly, but I got frustrated because only A3 did not work. I know it was useless.. Anyway, now I'll try with a resistor

The module is salvaged from some wireless earbuds (yes, it is the board on the back side) . I don't think it is very noisy, and even then, the measure is off by almost 1V. I did think about using the internal reference, and I think I will try like that. Just I have to make an voltage divider.
It is very strange, I've done may projects and never had this kind of issue.
I'll even try using another Arduino now to see if the problem is the board.

It is possible that something is damaged.
The ATmega328P microcontroller has the analog section separated from the rest. It is possible to blow or damage the analog section and the rest of the chip is still okay.

1 Like

Ok, I've done a voltage divider and misured the voltage of the battery firstly normally and then with the internal reference of 1.1V (adjusted to 1.081V) WITH ANOTHER ARDUINO NANO and IT WORKS with reasonable accuracy (3.9V measured vs 3.85V real). The issue was the analog section on the ATmega328P as suggested by Koepel:

It is possible that something is damaged.
The ATmega328P microcontroller has the analog section separated from the rest. It is possible to blow or damage the analog section and the rest of the chip is still okay.

What was actually happening is that A2 and A3 of my energy meter Arduino Nano have both low resistance towards ground (about 400 ohms) while other analog pins have about 800k (or no path at all with the Arduino Nano ON). That meant that those pins are BROKEN. My SOLUTION was just to use A7 instead.

Thank you for your help!

2 Likes