adafruit / adafruit/Adafruit_CircuitPython_BMP3XX
Potential to Infinite-Loop On Bus Errors
- Dominant language
- Python
- Stars
- 7
- Forks
- 15
- PR merge metrics
- No merged PRs in 30d
Description
We are developing a device that has potentially high capacitance on the I2C bus due to the number of sensors. While the most optimal solution is to resolve this issue using passive or active pull-ups, we have found that most sensors are able to tolerate and recover from I2C bus instability within some `Exception`s thrown and handled in software.
The BMP3XX as driven by this code is an exception, [due to this potential infinite loop](https://github.com/adafruit/Adafruit_CircuitPython_BMP3XX/blob/d4b01a2983eacaddf1700ab36984e33868e50542/adafruit_bmp3xx.py#L147).
By monkeypatching the `_read` function to time out and throw an exception after 5 seconds, we are able to use the Adafruit BMP3XX driver on an 11nF I2C bus with only a 4k passive pull-up.
I understand if you view this as a case of, "software is the wrong place to fix this issue," but I would suggest that timing out the `_read` operation is strictly better behavior than infinite-looping on a bus error.
Below is how we patched it:
```
def patch_bmp3xx_read(self) => Tuple[float, float]:
"""Returns a tuple for temperature and pressure."""
# OK, pylint. This one is all kinds of stuff you shouldn't worry about.
# pylint: disable=invalid-name, too-many-locals
# Perform one measurement in forced mode
self._write_register_byte(_REGISTER_CONTROL, 0x13)
# Wait for *both* conversions to complete
max_wait_time = 5
wait_time = 0
while self._read_byte(_REGISTER_STATUS) & 0x60 != 0x60 and wait_time < max_wait_time:
wait_time += self._wait_time
time.sleep(self._wait_time)
logger.info("Waited for {}s".format(wait_time))
if wait_time >= max_wait_time:
logger.info("Timed out waiting for data in BMP3XX")
raise Exception("Timed out waiting for data in BMP3XX")
# Get ADC values
data = self._read_register(_REGISTER_PRESSUREDATA, 6)
adc_p = data[2] << 16 | data[1] << 8 | data[0]
adc_t = data[5] << 16 | data[4] << 8 | data[3]
# datasheet, sec 9.2 Temperature compensation
T1, T2, T3 = self._temp_calib
pd1 = adc_t - T1
pd2 = pd1 * T2
temperature = pd2 + (pd1 * pd1) * T3
# datasheet, sec 9.3 Pressure compensation
P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11 = self._pressure_calib
pd1 = P6 * temperature
pd2 = P7 * temperature**2.0
pd3 = P8 * temperature**3.0
po1 = P5 + pd1 + pd2 + pd3
pd1 = P2 * temperature
pd2 = P3 * temperature**2.0
pd3 = P4 * temperature**3.0
po2 = adc_p * (P1 + pd1 + pd2 + pd3)
pd1 = adc_p**2.0
pd2 = P9 + P10 * temperature
pd3 = pd1 * pd2
pd4 = pd3 + P11 * adc_p**3.0
pressure = po1 + po2 + pd4
# pressure in hPa, temperature in deg C
return pressure, temperature
```
Contributor guide
No contributing guide indexed for this repository
Research direction
Start in adafruit_bmp3xx.py at the _read loop around line 147 and inspect how measurement completion is polled. Confirm the desired timeout behavior from the issue and verify that a bus error no longer causes an infinite loop; the issue does not name a test to run.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- embedded-iot
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100