adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.04k stars 1.19k forks source link

MagTag battery monitor is not returning expected values #3785

Closed dhalbert closed 3 years ago

dhalbert commented 3 years ago

There appears to be an issue with ADC calibration or operation. (Reported by @ladyada)

anecdata commented 3 years ago

I think this should be 3.3, not 2.6: https://github.com/adafruit/Adafruit_CircuitPython_MagTag/blob/31c664cc2f5fa0d4b81067a9e64ecf0003e3c16e/adafruit_magtag/peripherals.py#L86

I don't have a MagTag, but AnalogIn measurements match multimeter on Saola and FeatherS2 (6.1.0-beta.1), with @hierophect's caveat noted in #3501 - the Espressif docs indicating full range analog reads from 0-3.3V at 11dB seem to be incorrect (at this time).

makermelissa commented 3 years ago

I got the numbers from some code I got from @ladyada, but hadn't done any verifications. So it's possible the numbers may need to be adjusted in the MagTag library.

dhalbert commented 3 years ago

My impression was that the ADC might not be linear, and that the range might be limited, but I haven't started testing yet. I think I read that previously.

tannewt commented 3 years ago

@hierophect Will have context on this.

anecdata commented 3 years ago

Should be linear enough between 150 ~ 2600 mV, which covers the range for 50% of LiPo voltage. AnalogIn does apply calibration based on device-specific data in efuse.

But whereas docs say we should be able to measure voltages from 0-3.3v at 11dB attenuation, I can't get quite to zero, or above about 2.7v, with the current build.

ladyada commented 3 years ago

we divide by 2 fyi - so you should have full range

hierophect commented 3 years ago

What specifically is wrong with the voltage in testing? There are a number of caveats to the S2 ADC readings.

The relevant section to the voltage limits problem is here. When I asked Espressif about this issue, they confirmed that their voltage readings capped out at 2.6V. The section also notes:

At 11 dB attenuation the maximum voltage is limited by VDDA, not the full scale voltage.

So I guess this could vary depending on whether the VDDA implemented at the module or client PCB level. @anecdata I don't see anywhere in the docs that indicates it the ESP32S2 will measure anything below 100mV, that's been an issue on the ESP32 as well, and it's noted in the above function docs.

The ESP_ADC_CAL component is supposed to fix some of this, but the implementation for the S2 is less fully implemented than for the ESP32. I've looked at the latest master for the esp_adc_cal component and they haven't added any additional features, so we aren't missing anything from a recent update either.

hierophect commented 3 years ago

You can see some examples of the voltage curves on the ESP32 here. The ESP32 IDF implementation actually has the LUT lookup suggested by posters in that thread, that maps ADC readings to better values for nonlinear regions (though I'm not sure how much that can fix the low-end limit). But the ESP32 S2 doesn't have this implemented yet.

anecdata commented 3 years ago

I can measure 0.000v-3.299v (calibrated) on ADC1 on the ESP32 (using CircuitPython!), so I think Espressif has solved it there.

But for 1/2 of the battery voltage, we are well within the recommended linear range on the ESP32-S2.

I may be missing something, but is the library calculation correct (see my first comment)? analogin returns 0-65535 value for voltage relative to 3.3v if I'm reading it right, so the library should multiply by 3.3 (rather than 2.6) to get back to a voltage?

hierophect commented 3 years ago

ADC1 on the ESP32 (using CircuitPython!)

???

I see what you mean in your comment now, though. Yes, I had it multiply by 3.3 to align with the ADC readings of other Circuitpython boards and the documentation, even if it can't get all the way up to the max at whatever VDDA we're supplying. So you're right, that later adjustment should be 3.3, not 2.6.

ladyada commented 3 years ago

@dhalbert i havent tried this for a while, please see if you get good numbers now

anecdata commented 3 years ago

@hierophect We have analogin in ESP32SPI-NINA :-)

dhalbert commented 3 years ago

@ladyada will do, and I'll also see about adding the calibration code to the -S2 implementation.

hierophect commented 3 years ago

@dhalbert we already use the calibration code that's implemented in the IDF, which is just the basic calibration curve without an LUT. If we want our own LUT, we'll need to decide how to generate it.

dhalbert commented 3 years ago

I changed the multiplier, and the battery voltage is closer, but is reading about 0.15v low. I tried at 4.2, 3.7, and 3.35v, which are the variable values available from the Monsoon power monitor. I double-checked that the Monsoon is accurate with an external voltmeter.

I have not looked at the code yet.

hierophect commented 3 years ago

It's suspicious to me that the voltage curve starts at 150mV and is also off by that amount. Maybe the calibration curve offset isn't being applied properly?

dhalbert commented 3 years ago

I did some instrumentation of esp_adc_cal_esp32s2.c:

I haven't read all the support issues and surrounding doc yet.

dhalbert commented 3 years ago

On a Metro ESP32-S2, I fed its DAC to the ADC. I measured the voltage with the ADC, and also with a good multimeter and an ADS1115. I don't see a 150mV difference. The readers differ only by only a few mV, often only 3 or so. I did a similar test with a 1.5V battery on a MagTag, reading its voltage on D10 and also on a multimeter, with a similar minimal difference, certainly not 150mV

I'm thinking any issues with the voltage reading may have to do with the voltage divider and the capacitor. image AnalogIn does 64x multisampling, so even though the capacitor helps hold the voltage during sampling, it may be sagging slightly.

dhalbert commented 3 years ago

I changed the multiplier, and the battery voltage is closer, but is reading about 0.15v low. I tried at 4.2, 3.7, and 3.35v, which are the variable values available from the Monsoon power monitor. I double-checked that the Monsoon is accurate with an external voltmeter.

I repeated these measurements in situ with a battery, and I don't see this 150mV offset. I'm not quite sure why I saw it the first time with the Monsoon, but I don't see any offset with the battery and a voltmeter, and the corrected library (https://github.com/adafruit/Adafruit_CircuitPython_MagTag/pull/33), which now uses the proper 3.3V multipler. So I'm going to close this for now.

ladyada commented 3 years ago

hmm ok!

kevinjwalters commented 3 years ago

I just noticed this too while testing an Enviro+ FeatherWing. The voltage is read from its 3 gas sensors with 3 analogue inputs. The value differs to the one shown with a Feather nRF52840 Express hence my investigation. I was unaware of this 2.63V limit.

dhalbert commented 3 years ago

@kevinjwalters If you are seeing differing measurements below the 2.63V limit compared with another board, go ahead and open an issue with some example code and measurements. Thanks.

kevinjwalters commented 3 years ago

@dhalbert I've left the FeatherS2 side by side with an nRF52840. I may have a more accurate ADC somewhere, I'll rummage.

Is the ESP32-S2 behaving according to its documentation? It sounds like with -11dB to input signal it should be viable to read up to 3.3V but that's clearly not the case at the moment. Do Expressif have any sample code for reading 3.3V, I've not found any yet.

@anecdata Are you saying the ESP32 and ESP32-S2 differ here?

kevinjwalters commented 3 years ago

I think I've picked two inputs on the pair of ADCs and curiously there are different ceiling values coming out of these:

>>> a1v = a1.value ; d5a10v = d5a10.value ; print(a1v, bin(a1v)) ; print(d5a10v, bin(d5a10v))
52149 0b1100101110110101
51574 0b1100100101110110

BTW, is there a reason why the width is not set for ADC2?

https://github.com/adafruit/circuitpython/blob/f15e2d86c0fb3235a9934b74d22a0d70ea2b4f4c/ports/esp32s2/common-hal/analogio/AnalogIn.c#L66-L71

Ah, that's just way it is, according to general ESP32 docs: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html#adc-rtc-mode

anecdata commented 3 years ago

ESP32-S2 width is enforced to 13 bits. As far as I can tell, latest Espressif docs indicate ADC should be able to handle up to 3.3v (linearity may well suffer above 2.6v, even with the calibration). We either need to demonstrate 3.3v in another IDE (or demonstrate that it is similarly limited, and file an issue with Espressif), or ask someone at Espressif.

dglaude commented 3 years ago

Wrote a little piece of code that read on A0 the voltage written on A1:

import board
import time
from analogio import AnalogOut, AnalogIn
analog_in = AnalogIn(board.A1)
analog_out = AnalogOut(board.A0)
while True:
    for i in range(0, 65535, 512):
        write = i
        analog_out.value = write
        time.sleep(0.02)
        read=analog_in.value
        print((write, read))

It show something linear until it reach a plateau:

(0, 1886) (512, 2124) (1024, 2561) ... (54784, 53758) (55296, 54056) (55808, 54413) (56320, 54413) ... (64512, 54413) (65024, 54413)

kevinjwalters commented 3 years ago

@dglaude Interesting, your FeatherS2 can "reach" higher (2.74V) than mine (2.63V):

(0, 2303)
(512, 2621)
(1024, 3038)
...
(53248, 51534)
(53760, 52010)
(54272, 52149)
(54784, 52149)
...
(64512, 52149)
(65024, 52149)
hierophect commented 3 years ago

I'm not sure what the new issue is here - I confirmed with Espressif that the S2 cannot achieve readings above around 2.6V, so I think that's that. The ESP32 and ESP32S2 do differ in this regard, the original ESP32 has a more sophisticated calibration process including a lookup table (LUT) and the ESP32S2 does not. You could use a voltage divider and diode offset to put your measured voltages in a better range, if necessary.

anecdata commented 3 years ago

I think the issue is that the Espressif docs are either: wrong, or; anticipating further development to get the ESP32-S2 to comparable ADC behavior as the ESP32. Espressif should go on record with an answer.

hierophect commented 3 years ago

@anecdata where in the docs do you see the claim that Analog readings should be able to go to 3.3V? I'm going over the page and they say that it's 2.6V everywhere that I can find https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/adc.html

anecdata commented 3 years ago

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html#_CPPv425adc1_config_channel_atten14adc1_channel_t11adc_atten_t

When the analog voltage supply (VDDA) is 3.3 V: ... 11 dB attenuation (ADC_ATTEN_DB_11) gives full-scale voltage 3.9 V (see note below) ... Note At 11 dB attenuation the maximum voltage is limited by VDDA, not the full scale voltage.

hierophect commented 3 years ago

Yeah I had also just landed on that. I think that this also doesn't imply that 3.3V should be possible, it's just written in a confusing way. I think it means that the maximum value of the integer value would correspond to 3.9V, as in the integer max of ~65535~ (edit, the 12 bit max of 4095), but it will platau before ever actually hitting that value. Correspondingly, the maximum voltage is limited by VDDA, but that doesn't necessarily mean it is VDDA. Definitely some word salad going on there.

The header functions above it, however, all refer to a max reading of 2.6V:

ADC_ATTEN_DB_11 = 3 The input voltage of ADC will be attenuated, extending the range of measurement to up to approx. 2600 mV.

dhalbert commented 3 years ago

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html#_CPPv425adc1_config_channel_atten14adc1_channel_t11adc_atten_t

Note that's the ESP32, not ESP32-S2 docs.

hierophect commented 3 years ago

@dhalbert it's actually pretty obviously copy-pasted between the two, the ESP32S2 one is the same: https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/adc.html#_CPPv425adc1_config_channel_atten14adc1_channel_t11adc_atten_t

Again, I think they did that because it doesn't technically say what the actual maximums are. But this whole thing could absolutely be a lot more clear. They should really specify outright what the maximum voltages are at 3.3V VDDA and what linearity issues they have / how those are compensated for in the calibration process.

anecdata commented 3 years ago

sorry, wrong link, esp32-s2 reads the same https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/adc.html#_CPPv425adc1_config_channel_atten14adc1_channel_t11adc_atten_t

dhalbert commented 3 years ago

Ah, sorry, I should have checked the other doc.

anecdata commented 3 years ago

The odd thing to me, helped along by confusing docs, is that people coming from ESP32 could read the docs and expect 3.3v just like ESP32.

hierophect commented 3 years ago

@anecdata I agree. ESP32 says that the maximum "recommended" voltage is ~2.6mV, but can go above that, with caveats. ESP32S2 says the same thing but apparently cannot go above it at all.

kevinjwalters commented 3 years ago

@hierophect Is the statement from Espressif available somewhere for viewing? It seems odd they cripple the ESP32-S2 and make it incompatible with ESP32, did they say why they did this? And why are they referring to it as -11dB (i.e. 3.90V -> 1.10V) if it's not?

If you have a ticket open with them perhaps it's worth askign them to polish/clarify their documentation?

hierophect commented 3 years ago

Perhaps that with a higher VDDA value than 3.3V, a higher voltage would be possible? But I don't know if that's even allowed by the voltage specifications.

anecdata commented 3 years ago

It is a documentation issue, at least for now. ESP32 docs read the same, but with VDDA at 3.3v, you can still get 3.3v ADC read on ESP32. If this is permanent, the fix is easy for them. If it's a future feature to support full-range, you'd think they could indicate that.

hierophect commented 3 years ago

@kevinjwalters It was an email chain, and I would hesitate to call it a statement, just that the engineer on the other end confirmed that the maximum value they could obtain from the device was 2.6V. It was an old ticket, from back last fall when I was implementing this.

Again, I don't think they're actually doing anything duplicitous here. The header functions do state that the maximum attainable voltage is 2.6V, and the 11dB measurement is on a scale up to 3.9V, it just doesn't actually hit every value in that range.

So, on the ESP32, you can actually hit a full 3.3V reliably, using these same functions? I just checked out the ESP32 page, and it also claims to cap out at 2.6V. If you're obtaining higher voltages than that, it might actually be considered to be undocumented/unofficial functionality.

hierophect commented 3 years ago

In any case, there's a discrepancy here that should be laid out in more detail. I'll open a new ticket with them and see if I can clear any of this up.

anecdata commented 3 years ago

@hierophect just for completeness on your ESP32 question... The documentation has evolved a bit over time in how they describe analog range in various sections, and looking at code history it looks like calibration functions evolved as well. I ran the same ADC1 example as above on a Feather ESP32 with current IDF, and it has a range of 142mV-3124mV (calibrated voltage from esp_adc_cal_raw_to_voltage(); raw reading at the high end is 4095).

Most ESP32-S2 chips should have eFuse Two Point values, but we may still expect some chip-to-chip differences.

Hopefully Espressif can confirm the supported ADC voltage range (linearity aside), and align docs with that.

kevinjwalters commented 3 years ago

@anecdata Does it plateau at top end or is 3124mV actually equivalent to 3.300V?

anecdata commented 3 years ago

@kevinjwalters Calibrated ADC read tops out at 3124mV on that ESP32 with current ESP-IDF. That corresponds to raw 12-bit reading of 4095. Meter-measured voltage is 3.3v.

(I think we might expect more chip-to-chip differences on ESP32 since there are more possibilities for eFuse calibration data types)

kevinjwalters commented 3 years ago

@anecdata But does metered 3.20V and 3.25V also correspond to ACD reading of 3124mV?

anecdata commented 3 years ago

Yes, once the raw ADC bits are maxxed out, the ADC calibrated voltage gets no higher (e.g., 3124mV), regardless of increased voltage applied and read on meter.

In other words, meter reads near 3124mV when raw ADC is at 4095 (and calibrated voltage is at 3124mV) - meter then measures higher as voltage is increased, but ADC bits and calibrated voltage read are already maxxed out and don't change.

All actual meter-read voltages between 3124mV-3300mV are read by the ADC on that ESP32 as 4095 raw / 3124mV calibrated.

I re-did the test of the ESP32-S2, reporting raw and calibrated, and it's the same. All actual voltages above 2630mV register by the ADC as 8191 raw / 2630mV calibrated on that ESP32-S2.

hierophect commented 3 years ago

@anecdata - I still don't understand, you're saying that the ADC actually reaches the maximum value of the bit range (0xFFF, 12 1s in binary) and it corresponds to 3124mV? I'm extremely confused by that, the full scale voltage is supposed to be 3.9V, and my raw returns never approach the full scale value, that's what I've meant by referring to the plateau. I don't see how that would correlate to the documentation in any of the interpretations we've discussed here. Can you elaborate?

anecdata commented 3 years ago

What I see, using the Espressif ADC example, on ESP32 and ESP32-S2, is that the 12- or 13-bit ADC reading (64x multisampled) hits the maximum value (4095 or 8191) at some voltage well under 3.3V. Similarly, the calibrated value from esp_adc_cal_raw_to_voltage() also hits its maximum value at that point. At that point, calibrated value roughly equals the meter-measured voltage. But, increasing the applied voltage cannot increase the raw value because all bits are 1, and it does not increase the reported calibrated value.

Example 1: potentiometer turned so that applied voltage measured by meter is 3.1V (ESP32). The raw ADC reading is 4095, and the calibrated voltage is reported at ~3.1V.

Example 2: ...now the potentiometer is slowly raised up to a meter reading of 3.3V. The raw ADC value can't change b/c it's at its maximum value. The calibrated voltage is reported still at ~3.1V.

I think the 3.9V is a red herring and just a mathematical artifact. The datasheet says max VDDA is 3.6V, and in reality for us at least it's 3.3V.

As far as I can tell, circuitpython AnalogIn is implemented like the example, and behaves like the example.