adafruit / Adafruit_CircuitPython_LC709203F

Library for I2C LC709203F battery status and fuel gauge
MIT License
4 stars 10 forks source link

add retries and delays to work around ESP32-S3 problems #21

Closed dhalbert closed 1 year ago

dhalbert commented 1 year ago

ESP32-S3 has some issues with the clock-stretching and sleeping done by the LC709203F. See https://github.com/adafruit/circuitpython/issues/6311.

Tested with this program. Note the try-except which catches OSError. There can still be occasional failures, so it's good to catch and retry.

import time
import board
import busio
from adafruit_lc709203f import LC709203F, PackSize

uart = board.UART()
battery_monitor = LC709203F(board.I2C())
battery_monitor.pack_size = PackSize.MAH400

while True:
    try:
        print("Battery Percent: {:.2f} %".format(battery_monitor.cell_percent))
        print("Battery Voltage: {:.2f} V".format(battery_monitor.cell_voltage))

        uart.write(b"Battery Percent: {:.2f} %\r\n".format(battery_monitor.cell_percent))
        uart.write(b"Battery Voltage: {:.2f} V\r\n".format(battery_monitor.cell_voltage))
    except OSError:
        print("retrying reads")

    time.sleep(2)

@Gingertrout @BeatArnet @mopore @ThomasAtBBTF Could you try this changed version and see if it works for you? Thank you. You just need to copy the adafruit_lc709203f.py file in this pull request to your CIRCUITPY drive, which will override what is in your lib/. You should be able to remove the i2c.scan() code from your existing programs, since this does approximately the same thing.

New code is available at https://github.com/adafruit/Adafruit_CircuitPython_LC709203F/blob/1007e4c96bc465204de0cb5a3684022e9e0fb71b/adafruit_lc709203f.py

BeatArnet commented 1 year ago

After downloading the latest version of the adafruit_lc709203f.mpy library and running your sample code on my feather_s3_tft I got the following errors:

%Run -c $EDITOR_CONTENT Zurückverfolgung (jüngste Aufforderung zuletzt): Datei "", Zeile 7, in Datei "adafruit_lc709203f.py", Zeile 132, in init Datei "adafruit_lc709203f.py", Zeile 136, in init_RSOC Datei "adafruit_lc709203f.py", Zeile 257, in _write_word OSError: [Errno 116] ETIMEDOUT

dhalbert commented 1 year ago

@BeatArnet The proposed fix is not yet available as .mpy. Please try: https://github.com/adafruit/Adafruit_CircuitPython_LC709203F/blob/1007e4c96bc465204de0cb5a3684022e9e0fb71b/adafruit_lc709203f.py

BeatArnet commented 1 year ago

Unfortunately, after some impressive results…

Battery Percent: 100.00 %

Battery Voltage: 4.19 V

Battery Percent: 100.00 %

Battery Voltage: 4.19 V

Battery Percent: 100.00 %

Battery Voltage: 4.19 V

Battery Percent: 100.00 %

Battery Voltage: 4.19 V

Battery Percent: 100.00 %

Battery Voltage: 4.19 V

Battery Percent: 100.00 %

Battery Voltage: 4.19 V

Battery Percent: 100.00 %

Battery Voltage: 4.19 V

Battery Percent: 100.00 %

Battery Voltage: 4.19 V

retrying reads

retrying reads

retrying reads

retrying reads

retrying reads

retrying reads

retrying reads

retrying reads

retrying reads

retrying reads

retrying reads

retrying reads

retrying reads

retrying reads

retrying reads

retrying reads

retrying reads

retrying reads

retrying reads

retrying reads
dhalbert commented 1 year ago

@BeatArnet hmm, I saw a few missing reads, but not continuous as you did :slightly_frowning_face: . Are you using exactly the same test program, and which board are you testing on?

BeatArnet commented 1 year ago

I used your sample-code and the latest library *.py. I have an Adafruit S3 TFT board.

tekktrik commented 1 year ago

I'll defer to @dhalbert but I agree with you.

Also, I didn't fully realize until now that CircuitPython implemented the for/else combination, cool! I think this is the first time I've actually seen it in action.

dhalbert commented 1 year ago

@FoamyGuy @tekktrik I did not merge it because @BeatArnet still had trouble. But it may be particular samples. So ok, let's merge it, and it will fix the problem at least some of the time.

BeatArnet commented 1 year ago

I could solve the problems, using the following code:

# The following code for scanning the I2C addresses is necessary so that the voltage can be measured
BATTERY_MON_ADDRESS = 0x0B  # Address for ESP32-s3 tft Feather

def hack_for_i2c_issue():
    i2c = board.I2C()
    while not i2c.try_lock():
        pass
    running = True
    try:
        while running:
            print("I2C addresses found:", 
                [hex(device_address) for device_address in i2c.scan()]
            )
            # time.sleep(2)
            running = False
        return i2c
    finally:  # unlock the i2c bus when ctrl-c'ing out of the loop
        i2c.unlock()

try:
    print("Searching for battery monitor...")

    i2c = hack_for_i2c_issue()
    battery_monitor = LC709203F(i2c)

For me, the problem is solved with the above hack.

dhalbert commented 1 year ago

@BeatArnet The code I added is similar to your hack, but only tries three times to wake up the LC709203F. However, if it doesn't succeed, it will throw an exception. So I don't know why your eventually gets read failures. Perhaps the sensor is going back to sleep in a scenario I hadn't envisioned. We could retry reads a few times as well.