From 444991d49659915b4f975d7a51a8aa6a05dd22bd Mon Sep 17 00:00:00 2001 From: Jessica Janiuk Date: Tue, 22 Sep 2026 22:50:03 -0700 Subject: [PATCH] fix: silence false-alarm boot log errors from NVS and accelerometer probe - All four persistence load() functions (CalibrationStore, DroidPersistence, ComplicationPersistence, PowerConfigStore) opened their Preferences namespace read-only, which makes nvs_open() fail with NOT_FOUND (logged as an alarming error) on a device that's never saved that namespace yet -- true on every first boot. Opening read-write instead silently creates the empty namespace; no behavior change to what's actually loaded (still falls back to defaults), just no more false "hardware is broken" errors. - Accelerometer::begin() now does a plain presence probe (address-only write, real stop condition) before attempting its repeated-start register read. On boards without this part, endTransmission(false)'s repeated-start check doesn't reliably report the missing ACK by itself -- a known ESP32 I2C driver quirk -- so Wire.requestFrom() was getting called against a device that was never there, which the Wire library logs as an error. Co-Authored-By: Claude Sonnet 5 --- src/accelerometer.cpp | 14 ++++++++++++++ src/calibration_store.cpp | 10 +++++++++- src/complication_persistence.cpp | 5 ++++- src/droid_persistence.cpp | 5 ++++- src/power_config_store.cpp | 5 ++++- 5 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/accelerometer.cpp b/src/accelerometer.cpp index 9841d76..222fbd7 100644 --- a/src/accelerometer.cpp +++ b/src/accelerometer.cpp @@ -70,6 +70,20 @@ bool readRegister(uint8_t reg, uint8_t *outValue) { bool Accelerometer::begin() { Wire.begin(PinAssignment::kI2cSda, PinAssignment::kI2cScl); + // Plain presence probe (address-only write, real stop condition) + // before attempting the repeated-start register read below. On boards + // without this part (the first production run — see + // PCB/GPIO_table.md's Accelerometer section) endTransmission(false)'s + // repeated-start check doesn't reliably report the missing ACK by + // itself — a known ESP32 I2C driver quirk — so without this, + // Wire.requestFrom() gets called against a device that was never + // there, which the Wire library logs as an alarming-looking error. + Wire.beginTransmission(kI2cAddress); + if (Wire.endTransmission() != 0) { + present_ = false; + return false; + } + uint8_t chipId = 0; if (!readRegister(kRegChipId, &chipId)) { present_ = false; diff --git a/src/calibration_store.cpp b/src/calibration_store.cpp index 56bad73..6a73634 100644 --- a/src/calibration_store.cpp +++ b/src/calibration_store.cpp @@ -10,7 +10,15 @@ CalibrationData CalibrationStore::load() { CalibrationData data; // defaults if nothing has been saved yet Preferences prefs; - if (!prefs.begin(kNamespace, /*readOnly=*/true)) { + // Not readOnly: on a device that's never saved this namespace yet + // (first boot, or after a flash-partition wipe), NVS_READONLY makes + // nvs_open() fail with ESP_ERR_NVS_NOT_FOUND, which the Preferences + // library logs as an alarming-looking error regardless of how + // gracefully the caller handles the resulting begin() == false (we + // just fall back to defaults either way). Opening read-write instead + // creates the empty namespace silently — no behavior change, no false + // "hardware is broken" errors on real hardware's first boot. + if (!prefs.begin(kNamespace, /*readOnly=*/false)) { return data; } diff --git a/src/complication_persistence.cpp b/src/complication_persistence.cpp index fa4620e..415b3e0 100644 --- a/src/complication_persistence.cpp +++ b/src/complication_persistence.cpp @@ -10,7 +10,10 @@ constexpr const char *kNamespace = "snips_disp"; void ComplicationPersistence::load(ComplicationRegistry *registry) { Preferences prefs; - if (!prefs.begin(kNamespace, /*readOnly=*/true)) { + // Not readOnly: avoids a misleading "nvs_open failed: NOT_FOUND" log + // on a device that's never saved this namespace yet — see + // calibration_store.cpp's load() for the full explanation. + if (!prefs.begin(kNamespace, /*readOnly=*/false)) { return; } diff --git a/src/droid_persistence.cpp b/src/droid_persistence.cpp index 55a6886..27cf9ba 100644 --- a/src/droid_persistence.cpp +++ b/src/droid_persistence.cpp @@ -12,7 +12,10 @@ DroidStore DroidPersistence::load() { DroidStore store; Preferences prefs; - if (!prefs.begin(kNamespace, /*readOnly=*/true)) { + // Not readOnly: avoids a misleading "nvs_open failed: NOT_FOUND" log + // on a device that's never saved this namespace yet — see + // calibration_store.cpp's load() for the full explanation. + if (!prefs.begin(kNamespace, /*readOnly=*/false)) { return store; } diff --git a/src/power_config_store.cpp b/src/power_config_store.cpp index 5611d01..765773e 100644 --- a/src/power_config_store.cpp +++ b/src/power_config_store.cpp @@ -10,7 +10,10 @@ PowerConfig PowerConfigStore::load() { PowerConfig config; // defaults if nothing has been saved yet Preferences prefs; - if (!prefs.begin(kNamespace, /*readOnly=*/true)) { + // Not readOnly: avoids a misleading "nvs_open failed: NOT_FOUND" log + // on a device that's never saved this namespace yet — see + // calibration_store.cpp's load() for the full explanation. + if (!prefs.begin(kNamespace, /*readOnly=*/false)) { return config; }