This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Inconsistent battery measurement

Hi!

I'm having issues with inconsistent battery readout results. I'll elaborate more below, but in general, I have multiple devices which 'sleep' most of the time, and when they wake up they measure the battery voltage using the SAADC, translate it to battery %, and report it via BAS. My problem is that within a few hours, with nothing major changing, I can get an enormous variance in the battery % values reported. The voltage is read before any peripherals draw current, before advertising starts, the batteries are placed in a control environment so the temperature is constant, the code they run is constant, and there is nothing else I can think of which can affect them.

My devices use: nRF52840, CR2032 batteries (~3V). No relevant peripherals draw current until after the SAADC read is completed.

To get the battery %, I use the SAADC with pre-scaling 1/6, 10 bit resolution, reference voltage 0.6A, I use the linearization formula suggested here for the CR2032 curve. While I understand the curve may be inaccurate in terms of it matching reality for my specific battery, I should still expect it to give me consistent readouts- i.e, that the battery % values given should be ballpark accurate, should not increase sporadically, with minor fluctuations etc.

When I try to read the ADC multiple consecutive times with 1 ms sleep time in between them, I get a small variance between the results, lets say up to 5%. its only between different reads in different wake ups, where I see this large gap between measurements.


I'm not sure what could be causing such a large variance in the measurements, and would love it if someone could point me in any possible direction.
Here is an example of the values I'm logging:

Day0 08:36 - ADC value 849, Bat 90%

Day1 14:55 - ADC value 827, Bat 48%.

Day1 14:56 - ADC value 839, Bat 73%

Day1 14:57 - ADC value 844, Bat 83%

Day1 15:26 - ADC value 802, Bat 30%

Day1 21:27 - ADC value 785, Bat 21%

Thanks!

Parents
  • Hello,

    That does sound strange.

    While I understand the curve may be inaccurate in terms of it matching reality for my specific battery, I should still expect it to give me consistent readouts- i.e, that the battery % values given should be ballpark accurate, should not increase sporadically, with minor fluctuations etc.

    Your understanding here is correct, the measurements should be fairly accurate - accurate enough for most use-cases. The fluctuations you are seeing is larger than expected.

    its only between different reads in different wake ups, where I see this large gap between measurements.

    Are you performing a calibration before you begin sampling? It is recommended to perform a calibration upon every initialization of the SAADC, and when there has been a significant change in temperature.

    No relevant peripherals draw current until after the SAADC read is completed.

    What do you mean by 'relevant' here?

    The fluctuations you are seeing might indeed be the result of another peripheral / the SoftDevice drawing a lot of power right at the time of sampling.
    You say that nothing else is happening on the device when the measurement is taking -  how have you verified this?
    Is the SoftDevice enabled?
    How many samples are you basing the BAS update on? You might benefit from taking multiple samples over a small period, or enabling oversampling for this.

    Looking forward to resolve this issue together!

    Best regards,
    Karl 

  • Hi Karl!

    I'm not performing calibration before sampling, I'll try adding it to my flow so it will occur upon initialization of the SAADC.

    What I meant by 'relevant peripherals' is that I do perform some operations before sampling the battery, but I don't think they are relevant as they should not draw any significant power. For example, I feed the watchdog, check some flags, configure some GPIOs to input/output etc.

    I had considered oversampling, but after sampling 5 consecutive times (with 1 ms in between each sample) and getting more or less the same result, I've concluded that the sampling is relatively consistent within a short period of time.

    I have found a scenario where if a few conditions are met, I sample the battery (again) after having advertised for a minute or more. So I need to issue a fix for this and test if this helps solve the problem.

    I'll add all of these to my code, test it and let you know if this fixes the problem.

    In the meantime, I noticed there is an SAADC 'version 2' in SDK 17.0.0 (nrfx_saadc_v2). Could you explain what the difference is between version1 and version 2 please? Could version 2 help me get more accurate results? and are there any examples using SAADC version 2?

  • Hi again!

    I've been unsuccessful in trying to capture the ADC readings on RTT viewer with a board displaying this increase in power. While I keep trying to catch one red handed, I can pass the attached table onto you and hope it helps.

    What this table shows is as follows:

    1. Wake up, initiate the SAADC, calibrate it and take 5 measurements with 1ms sleep in between them.

    2. On the first measurement, save the ADC read value.

    3. Average the 5 measurements and report them as the battery %.

    4. Do the rest of the relevant logic (advertise, connect report measurements, other stuff and go to sleep for 10 minutes).

    repeat these steps.

    So, you might notice slight inconsistencies between the ADC value in the table and its matching battery %- that is because the SAADC value is the first read, which the battery% is the average of 5 readings.

    Battery increase measurements example.xlsx

    The saadc setup / configuration is as follows:


    #define ADC_REF_VOLTAGE_IN_MILLIVOLTS 600 //
    #define DIODE_FWD_VOLT_DROP_MILLIVOLTS   270 //
    #define ADC_RES_10BIT 1023 //
    //#define ADC_RES_12BIT 4095 //
    #define ADC_PRE_SCALING_COMPENSATION 6 //
    #define ADC_RESULT_IN_MILLI_VOLTS(ADC_VALUE) ((((ADC_VALUE)*ADC_REF_VOLTAGE_IN_MILLIVOLTS) / ADC_RES_10BIT) * ADC_PRE_SCALING_COMPENSATION)

    The SAADC init function is this:

    nrfx_saadc_config_t saadc_config = NRFX_SAADC_DEFAULT_CONFIG;
    
        nrf_saadc_channel_config_t channel_config;
    
        err_code = nrfx_saadc_init(&saadc_config, saadc_event_handler);
        APP_ERROR_CHECK(err_code);
        channel_config           = (nrf_saadc_channel_config_t)NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_VDD);
        channel_config.reference = NRF_SAADC_REFERENCE_INTERNAL;
        channel_config.gain      = NRF_SAADC_GAIN1_6;
        channel_config.acq_time  = NRF_SAADC_ACQTIME_40US;
    
        err_code = nrfx_saadc_channel_init(HAL_SAADC_VBAT_CHANNEL, &channel_config);

    If you need anything else, I'd rather we switch the ticket to private before sharing more specific code.

    Thanks!

  • Hello again,

    A.P said:
    I've been unsuccessful in trying to capture the ADC readings on RTT viewer with a board displaying this increase in power. While I keep trying to catch one red handed, I can pass the attached table onto you and hope it helps.

    Thank you for providing me the table of results. I have not heard of this behavior previously, so it would definitely be interesting to see the rising battery measurements if you should encounter them again.

    A.P said:
    Wake up, initiate the SAADC, calibrate it and take 5 measurements with 1ms sleep in between them.

    Are you doing the 1 ms delay to avoid an outlier sample caused by high current draw from a peripheral, or is there some other reason for the 5 samples over 5 ms?

    A.P said:
    So, you might notice slight inconsistencies between the ADC value in the table and its matching battery %- that is because the SAADC value is the first read, which the battery% is the average of 5 readings.

    Thank you for the thorough explanation. 

    A.P said:
    If you need anything else, I'd rather we switch the ticket to private before sharing more specific code.

    I can set the ticket to private at any time, if you wish to share more code.
    As it stands right now, I do not see anything in your configuration that could cause rising measurements. I do however have two comments to the above:
    I see you are referencing HAL_SAADC_VBAT_CHANNEL, is HAL_SAADC_VBAT_CHANNEL = NRF_SAADC_INPUT_VDD ?
    I also register that you have the DIODE_FWD_VOLT_DROP_MILLIVOLTS define in place. This define is to account for the voltage drop over the reverse current protection diodes on some of the Development Kits. In your case, the nRF52840 DK instead uses power switches ( and no diodes ) so this voltage drop can be discarded.
    This might actually have quite an impact on your battery measurements, in the case that you are accounting for the voltage drop without having reverse current protection diodes present - because it will shift the samples further out on the discharge curve.
    Could you confirm whether you are using an nRF52840 DK or a custom board, by the way?
    In the case that you are using a custom board, could you confirm for me whether you have reverse current protection, and in that case which type of reverse current protection you are using?

    Best regards,
    Karl

  • I looked through the posts in this thread and don't see one of the fundamental steps for getting a useful voltage measurement on a coin cell to infer capacity. Out of interest a (say) 270mAH coin cell which has supplied (say) 220mAH - so most of the capacity - can recover to 2.95 volts or higher after a significant period where the coin cell does not supply significant current (say few uA). When this cell is used to power a circuit which measures the voltage an erroneous reserve power indication can result, despite monitoring temperature and using compensation curves. The reason is the changing internal cell resistance as the chemistry of the battery changes over time and discharge.

    One solution is to identify a part of the circuit which presents a predictable load each time it is turned on, perhaps a simple resistor driven by a port but also perhaps a small motor or voice coil or even a fixed code execution sequence such as code CRCC. Applying a fixed sequence of these battery load pulses during battery measurement allows measurement both under a known load and under relatively little load (the latter by sleeping during the SAADC sample). The difference between loaded and unloaded  measurements allow inferring the approx internal resistance of the coin cell, and thus better estimation of remaining battery life using the temperature and curve fitting already presented. Simply put, the loaded measurement returns a much lower battery voltage than the unloaded measurement. Pulse time is affected by total reservoir capacitance, which props up the coin cell battery voltage; the more the capacitance the longer the required load test pulse.

  • Thank you very much for your input here @hmolesworth, these are all important aspects that you mention!
    The implementation you describe could definitely yield a more accurate measurement of the remaining battery charge. If I could upvote your comment twice then I would have done it!

    I personally have yet to see a consistent trend of increasing VDD readings for a battery, such as the numbers shared by A.P initially. For most use-cases similar to this, reading the VDD and making sure there is no big power draw at that instant is usually enough to get a good ballpark of the remaining battery charge, in my experience. However, the approach you describe could absolutely be a good addition to projects if higher accuracy is needed.

    In applications where battery monitoring is critical, I would also recommend looking into the addition of a low-power battery fuel gauge IC to the project, in order to get precise battery charge measurements.

    Best regards,
    Karl

  • Hi!

    Are you doing the 1 ms delay to avoid an outlier sample caused by high current draw from a peripheral, or is there some other reason for the 5 samples over 5 ms?

    The 5 multiple samples are there, as you say, to average out any outlier samples. I placed a 1 ms in between them to 'space out' the samples over a bit of time. There shouldn't be any peripherals drawing power before or during the sampling.

    I see you are referencing HAL_SAADC_VBAT_CHANNEL, is HAL_SAADC_VBAT_CHANNEL = NRF_SAADC_INPUT_VDD ?

    Yes, we set up the SAADC channel as follows:

    nrf_saadc_channel_config_t channel_config;
    
        err_code = nrfx_saadc_init(&saadc_config, saadc_event_handler);
        APP_ERROR_CHECK(err_code);
        channel_config           = (nrf_saadc_channel_config_t)NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_VDD);
        channel_config.reference = NRF_SAADC_REFERENCE_INTERNAL;
        channel_config.gain      = NRF_SAADC_GAIN1_6;
        channel_config.acq_time  = NRF_SAADC_ACQTIME_40US;
    
        err_code = nrfx_saadc_channel_init(HAL_SAADC_VBAT_CHANNEL, &channel_config);

    The channel is set to NRF_SAADC_INPUT_VDD before init and #define HAL_SAADC_VBAT_CHANNEL 0.

    I also register that you have the DIODE_FWD_VOLT_DROP_MILLIVOLTS define in place. This define is to account for the voltage drop over the reverse current protection diodes on some of the Development Kits. In your case, the nRF52840 DK instead uses power switches ( and no diodes ) so this voltage drop can be discarded.

    You're right. I copied this code from the example, and indeed we do not have a reverse current protection diode in place. Accordingly, we do not use this value in any of our calculations, it was placed there before we had decided whether to place a reverse current protection diode or not, and we had overlooked removing it from our code. Thank you for that point though, I will make a note of it in case we add a reverse battery protection diode to our PCB.

    Could you confirm whether you are using an nRF52840 DK or a custom board, by the way?
    In the case that you are using a custom board, could you confirm for me whether you have reverse current protection, and in that case which type of reverse current protection you are using?

    We are using a custom board, not the DK. No reverse current protection is in place.


    I'm afraid I still have not been able to catch this raising battery values while having a debugger attached to the board. I'll try to give you the data as soon as possible. Just so I'm sure we're on the same page, you need the ADC readings as well as the battery% calculated from every sample. Is there anything else you'd need?

    Thanks!

Reply
  • Hi!

    Are you doing the 1 ms delay to avoid an outlier sample caused by high current draw from a peripheral, or is there some other reason for the 5 samples over 5 ms?

    The 5 multiple samples are there, as you say, to average out any outlier samples. I placed a 1 ms in between them to 'space out' the samples over a bit of time. There shouldn't be any peripherals drawing power before or during the sampling.

    I see you are referencing HAL_SAADC_VBAT_CHANNEL, is HAL_SAADC_VBAT_CHANNEL = NRF_SAADC_INPUT_VDD ?

    Yes, we set up the SAADC channel as follows:

    nrf_saadc_channel_config_t channel_config;
    
        err_code = nrfx_saadc_init(&saadc_config, saadc_event_handler);
        APP_ERROR_CHECK(err_code);
        channel_config           = (nrf_saadc_channel_config_t)NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_VDD);
        channel_config.reference = NRF_SAADC_REFERENCE_INTERNAL;
        channel_config.gain      = NRF_SAADC_GAIN1_6;
        channel_config.acq_time  = NRF_SAADC_ACQTIME_40US;
    
        err_code = nrfx_saadc_channel_init(HAL_SAADC_VBAT_CHANNEL, &channel_config);

    The channel is set to NRF_SAADC_INPUT_VDD before init and #define HAL_SAADC_VBAT_CHANNEL 0.

    I also register that you have the DIODE_FWD_VOLT_DROP_MILLIVOLTS define in place. This define is to account for the voltage drop over the reverse current protection diodes on some of the Development Kits. In your case, the nRF52840 DK instead uses power switches ( and no diodes ) so this voltage drop can be discarded.

    You're right. I copied this code from the example, and indeed we do not have a reverse current protection diode in place. Accordingly, we do not use this value in any of our calculations, it was placed there before we had decided whether to place a reverse current protection diode or not, and we had overlooked removing it from our code. Thank you for that point though, I will make a note of it in case we add a reverse battery protection diode to our PCB.

    Could you confirm whether you are using an nRF52840 DK or a custom board, by the way?
    In the case that you are using a custom board, could you confirm for me whether you have reverse current protection, and in that case which type of reverse current protection you are using?

    We are using a custom board, not the DK. No reverse current protection is in place.


    I'm afraid I still have not been able to catch this raising battery values while having a debugger attached to the board. I'll try to give you the data as soon as possible. Just so I'm sure we're on the same page, you need the ADC readings as well as the battery% calculated from every sample. Is there anything else you'd need?

    Thanks!

Children
  • Hi,

    A.P said:
    The 5 multiple samples are there, as you say, to average out any outlier samples. I placed a 1 ms in between them to 'space out' the samples over a bit of time. There shouldn't be any peripherals drawing power before or during the sampling.
    A.P said:
    Yes, we set up the SAADC channel as follows:
    A.P said:
    The channel is set to NRF_SAADC_INPUT_VDD before init and #define HAL_SAADC_VBAT_CHANNEL 0.

    Great, thank you for clarifying.
    The channel configuration seems all right to me, but you might not need the full 40 US ACQTIME. Not that it should matter in this case though, since you are not doing high-frequency sampling.

    A.P said:
    You're right. I copied this code from the example, and indeed we do not have a reverse current protection diode in place. Accordingly, we do not use this value in any of our calculations, it was placed there before we had decided whether to place a reverse current protection diode or not, and we had overlooked removing it from our code. Thank you for that point though, I will make a note of it in case we add a reverse battery protection diode to our PCB.
    A.P said:
    We are using a custom board, not the DK. No reverse current protection is in place.

    Thank you for confirming this. So it sounds like it has not caused any inaccurate readings, which is good. The reverse current protection in place on the DK's is there mainly due to the multiple POWER options with different voltage ranges the DK's have available.

    A.P said:
    I'm afraid I still have not been able to catch this raising battery values while having a debugger attached to the board. I'll try to give you the data as soon as possible. Just so I'm sure we're on the same page, you need the ADC readings as well as the battery% calculated from every sample. Is there anything else you'd need?

    I will take a look at it whenever I get the data, no worries at all! Since I know which linearization scheme you have then I primarily need the SAADC readings and the interval in which they are taken ( i.e if there is 10 min between each sample ).

    Best regards,
    Karl

Related