Skip to content

Commit 55da258

Browse files
Document BatteryManager
1 parent 5381310 commit 55da258

File tree

2 files changed

+284
-0
lines changed

2 files changed

+284
-0
lines changed

docs/frameworks/battery-manager.md

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
# BatteryManager
2+
3+
The `BatteryManager` provides an Android-inspired API for querying battery and power information. It handles ADC readings, voltage calculations, percentage calculations, and intelligent caching with WiFi coordination on ESP32-S3.
4+
5+
## Overview
6+
7+
`BatteryManager` is a static class that provides direct access to battery voltage, charge percentage, and raw ADC values. It automatically handles:
8+
9+
- **ADC1/ADC2 Detection** - Identifies which ADC bank the pin uses
10+
- **Adaptive Caching** - Different cache durations based on WiFi impact
11+
- **WiFi Coordination** - Temporarily disables WiFi for ADC2 reads on ESP32-S3
12+
- **Desktop Simulation** - Returns random values in typical range when ADC unavailable
13+
14+
## Import
15+
16+
```python
17+
from mpos import BatteryManager
18+
```
19+
20+
## Initialization
21+
22+
Before using battery readings, initialize the ADC with a conversion function:
23+
24+
```python
25+
def adc_to_voltage(adc_value):
26+
"""Convert raw ADC value (0-4095) to battery voltage in volts."""
27+
return adc_value * 0.001651 + 0.08709
28+
29+
BatteryManager.init_adc(pin=13, adc_to_voltage_func=adc_to_voltage)
30+
```
31+
32+
### Parameters
33+
34+
- **pin** (int): GPIO pin number for battery voltage measurement
35+
- ADC1 pins: GPIO1-10 (no WiFi interference)
36+
- ADC2 pins: GPIO11-20 (requires WiFi disable on ESP32-S3)
37+
- **adc_to_voltage_func** (callable): Function that converts raw ADC value (0-4095) to voltage in volts
38+
39+
### Important Notes
40+
41+
- **ADC2 on ESP32-S3**: WiFi will be temporarily disabled during readings to avoid conflicts
42+
- **Desktop Mode**: If ADC is unavailable, returns simulated random values
43+
- **Calibration**: The conversion function should be calibrated for your specific hardware
44+
45+
## API Methods
46+
47+
### read_battery_voltage()
48+
49+
Get the current battery voltage in volts.
50+
51+
```python
52+
voltage = BatteryManager.read_battery_voltage()
53+
print(f"Battery: {voltage:.2f}V")
54+
```
55+
56+
**Parameters:**
57+
- `force_refresh` (bool, optional): Bypass cache and force fresh reading. Default: `False`
58+
- `raw_adc_value` (float, optional): Pre-computed raw ADC value (for testing). Default: `None`
59+
60+
**Returns:** float - Battery voltage in volts
61+
62+
**Raises:** RuntimeError - If ADC2 is used and WiFi is already busy
63+
64+
### get_battery_percentage()
65+
66+
Get the current battery charge as a percentage (0-100).
67+
68+
```python
69+
percentage = BatteryManager.get_battery_percentage()
70+
print(f"Battery: {percentage:.1f}%")
71+
```
72+
73+
**Parameters:**
74+
- `raw_adc_value` (float, optional): Pre-computed raw ADC value (for testing). Default: `None`
75+
76+
**Returns:** float - Battery percentage (0-100), clamped to valid range
77+
78+
### read_raw_adc()
79+
80+
Get the raw ADC value (0-4095) with averaging.
81+
82+
```python
83+
raw = BatteryManager.read_raw_adc()
84+
print(f"Raw ADC: {raw}")
85+
```
86+
87+
**Parameters:**
88+
- `force_refresh` (bool, optional): Bypass cache and force fresh reading. Default: `False`
89+
90+
**Returns:** float - Raw ADC value (average of 10 samples)
91+
92+
**Raises:** RuntimeError - If ADC2 is used and WiFi is already busy
93+
94+
### clear_cache()
95+
96+
Clear the cached battery reading to force a fresh read on the next call.
97+
98+
```python
99+
BatteryManager.clear_cache()
100+
voltage = BatteryManager.read_battery_voltage() # Fresh read
101+
```
102+
103+
## Caching Strategy
104+
105+
BatteryManager uses adaptive caching to balance accuracy with performance:
106+
107+
| ADC Bank | Cache Duration | Reason |
108+
|----------|----------------|--------|
109+
| ADC1 (GPIO1-10) | 30 seconds | No WiFi interference |
110+
| ADC2 (GPIO11-20) | 10 minutes | Expensive WiFi disable/enable |
111+
112+
To override cache duration at runtime:
113+
114+
```python
115+
import mpos.battery_manager
116+
mpos.battery_manager.CACHE_DURATION_ADC1_MS = 60000 # 60 seconds
117+
mpos.battery_manager.CACHE_DURATION_ADC2_MS = 300000 # 5 minutes
118+
```
119+
120+
## WiFi Coordination (ESP32-S3)
121+
122+
On ESP32-S3, ADC2 (GPIO11-20) cannot be read while WiFi is active. BatteryManager automatically:
123+
124+
1. Checks if WiFi is busy (connecting/scanning)
125+
2. Temporarily disables WiFi if needed
126+
3. Reads the ADC value
127+
4. Restores WiFi to its previous state
128+
129+
**Error Handling:**
130+
131+
```python
132+
try:
133+
voltage = BatteryManager.read_battery_voltage(force_refresh=True)
134+
except RuntimeError as e:
135+
print(f"Cannot read battery: {e}")
136+
# WiFi is busy, use cached value instead
137+
voltage = BatteryManager.read_battery_voltage()
138+
```
139+
140+
## Constants
141+
142+
```python
143+
from mpos.battery_manager import MIN_VOLTAGE, MAX_VOLTAGE
144+
145+
MIN_VOLTAGE = 3.15 # Minimum battery voltage (0%)
146+
MAX_VOLTAGE = 4.15 # Maximum battery voltage (100%)
147+
```
148+
149+
These constants define the voltage range used for percentage calculation. Adjust based on your battery chemistry.
150+
151+
## Example: Battery Status Display
152+
153+
```python
154+
from mpos import BatteryManager
155+
import time
156+
157+
# Initialize with calibration for your hardware
158+
def adc_to_voltage(adc_value):
159+
return adc_value * 0.001651 + 0.08709
160+
161+
BatteryManager.init_adc(pin=13, adc_to_voltage_func=adc_to_voltage)
162+
163+
# Display battery status
164+
while True:
165+
voltage = BatteryManager.read_battery_voltage()
166+
percentage = BatteryManager.get_battery_percentage()
167+
168+
if percentage > 80:
169+
status = "🔋 Full"
170+
elif percentage > 50:
171+
status = "🔋 Good"
172+
elif percentage > 20:
173+
status = "⚠️ Low"
174+
else:
175+
status = "🔴 Critical"
176+
177+
print(f"{status}: {voltage:.2f}V ({percentage:.0f}%)")
178+
time.sleep(5)
179+
```
180+
181+
## Example: Real-time Battery Monitoring in UI
182+
183+
```python
184+
from mpos import BatteryManager, Activity
185+
import lvgl as lv
186+
187+
class BatteryMonitor(Activity):
188+
def onCreate(self):
189+
screen = lv.obj()
190+
self.battery_label = lv.label(screen)
191+
self.battery_label.set_text("Battery: --V")
192+
self.setContentView(screen)
193+
194+
def onResume(self, screen):
195+
super().onResume(screen)
196+
197+
def update_battery(timer):
198+
voltage = BatteryManager.read_battery_voltage()
199+
percentage = BatteryManager.get_battery_percentage()
200+
text = f"Battery: {voltage:.2f}V ({percentage:.0f}%)"
201+
self.update_ui_threadsafe_if_foreground(
202+
self.battery_label.set_text, text
203+
)
204+
205+
# Update every 15 seconds
206+
lv.timer_create(update_battery, 15000, None)
207+
```
208+
209+
## Board-Specific Configuration
210+
211+
### Fri3d 2024
212+
213+
```python
214+
# GPIO13 (ADC2) with calibrated conversion function
215+
def adc_to_voltage(adc_value):
216+
return adc_value * 0.001651 + 0.08709
217+
218+
BatteryManager.init_adc(13, adc_to_voltage)
219+
```
220+
221+
### Waveshare ESP32-S3 Touch LCD
222+
223+
```python
224+
# GPIO5 (ADC1) - no WiFi interference
225+
def adc_to_voltage(adc_value):
226+
return adc_value * 0.001651 + 0.08709
227+
228+
BatteryManager.init_adc(5, adc_to_voltage)
229+
```
230+
231+
### Desktop (Linux)
232+
233+
```python
234+
# Simulated ADC (returns random values)
235+
def adc_to_voltage(adc_value):
236+
return adc_value * 0.001651 + 0.08709
237+
238+
BatteryManager.init_adc(999, adc_to_voltage)
239+
```
240+
241+
## Testing
242+
243+
BatteryManager includes comprehensive unit tests:
244+
245+
```bash
246+
./MicroPythonOS/tests/unittest.sh MicroPythonOS/tests/test_battery_voltage.py
247+
```
248+
249+
Tests cover:
250+
- ADC1/ADC2 pin detection
251+
- Caching behavior
252+
- WiFi coordination
253+
- Voltage calculations
254+
- Desktop mode simulation
255+
256+
## Troubleshooting
257+
258+
### "RuntimeError: WifiService is already busy"
259+
260+
This occurs when trying to read ADC2 while WiFi is connecting or scanning. Solutions:
261+
262+
1. Use ADC1 pins (GPIO1-10) instead if possible
263+
2. Wait for WiFi to finish connecting before reading
264+
3. Use cached value: `BatteryManager.read_battery_voltage()` (without force_refresh)
265+
266+
### Inaccurate voltage readings
267+
268+
1. Verify your ADC conversion function is calibrated correctly
269+
2. Check that the voltage divider is properly connected
270+
3. Ensure ADC attenuation is set to 11dB (0-3.3V range)
271+
4. Use `force_refresh=True` to bypass cache and get fresh reading
272+
273+
### Battery percentage always 0% or 100%
274+
275+
1. Check MIN_VOLTAGE and MAX_VOLTAGE constants match your battery
276+
2. Verify ADC conversion function is correct
277+
3. Test with known voltage: `BatteryManager.get_battery_percentage(raw_adc_value=2048)`
278+
279+
## See Also
280+
281+
- [`SensorManager`](sensor-manager.md) - For temperature and IMU sensors
282+
- [`WifiService`](wifi-service.md) - For WiFi connectivity management
283+
- [`DeviceInfo`](device-info.md) - For device information

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ nav:
5050
- Bundling Apps: apps/bundling-apps.md
5151
- Frameworks:
5252
- AudioFlinger: frameworks/audioflinger.md
53+
- BatteryManager: frameworks/battery-manager.md
5354
- BuildInfo: frameworks/build-info.md
5455
- ConnectivityManager: frameworks/connectivity-manager.md
5556
- DeviceInfo: frameworks/device-info.md

0 commit comments

Comments
 (0)