mp-se / gravitymon

iSpindle replacement firmware for beer brewing/fermenting
MIT License
59 stars 16 forks source link

ESP32S2 ADC problems #211

Open Levi--G opened 1 week ago

Levi--G commented 1 week ago

Is your feature request related to a problem? Please describe. On the ESP32S2 boards (im using wemos S2 mini) the adc value is expressed in 13-bits instead of 12 like the other esp32, so the calculation of the voltage is doubled out of the box making finding the voltage factor harder. Additionally i noticed it only measures up to 2.5V then it always outputs the same value, so the 470kOhm resistor is not enough for a ESP32S2, i think it should be closer to 220kOhm.

Describe the solution you'd like Is it possible to test this on one of your devices? Since i have only 1 available it might be a problem on my end.

Describe alternatives you've considered since SOC_ADC_MAX_BITWIDTH is 13 on the ESP32s2, maybe we can divide by (1 << SOC_ADC_MAX_BITWIDTH) -1 instead of the constant values? If this is correctly defined on the esp8266 that could remove the difference there too and provide better support for future esp boards.

Additional context

I am trying the esp32S2 now because i have no working esp8266 and want to continue testing the icm imu 😉

mp-se commented 1 week ago

Thats strange, according to the data sheets the s2 and s3 both have 12bit adc. But i have some that i can test with

Levi--G commented 1 week ago

I added

#elif defined(ESP32S2)
  _batteryLevel = ((3.3 / 8191) * v) * factor;
#else

and disabled the pulldown (cause it is misbehaving and making the voltage non-linear on the s2) And now with a 170kOhm (100k+68k) resistor to ground i get a very nice linear response with a voltage factor of 1.8

mp-se commented 1 week ago

Ok, i will do some tests tomorrow on the various boards

Levi--G commented 1 week ago

Small edit, out of some more testing this formula seemed more logical and the voltage factor is now back to being the actual divider of the resistors used:

#elif defined(ESP32S2)
  _batteryLevel = ((2.5 / 8191) * v) * factor;
#else

Disabling the INPUT_PULLDOWN to INPUT also really helped getting the analog voltage linear so i added:

#if defined(ESP8266) || defined(ESP32S2)
  pinMode(myConfig.getVoltagePin(), INPUT);
#else

If you get the same results i can add these to my branch and just include them in the ICM PR, since that seems to work perfectly now with the new esp 😄

mp-se commented 1 week ago

I have now verified on the c3/s2/s3 boards and I can confirm your finding. It seams that the default attenuation limits the measurements to 2.7V so I tested with 2 x 220k which should be a better to divide the value in half. This is the code I tested with, I set all the default params in case something changes. I has been 2 years since I wrote that code and I guess something has changed in arduino or idf since it worked then.

Since you found it would you like to do a PR ?

BatteryVoltage::BatteryVoltage() {
#if defined(ESP8266)
  pinMode(myConfig.getVoltagePin(), INPUT);
#else
  pinMode(myConfig.getVoltagePin(), INPUT);
  analogReadResolution(SOC_ADC_MAX_BITWIDTH);
  analogSetAttenuation(ADC_11db); // Max possible input is 2.7V
#endif
}

void BatteryVoltage::read() {
  float factor = myConfig.getVoltageFactor(); 
  int v = analogRead(myConfig.getVoltagePin());

#if defined(ESP8266)
  _batteryLevel = ((3.3 / 1023) * v) * factor;
#else  // defined (ESP32)
  _batteryLevel = ((3.3 / ((1 << SOC_ADC_MAX_BITWIDTH)-1)) * v) * factor;
#endif
}

These are the voltage factors that seams to work fine with those boards. (These go into the config.hpp file)

if defined(ESP8266)

return 1.59;  // 220k to Batt+

elif defined(ESP32C3)

return 1.70;  // 220k + 220k

elif defined(ESP32S2)

return 1.65; // 220k + 220k

elif defined(ESP32S3)

return 2.02;  // 220k + 220k

endif

}

mp-se commented 1 week ago

Perhaps we should use 2.7V for the calculation since that is the max value that can be measured ?

Levi--G commented 1 week ago

Perhaps we should use 2.7V for the calculation since that is the max value that can be measured ?

Did you get a max of 2.7V? On the S2 i get the same value from 2.5V onwards weirdly enough. The documentation also suggests 2.5V:

I think keeping within 2.5V seems better as some devices (like mine) will not function over 2.5V. To be on the safe side i even used a lower resistance to stay within 2.2V as there is plenty of resolution and this will mean the ADC is more linear in that region since the curve changes the closer you get to 2.5V.

There is also the issue that many ESP's have different internal reference voltages (Vref) which means some can read even less than 2.5V depending on silicon lottery luck. This has been described and even documented by espressif to be as much as 10% difference. So i would caution to pick a "safe" voltage. 2.5V - 0.25 = 2.25V which is why i went for 2.2V 😉 I think you just got lucky with a +8% Vref which further proves that there is a lot of difference.

I has been 2 years since I wrote that code and I guess something has changed in arduino or idf since it worked then.

A lot has changed, but i think the major difference is boards don't use the espressive modules anymore some of which had builtin resistors like the esp8266 does. So any of the boards with just the "chip" don't have resistors and are limited by the ADC max voltage of 2.5V.

Just for reference, here is a list of max voltages for the different chips if Vref = 0% deviation: ESP32: 2450mV ESP32-S2: 2500mV ESP32-S3: 3100mV ESP32-C3: 2500mV ESP32-C6: 3300mV Other ones i couldn't find easily and would need to search the datasheet

mp-se commented 1 week ago

I did a search for attenuation and found 2.7V but that must have been another board then... 2.2v seams to be a good level. I will find the factors for that then..

mp-se commented 1 week ago

I have updated the code with your suggestions, changed the documentation, tested the esp32 boards and updated the default factors. Thanks for spotting this.

Levi--G commented 1 week ago

My pleasure, i love the work you put into this.

I think there was a slight misunderstanding though, when i said 2.2V i meant that as the target voltage for the resistors, so every device could reliably measure it, not as the voltage to multiply by, that should be the specific voltage per chip to be accurate. Maybe it would be clearer if i show it in a PR?

mp-se commented 1 week ago

Ok, no problem i can adjust that.