LoPy ADC is not accurate
-
Has anybody successfully used the machine.ADC function to read analog input voltages such as Battery Voltages?
I have a LoPy on an expansion 2 board. It is currently connected and powered through a MicroUSB with no Battery Connection. A Battery Voltage sense line is supplied through a 115k-56k resistor divider to P16 of the LoPy. With no Battery connected and 5 volts being supplied by the MicroUSB port the Voltage at P16 is measured to be 1.305 volts which is accurate based on the 5 volts and the 115k-56k resistor divider network.
Using the following code and I get a consistent 1018 voltage value back and it should be 1305.
import machine
adc = machine.ADC()
BatterySensorPin = adc.channel(pin='P16')
BatterySensorPinVoltage = BatterySensorPin.voltage()
Print (BatterySensorPinVoltage)
1018Adding an ATTN value I get random readings some close to the correct value and other reading way off.
import machine
adc = machine.ADC()
BatterySensorPin = adc.channel(pin='P16', attn=machine.ADC.ATTN_6DB)
BatterySensorPinVoltage = BatterySensorPin.voltage()
Print (BatterySensorPinVoltage)
1309
Print (BatterySensorPinVoltage)
1205
Print (BatterySensorPinVoltage)
1323Has anybody seen this or understand what is going on? Is there accurate documentation or errata on the ADC function and it use with either Gpy or LoPy?
-
Thank you for the feedback on the ESP32 ADC challenges. It helps greatly.
In dealing with the ADC Class function I have the following questions;
I have 2 prototype designs, 1 based on LoPy and the other on Gpy.
On the Gpy design I am trying to read the Battery Voltage (range between 5.5v and 3.3v) through a 1M ohm and 47K ohm resistor divider connected to ADC1_CH7 (GPIO35 ESP32 pin 11).Can I use the ADC Class Function to read that channel? My code would be;
adc = machine.ADC()
BatterySensorPin = adc.channel(pin='P11', attn=machine.ADC.ATTN_11DB)
BatterySensorPinVoltage = BatterySensorPin.voltage()My concern is that when I read the ADC Class documentation it says valid pins are 13-20. Is that a documentation error? If that is correct how is the other available ADC pins read? As an example, in my case how would I read ADC1_CH7 (GPIO35 ESP32 pin 11)?
-
@guyc In simple words: the quality of the ESP32 ADC is lousy. It is discussed a lot in the espressif forum, and here is a nice analysis: https://github.com/bboser/IoT49/blob/master/doc/analog_io.md. The raw resolution is about 8-9 bits. If you need precise values, use an external ADC.
You can improve the internal ADC readings by:- adding a small ceramic capacitor, like 10 nF, between the ADC input and GND.
- use digital filtering, e.g. by averaging as suggested by @kjm or by a lowpass filter. The latter needs less memory - 1 value for each order plus the filter constants.
-
The ESP32 adc needs some TLC to get decent results:
- It helps to over sample. By experiment I settled on 32 lots of 128 readings which gives me 10 bit resolution (just!)
def _adc(ch, offset, scale): try: offset=pycom.nvs_get(offset)/1000; scale=pycom.nvs_get(scale)/1000 av2=0 for i in range(32): av1=0 for i in range(128): val=adc.channel(pin=ch, attn=adc.ATTN_11DB); c=val.voltage(); av1+=c av=av1/128; av2+=av Vraw=round(av2/32000, 4); Voffset=Vraw+offset; Vscale=round(Voffset*scale, 4) # round sometimes fails to round print('Vraw =', Vraw, ' Voffset =', Voffset, ' Vscale =', Vscale); return Vscale except Exception as e: sys.print_exception(e)
-
You can get offset by grounding the business end of your adc input resistor adc--100k--gnd & reading what the adc thinks 0v is
-
Then you fudge it with scale to get the reading you want!
-
Then check it over the full range of your battery voltage to check for consistency.
-
I've found the gpy adc is linear over input voltages 0.4 - 2.5v so choose your voltage divider so battery voltage fits into that range.