analogRead() erratic readings through op amp

have a circuit i built to discharge a lithium ion battery at a constant current. it uses a mosfet which is driven by a pair of resistor/capacitor low pass filters which are in turn driven by a PWM pin of the ardiuino.

one side of an lm358 op amp is used to regulate the current using the mosfet in linear mode. mosfet adjusts its resistance in order to keep the current constant across a 1 ohm 5 watt resistor.

the other side of the op amp is used as a voltage follower from the positve termimal of the battery so that the pin A0 can read its voltage and follow logic accordingly.

the problem is when reading the voltage of the battery it is very erratic in its readings and i’d like to know where i’m going wrong. I get 3.85 volts constant from my multimeter, but the a0 pin reads anywhere from 4.0 down to 2.85 volts and doesnt seem to steady itself.

I was using this circuit a long time ago and I THOUGHT that I had it working, but i’m second guessing myself as to whether or not that’s the case and I just gave up on it months ago. schematic attached for reference. Thanks

Which Arduino.
How do you power the Arduino.
Is it using default Aref.
Post the code.
Read the forum rules before you do.
Leo..

Sorry, forgot to say it’s a mega 2560.
Have tried to power through the USB port, as well as through the barrel jack with a 12v 3a wall wart (verified 12.5v through the wart via meter).
Using the default 5v internal voltage reference, and verified the 5v pin at 5.008 volts via meter while running both with the barrel jack as well as usb port.

doing a search here and on google there are some things that they suggested I try such as not powering my lm358 chip from the same 12v source of the arduino. so i set up a separate wall wart to that circuit and it has the same result. since the PWM value will not change to the mosfet gate, i’m assuming my double resistor/capacitor network would be fine and supply a relatively stable voltage, and seeing the current through the mosfet remain at about 200ma, this shows that it’s getting what it needs, and is driving it fine in linear mode.

If I hook up the battery’s positive terminal directly to the A0 pin, it will read the voltage perfectly fine, so there’s something in the return voltage follower that is causing this to muck up. I have changed out the lm358 chip to another to see if that was the issue, but the result is still duplicated. Have also changed out the mosfet, but also doesnt solve the issue.

#include <LiquidCrystal.h>

float vRef = 5.008;
float battCutoff = 3.0;
float battTestThreshold = 4.0;
int readSamples = 1;

int controlPin[16] = {2,3,4,5,6,7,8,9,10,11,12,13,14,44,45,46}; // Specify the PWM pins to use for each slot.  Pin 14 is not PWM and should be connected to a trimpot on its circuit board
int batteryPin[16] = {A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12,A13,A14,A15}; // Analog pins to read voltages of the cells
int controlPinSet[16] {10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // this is what the PWM pins get set to when we turn on the mosfets.  They will eventually be configurable through the serial line or interface and stored in eeprom

int currentState[16];
    // 0 = Waiting for battery, no battery present
    // 1 = Test currently in progress
    // 2 = Test is complete.  Battery has completed its testing and has dropped below the battCutoff
    // 4 = Error, low voltage.  Battery voltage is too low to begin a new test based on battTestThreshold
float mAh[16]; // cumulative milliamp hours for each cell as we test it
long int cellReading[16]; // used to store the read cell pins (long int used to average multiple reads of the battery and average them)
long int cellStartTestTime[16] = { 0 }; // used to store the starting time of a cell we test with
long int lastTimeRead[16]; // used to mark the time that the last cell was read so we can calculate the milliamp hours generated during that time
long int lcd1UpdateTime = 0; // used to indicate the last time the LCD screen #1 was updated or switched (cells 1-8)
long int lcd2UpdateTime = 0; // used to indicate the last time the LCD screen #2 was updated or switched (cells 9-16)
float currentBatteryVoltage[16]; // current voltage of the cell


void setup() {
  Serial.begin(9600);
  Serial.println("Initializing...");
  for (int i=0; i < 16; i++) {
    pinMode(controlPin[i],OUTPUT);
    digitalWrite(controlPin[i],LOW);
    currentState[i] = 0; // 0: waiting for battery
                         // 1: test in progress
                         // 2: test complete
                         // 3: battery low voltage - battery when it started to test was too low to start its test.  we msut be above 4.0 volts in order to test our battery indicated by battTestThreshold
    mAh[i] = 0.0;
    cellReading[i] = 0;
    lastTimeRead[i] = 0;
    currentBatteryVoltage[i] = 0.0;
  }
  Serial.println("Ready to start.");
}

void loop() {
  for (int i=0; i < 1; i++) {
    delay(1000);
    Serial.print("Time: ");
    lastTimeRead[i] = millis();
    Serial.print(lastTimeRead[i] / 1000);
    Serial.print("  *  Bay: ");
    Serial.print(i);
    Serial.print("  *  Pin: ");
    Serial.print(batteryPin[i]);
    cellReading[i] = 0;
    for (int s=0; s < readSamples; s++){
        cellReading[i] = cellReading[i] + analogRead(batteryPin[i]);   // we will average out the cell readings at the end of the loop to get a good voltage reading
    }
    cellReading[i] = cellReading[i] / readSamples; // averaging out the readings for the samples so that they are more consistent
    Serial.print("  *  Reading: ");
    Serial.print(cellReading[i]);
    Serial.print("  *  Voltage: ");
    currentBatteryVoltage[i] = vRef * cellReading[i] / 1023; // converts our reading into a voltage
    Serial.print(currentBatteryVoltage[i],2);
    Serial.print("  *  Status: ");
    Serial.print(currentState[i]);
    Serial.print("  *  Time elapsed: ");
    if (cellStartTestTime[i] > 0) {
      Serial.print((millis() - cellStartTestTime[i]) / 1000);
    }
    else {
      Serial.print("Not testing.");
    }
    
    
    




    Serial.println("");
    // now we need to determine what to do with this cell:

    if (currentBatteryVoltage[i] == 0.0) {
      // we have no cell in the bay and we need to continue to the next cell
      currentState[i] = 0;
    }
    if (currentState[i] == 0 and currentBatteryVoltage[i] > 0.0) {
      Serial.print("New battery detected in bay: ");
      Serial.println(batteryPin[i]);
      // we have detected that there is a new battery in the socket and we need to start testing
      cellStartTestTime[i] = millis(); // this is the time that our test begins
      analogWrite(controlPin[i],controlPinSet[i]); // set the PWM pin in order to turn the mosfet on
      currentState[i] = 1; // set the mode we are in to testing
    }
    if (currentState[i] == 1 and currentBatteryVoltage[i] > battCutoff) {
      // we have detected that the battery is currently in testing mode, and it is above our cutoff voltage, so we keep on testing the battery
      
    }
  }
  
  // do we need to update LED's? (need shift registers for this)
  // do we need to reach pushbutton switches to start a test? (need shift registers for this)
  // update the LCD displays if needed
}

Is the voltage reading the only thing that's not working?

Is the constant-current control working? Are you getting the expected current? (Are you reading what you expect across the current-sensing resistor with your DMM?)

Is the negative side of your battery connected to Arduino ground?

Are you doing any other analog reads? (You may need a short delay between reads.)


Is there a heatsink on the MOSFET?

I don't see the need for the voltage follower.

The op-amp output won't go completely to zero (without a negative supply voltage) so, is the MOSFET turning-off completely, or are you able to reduce the current as low as you want to go?

There might also be issues running the op-amp inputs near ground (so it might not work as expected at low currents).

divi2323:
resistor.

the other side of the op amp is used as a voltage follower from the positve termimal of the battery so that the pin A0 can read its voltage and follow logic accordingly.

the problem is when reading the voltage of the battery it is very erratic in its readings and i'd like to know where i'm going wrong. I get 3.85 volts constant from my multimeter, but the a0 pin reads anywhere from 4.0 down to 2.85 volts and doesnt seem to steady itself.

No decoupling capacitors, no low pass filtering to remove noise and EMI.

For opamps try 10uF or more nearish to the chip, its less critical than with logic.

You probably have digital noise on the supply.

If you want to sample a few times a second, use an analog low pass filter to match - otherwise you alias all the
high frequency noise down onto the signal of interest.

You can also digitally low-pass filter once you've sampled.

DVDdoug:
Is the voltage reading the only thing that's not working?

Is the constant-current control working? Are you getting the expected current? (Are you reading what you expect across the current-sensing resistor with your DMM?)

Is the negative side of your battery connected to Arduino ground?

Are you doing any other analog reads? (You may need a short delay between reads.)

Is there a heatsink on the MOSFET?

I don't see the need for the voltage follower.

The op-amp output won't go completely to zero (without a negative supply voltage) so, is the MOSFET turning-off completely, or are you able to reduce the current as low as you want to go?

There might also be issues running the op-amp inputs near ground (so it might not work as expected at low currents).

The voltage reading is the only thing that's not working correct. The current is staying constant at the intended amperage fine. There is no current sensing resistor. Since i'm setting the voltage of the mosfet gate in linear mode, it will stay constant for me, and i'm verifying this with a DMM in series to the battery.

all grounds lead to the same location. arduino, battery, resistor, power supply.

at the moment no other analog reads are being done. i'm aware of the capacitance of the ADC, and i intend to mitigate that with the double analog read workaround. eventually i'll be reading all 16 analog pins in a loop, and have anticipated that. in this scenario, only the A0 pin is being read at the moment. The read samples variable and loop which averages my readings would help to stabilize the reading, but it's still way off from what it's supposed to be. I set the sample to 1 and then that will allow me to read the only value.

mosfet is properly heat sinked, no issues there. current doesnt flux on me.

The opamp wont go all the way to zero, correct, the battery test will be shut off before that point. using a standard 18650 cell, i'll stop the test at 3v myself. the problem is, every second the voltage could flux as much as a full volt. so if the battery is sitting at 3.2 volts, and there's more time needed to finish the test, and the opamp acts up and makes it read at 2.8 volts, the test will get cut short too early.

the opamp running near a ground is unavoidable. the DIP chip that the LM358 comes on has set pin positions, or are you talking about something else?

for the need for a voltage follower, the reason it's in the circuit, there are several videos on YouTube from a guy named Chris Bird which developed a constant current charger/discharger which i'm modeling after. he mentions needing this opamp follower in order to generate a higher impedance for the arduino pin and also to isolate the pin from the direct voltage/current of the battery.

normally when I run into this problem, i'm not giving enough voltage to the opamp chip whether it's a quad or dual, and increasing that voltage will allow it to come in at the right level when doing a read.

MarkT:
No decoupling capacitors, no low pass filtering to remove noise and EMI.

For opamps try 10uF or more nearish to the chip, its less critical than with logic.

You probably have digital noise on the supply.

If you want to sample a few times a second, use an analog low pass filter to match - otherwise you alias all the
high frequency noise down onto the signal of interest.

You can also digitally low-pass filter once you've sampled.

more research today, came across this link operational amplifier - How to correct instability of op-amp voltage follower? - Electrical Engineering Stack Exchange

it talks about adding a low pass filter on the output like you do. Sampling a few times a second isnt a requirement, the code was only written that way to see if I could get a good running average. reading once or twice a second at most is all i'll ever need, and that's in an extreme scenario.

If i'm hearing you correctly, adding a 10uF cap to the output of the opamp to ground, and then continuing through a resistor as well, that should provide some stability, correct? How much of a lag is there in readings? not that this would change my original requirements, i'm just trying to understand how it will smooth it out for me at the voltage i'm expecting. further quesiton, does a resistor/capacitor network remove digital noise on the supply? the supply is relatively stable as far as I can tell (no scope but its voltage is very stable during operation (external hard drive wall wart)

DVDdoug:
I don't see the need for the voltage follower.

I second that.

divi2323:
he mentions needing this opamp follower in order to generate a higher impedance for the arduino pin and also to isolate the pin from the direct voltage/current of the battery.

A resistor does the same.
An opamp there (on a 12volt supply) is dangerous for the Arduino pin.

I would replace that voltage follower with a resistor between +batt and analogue in.
A resistor to prevent phantom powering the Arduino when it's off. 4k7 should be ok.
Add a 100n cap from analogue pin to ground.
Leo..

i'll give the resistor/cap combo a try and eliminate the voltage follower and report back. Can anyone tell me why a voltage follower would have been suggested for the circuit in the first place? Perhaps because the chip has 2 opamps on board, it was being wasted and to put it to some kind of use that's what he used?

divi2323:
If i'm hearing you correctly, adding a 10uF cap to the output of the opamp to ground, and then continuing through a resistor as well, that should provide some stability, correct?

No, use a low-pass-filter, series resistor then capacitor to ground, to cut out high frequency noise from
ever reaching the analog pin.

Putting 10uF directly on the output of an opamp will have a bad outcome, it will become unstable and oscillate
probably.