fix(wio-tracker-l2): USB power detection works - charge bolt, Power row, GPS hint

- The AW35615 USB-C controller (FUSB302-style registers, ID 0x91) only
  reports VBUSOK with its measure block on; POWER resets to bandgap only,
  so isExternalPowered() was always false. Board init turns it on
- Status bar shows a charge bolt on the cable, Diagnostics > Live has a
  Power row, the GPS screen notes that charging disturbs GPS, and the
  low-battery warning is skipped on USB as intended
- Roadmap: USB detection closed; GPS indoors still worse than the L1
  even on battery, left open for later

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-09-26 22:12:05 +02:00
co-authored by Claude Opus 5.5
parent 70921cd865
commit f849ee0b00
6 changed files with 45 additions and 16 deletions
+11 -7
View File
@@ -196,13 +196,12 @@ this roadmap is done; the Cardputer follows whatever the spike shows.
## Backlog (found along the way)
- [ ] USB power detection: `WioTrackerL2Board::isExternalPowered()` reads
STATUS0 (0x40) bit 7 of the chip at I2C 0x22 and always gets 0 with
USB plugged in (0x40 reads 0x01). Needed for a charging indicator and
for skipping the low-battery shutdown on the cable. Find the right
chip / register (devices on the bus: 0x14, 0x18, 0x21, 0x22, 0x2c,
0x34, 0x48, 0x5d). Found 2026-09-26. Also wanted for a hint on the
GPS screen while on the cable (see below).
- [x] USB power detection (fixed 2026-09-26): the AW35615 at 0x22 has a
FUSB302-style register map (device ID 0x91); VBUSOK (STATUS0 bit 7)
needs the measure block on, and POWER (0x0B) resets to 0x01. Board
init sets PWR[1..2]; STATUS0 then reads 0x80 on USB. Used by the
status bar (charge bolt), Diagnostics > Live "Power", the GPS screen
hint and the low-battery warning (skipped on the cable).
- [x] GPS screen (2026-09-26): Home > GPS and Settings > System > GPS
details -- sky plot, C/N0 bar per satellite, fix / TTFF / DOPs,
per-constellation counts (helpers/sensors/GpsSky.h, -D GPS_SKYVIEW;
@@ -214,6 +213,11 @@ this roadmap is done; the Cardputer follows whatever the spike shows.
seconds, first fix after 377 s. Screen, WiFi, Bluetooth and LoRa TX
were ruled out. Cause: noise from USB power / the charger -- a board
property, not fixable in firmware. Test GPS on battery.
- [ ] GPS indoors, still open (user, 2026-09-26): even on battery the L2
does worse than the L1 with the same L76K -- first fix after 377 s by
a window, 4 of 22 satellites used, the fix drops and comes back.
Something is still off (antenna / placement / another noise source);
look again later with the GPS screen, e.g. side by side with the L1.
Ideas to come back to (2026-09-26):
@@ -10,7 +10,7 @@ namespace diagview {
enum : uint8_t { TAB_LIVE, TAB_SYSTEM, TAB_FONT, TAB_COUNT };
static uint8_t s_tab = TAB_LIVE; // kept across visits
static lv_obj_t* s_list = nullptr;
static const int EXTRA = 3; // GPS, last reset, last crash (L2 only)
static const int EXTRA = 4; // GPS, power, last reset, last crash (L2 only)
static lv_obj_t* s_vals[diag::MAX_ROWS + EXTRA];
static int s_rows = 0;
@@ -34,6 +34,7 @@ static int allRows(diag::Row* rows, bool gps_on) {
if (!gps_on) extraRow(rows, n, "GPS", "off");
else if (!data) extraRow(rows, n, "GPS", "no data (%lu B)", (unsigned long)c);
else extraRow(rows, n, "GPS", "%s, %ld sats", gps.isValid() ? "fix" : "no fix", gps.satellitesCount());
extraRow(rows, n, "Power", "%s", board.isExternalPowered() ? "USB" : "battery");
#else
(void)gps_on;
#endif
+5 -1
View File
@@ -348,7 +348,7 @@ void UITask::refreshGps() {
if (off) lv_obj_remove_flag(s_on_btn, LV_OBJ_FLAG_HIDDEN);
else lv_obj_add_flag(s_on_btn, LV_OBJ_FLAG_HIDDEN);
char used[48] = "", info[200] = "";
char used[48] = "", info[240] = "";
if (!g) {
lv_label_set_text(s_status, "Not available");
lv_obj_set_style_text_color(s_status, lv_color_hex(theme::TEXT_MUTED), 0);
@@ -387,6 +387,10 @@ void UITask::refreshGps() {
o += snprintf(info + o, sizeof(info) - o, "%.0f m - %.1f km/h\n", g->alt_m, g->speed_kmh);
if (g->utc_valid) o += snprintf(info + o, sizeof(info) - o, "UTC %02u:%02u:%02u", g->utc_h, g->utc_m, g->utc_s);
}
if (g && !off && _board->isExternalPowered()) { // measured: on the cable the L76K never decodes indoors
size_t o = strlen(info);
snprintf(info + o, sizeof(info) - o, "%sOn USB power: charging disturbs GPS", o ? "\n" : "");
}
lv_label_set_text(s_used, used);
lv_label_set_text(s_info, info);
+1 -1
View File
@@ -616,7 +616,7 @@ void UITask::refreshStatusBar() {
case battery::VOLTAGE: snprintf(level, sizeof(level), " %u.%02u V", mv / 1000, (mv % 1000) / 10); break;
default: break;
}
lv_label_set_text_fmt(_status_batt, "%s%s", batt, level);
lv_label_set_text_fmt(_status_batt, "%s%s%s", _board->isExternalPowered() ? LV_SYMBOL_CHARGE " " : "", batt, level);
// Status icons, as the original's status bar (ui-new): Bluetooth (bright
// when the app is connected), GPS (green with a fix), the alarm, mute, then
+25 -6
View File
@@ -125,6 +125,20 @@ void WioTrackerL2Board::begin() {
Wire.beginTransmission(0x22);
aw_ok = Wire.endTransmission() == 0;
if (aw_ok) {
// AW35615 = FUSB302-style register map. VBUSOK (STATUS0 bit 7) only
// works with the measure block powered: POWER (0x0B) resets to 0x01
// (bandgap only), which reads "no USB" forever. Add the receiver /
// current references and the measure block (PWR[1..2]).
// Verified on hardware: device ID 0x91; STATUS0 0x01 -> 0x80 on USB.
int pw = awRead(0x0B);
if (pw >= 0) {
Wire.beginTransmission(0x22);
Wire.write((uint8_t)0x0B);
Wire.write((uint8_t)(pw | 0x07));
Wire.endTransmission();
}
}
MESH_DEBUG_PRINTLN("WioTrackerL2Board: init done");
esp_reset_reason_t reason = esp_reset_reason();
@@ -156,13 +170,18 @@ int WioTrackerL2Board::expReadInputs() {
return lo | (hi << 8);
}
bool WioTrackerL2Board::isExternalPowered() {
if (!aw_ok) return false;
int WioTrackerL2Board::awRead(uint8_t reg) {
if (!aw_ok) return -1;
Wire.beginTransmission(0x22);
Wire.write((uint8_t)0x40); // STATUS0
if (Wire.endTransmission() != 0) return false;
if (Wire.requestFrom(0x22, 1) != 1) return false;
return (Wire.read() & 0x80) != 0; // VBUSOK bit
Wire.write(reg);
if (Wire.endTransmission() != 0) return -1;
if (Wire.requestFrom(0x22, 1) != 1) return -1;
return Wire.read();
}
bool WioTrackerL2Board::isExternalPowered() {
int st0 = awRead(0x40); // STATUS0
return st0 >= 0 && (st0 & 0x80) != 0; // VBUSOK
}
bool WioTrackerL2Board::readWakeButton() {
@@ -84,6 +84,7 @@ public:
// VBUS presence via the AW35615 USB-C controller (I2C 0x22)
bool isExternalPowered() override;
int awRead(uint8_t reg); // AW35615 register, -1 on error
bool expanderOK() const { return expander_ok; }