Closed ixt closed 7 months ago
The esp adc calibration struct/interface I added was, at the time, already slated to be deprecated in the next version of the IDF and I'm certain that has come to pass already.
It should probably be redone with the new interfaces that IDF provides rather than try to shoehorn the workaround into using the deprecated stuff.
Espressif provides an example how to read the ADC, maybe this is a good starting point for adopting the new API:
https://github.com/espressif/esp-idf/tree/master/examples/peripherals/adc/oneshot_read
Espressif provides an example how to read the ADC, maybe this is a good starting point for adopting the new API: https://github.com/espressif/esp-idf/tree/master/examples/peripherals/adc/oneshot_read
To my untrained eye, it doesn't look to me like the new API is available in the environment provided by Heltec for these boards just yet. Can anyone double check this?
has to be read via the RTC due to Wifi-related issues in IDF (If i'm understanding correctly).
As far as I'm aware, this means that WiFi must be powered off to read from ADC2. I don't know how frequently the battery level needs to be checked; is it an option to briefly power WiFi off to make the reading?
The schematic doesn't make obvious which pin we should be reading from, but its either 19 or 20.
VBAT is connected to ADC_IN (GPIO20) through a voltage divider, and a P-Channel MOSFET. The gate is ADC_CTRL, which is connected to ADC_CTRL (GPIO19). When GPIO19 is set LOW, ADC_IN is at roughly 50% of VBAT.
Admittedly, I've never used the ADC on an ESP32 before now, but I had no trouble reading ADC_IN using Arduino's analogReadMilliVolts()
just now.
#include <Arduino.h>
#define PIN_ADC_CTRL 19
#define PIN_ADC_IN 20
void setup() {
Serial.begin(9600);
pinMode(PIN_ADC_CTRL, OUTPUT);
digitalWrite(PIN_ADC_CTRL, LOW); // Unneeded, pins default to LOW (?)
}
void loop() {
const uint8_t repeats = 10;
uint32_t millivolts = 0;
// Multiple readings
for (uint8_t i = 0; i < repeats; i++) {
millivolts += analogReadMilliVolts(PIN_ADC_IN);
delay(100);
}
// Average the readings
millivolts /= repeats;
// Adjust for voltage divider: roughly 50% drop
millivolts *= 2;
Serial.print(millivolts);
Serial.println("mV");
delay(2000);
}
https://github.com/meshtastic/firmware/assets/24772776/2de176f2-09e2-4398-a845-749bd042d7f0
A bit more sniffing around and it sounds like it might not be necessary to power-off the WiFi
ADC can be used together with Wi-Fi, although usage by Wi-Fi has priority over the usage by software
An older example for reading ADC2 suggests that reading with adc2_get_raw()
will return a status which can be used to check if WiFi interfered with the ADC sample.
Apologies, looking closer at the power.cpp code I can see that I definitely went on a bit of a tangent there, explaining things that have clearly been thought of and dealt with before.
Okay, some success:
https://github.com/meshtastic/firmware/assets/24772776/0eb34215-ea37-4501-a3ef-493faaca5010
A few parameters need adjusting in variant.h
#define ADC_CTRL 19
#define BATTERY_PIN 20
#define ADC_CHANNEL ADC2_GPIO20_CHANNEL
#define ADC_MULTIPLIER 2 // Voltage divider is roughly 1:1
#define BAT_MEASURE_ADC_UNIT 2 // Use ADC2
#define ADC_ATTENUATION ADC_ATTEN_DB_11 // Voltage divider output too high
ADC_CTRL needs to be implemented somewhere in Power.cpp
The "ADC2 Wifi bug workaround" is not compatible with ESP32S3, even with the new register names. Setting these registers causes values from adc2_get_raw()
to be multiplied by 2. These elevated readings were then triggering this assertion
ESP32S3 doesn't seem to suffer from the same issue as ESP32: supposedly, WiFi and ADC2 can both be used, however the ADC reading can theoretically fail if WiFi is currently transmitting / receiving? By checking the esp_err_t
returned by adc2_get_raw()
this can be handled one way or another:
for (int i = 0; i < BATTERY_SENSE_SAMPLES;) {
esp_err_t read_result = adc2_get_raw(adc_channel, ADC_WIDTH_BIT_12, &adc_buf);
if (read_result == ESP_OK) {
raw += adc_buf;
i++;
}
else {
LOG_DEBUG("ADC read failed, retrying\n");
}
}
In the few minutes I spent testing, I didn't see any failed readings.
Are there any other ESP32S boards using ADC2 right now, which need to be considered / avoided? I can probably look into writing a fix in the next few days, unless anyone else wants to work on it, or thinks we should hold off for now.
Although VBAT will read correctly, it seems that there are still some issues with power (detecting USB, & charging):
DEBUG | 15:34:21 91 [Power] Battery: usbPower=0, isCharging=0, batMv=3870, batPct=64
I also observed that usbPower
went true
after waking from sleep, even when powered from battery. If nobody gets to it first, I'll open this as a new issue in the next day or so; It is late here now.
Category
Hardware Compatibility
Hardware
Heltec Wireless Paper
Firmware Version
2.2.17+
Description
Currently Wireless Paper (Assuming both versions, although only tested v1.1) does not display battery power, this is due to the battery sensing pin being on ADC2 and incorrectly stated.
ADC2 doesn't by default like being read from ESP32 or ESP32s3, and has to be read via the RTC due to Wifi-related issues in IDF (If i'm understanding correctly).
The schematic doesn't make obvious which pin we should be reading from, but its either 19 or 20. In the Heltech v2 boards we use a workaround that sets up the registers (SENS_SAR_READ_CTRL2_REG). But on ESP32s3 they are renamed to SENS_SAR_READER2_CTRL_REG. Making those updates however doesn't get me a working solution. I dont quite understand the esp_adc_cal code yet, so not sure which knobs to turn to help it along, but I think I've found the right tree to bark up...
I imagine this would be an issue on any other boards that happen to place the battery sensing on the ADC2 on ESP32/s3/c3. So any Heltec folks listening (if they are here), for v1.2 move the battery sensing to a pin with ADC1, I know thats a ridiculous ask but it does seem like even if we get this working it'll probably be unreliable.
I tried finding other examples of people using ADC2 on a ESP32s3, but I don't feel convinced anyone has managed to do it from scratch as most of the instances I could find where just ESP32 examples with commented out parts that would have given compile issues.
It's possible I missed some so it's worth searching anyway. Attached is a log of the assert fail I get from the loop.
Again, not my area at all so might have just missed something obvious that someone in the area might know. Don't mistake my depth for subject knowledge lol
I also changed the battery pin stuff to this
The ADC_MULTIPLIER I adjusted through a range of amounts and same with the Battery and ADC pins.
ESP32s3 technical reference
Relevant log output