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?

Reply
  • 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?

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

    Great! This is recommended practice.

    A.P said:
    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.

    Well, any peripheral that draws current at the time of measurement could offset your samples.
    For example as Turbo mentions, logging does exactly this. Another big source of error here could be if the radio was active at the time of sampling - this would most definitely offset your CR2032 measurements.

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

    I look forward to hearing if this resolves your issues, please let me know!

    A.P said:
    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?

    There is indeed a SAADC version 2 in the SDK 17, yes. This is nrfx v2.2. The main difference here is that the newest driver is easier to understand, more intuitive both in naming and function.
    Performance-wise it is the same, so it will not increase your accuracy.
    If you are already familiar with the nrfx_saadc v1 then you might just very well continue using it, but then I would also think that you will appreciate the improved naming of the updated version.
    Unfortunately there are no examples using the SAADC version 2 driver as of yet, sorry.

    Best regards,
    Karl

  • Hi Karl!

    I've added the two points we've discussed to my code-
    1. I now calibrate the SAADC upon initialization, which means on every wake-up, before the ADC measurement is performed.
    2. I've eliminated the possibility of measuring after advertising. I now measure only before advertising and before any peripherals draw current.

    I ran ~10 devices throughout the night and read the battery values every 10 minutes or so. The result is that I see much more stable values from my ADC reads, which is good news. One device does seem peculiar in that it seems to report an increase in power. The measurements are very consistent, but they rise by from ~40% to ~70%  in about 10 hours, then start dipping again.

    Here are the results, they are measured every ~10 minutes, the SAADC is calibrated before each measurement.

    Battery %
    37
    39
    38
    39
    39
    41
    40
    40
    40
    45
    43
    44
    43
    45
    46
    47
    49
    51
    51
    50
    52
    52
    48
    52
    51
    54
    56
    55
    55
    56
    58
    56
    53
    57
    55
    56
    56
    60
    60
    60
    60
    62
    62
    63
    58
    57
    60
    65
    63
    62
    60
    63
    65
    63
    57
    64
    62
    61
    65
    60
    65
    65
    65
    68
    60
    61
    67
    65
    63
    66
    65
    66
    67
    68
    68
    68
    72
    64
    68
    68
    70
    64
    63
    57
    60
    59

    The only thing I can think of is that during the night the air conditioner is turned off, so some change in temperature is to be expected. However, I don't expect the temperature to change by much. Lets say about 10 degrees C.

    Do you have any other ideas I could test?

  • I've observed the same behavior in other devices. They may take longer, or rise by a different amount, but a steady increase in battery over the span of a few hours, maybe days does occur in a few of my devices.

    Have I accidentally created a Perpetuum mobile device Slight smile?

  • Hello again,

    A.P said:
    Have I accidentally created a Perpetuum mobile device

    Now that would have been quite the marvel!
    Sadly, I fear that this is related to the SAADC configuration and usage, more so than a sure-fire Nobel physics prize. 
    While the output may not be Nobel-grade, it is still some interesting percentages you are seeing, with a particular device seemingly increasing in power.

    A.P said:
    The only thing I can think of is that during the night the air conditioner is turned off, so some change in temperature is to be expected. However, I don't expect the temperature to change by much. Lets say about 10 degrees C.

    Temperature is the biggest cause of offset error, but you say you calibrate it before every measurement, so that should be fine.
    Could you share with me the exact implementation of your battery discharge curve model?
    Perhaps could you also provide the raw SAADC readouts, so that I may see that fluctuation in the measurements directly.

    With your configuration I would expect some LSB fluctuation in your SAADC output - and if your linearized discharge curve is incorrect, it might be the source of these wild measurements ( i.e it is just coincidental that the measurements seems to be increasing ).

    Looking forward to getting to the bottom of this!

    Best regards,
    Karl

  • Hi Karl!

    Here is the linearization scheme as it is in our code, as well as the discharge curve for the batteries we use. I've yet to capture an increasing battery % while recording the SAADC readout, so I'll try and get that to you as soon as I catch it.

    Here is something to get things rolling

    Sener CR2032.pdf

    Linearization scheme:

    static __INLINE uint8_t battery_level_in_percent(const uint16_t mvolts)
    {
        uint8_t battery_level;
    
        if (mvolts >= 3000)
        {
            battery_level = 100;
        }
        else if (mvolts > 2900)
        {
            battery_level = 100 - ((3000 - mvolts) * 58) / 100;
        }
        else if (mvolts > 2740)
        {
            battery_level = 42 - ((2900 - mvolts) * 24) / 160;
        }
        else if (mvolts > 2440)
        {
            battery_level = 18 - ((2740 - mvolts) * 12) / 300;
        }
        else if (mvolts > 2100)
        {
            battery_level = 6 - ((2440 - mvolts) * 6) / 340;
        }
        else
        {
            battery_level = 0;
        }
    
        return battery_level;
    }

Related