Skip to content

Arduino off-ramp: raw NVS settings reads + millis/delay shim - #127

Draft
zjwhitehead wants to merge 5 commits into
masterfrom
cursor/arduino-nvs-time-shim-b9f2
Draft

zjwhitehead wants to merge 5 commits into
masterfrom
cursor/arduino-nvs-time-shim-b9f2

Conversation

@zjwhitehead

@zjwhitehead zjwhitehead commented Sep 7, 2026

Copy link
Copy Markdown
Member

Two scoped Arduino off-ramp steps. Does not bump espressif32 / ESP-IDF, does not drop framework = arduino, espidf, and does not migrate GPIO, ADC, LEDC, Wire/SPI, Adafruit, or NimBLE.

1) Finish NVS for device settings (drop Preferences reads)

writeDeviceData() already used raw ESP-IDF nvs_* with a single nvs_commit(). refreshDeviceData() and factory reset still went through Arduino Preferences.

This PR ports the read/load and reset paths to the same raw NVS API and key layout, then removes #include <Preferences.h> and the Preferences instance from device_settings.cpp. Load sites call nvs_get_* / nvs_erase_all directly (no typed getter wrappers).

Field compatibility

Verified against Arduino-ESP32 2.0.17 Preferences.cpp (espressif32@6.13.0) and the existing write-path comments. Keys and NVS types are unchanged, so devices already in the field keep their stored values:

Key Type Notes
ver_major, ver_minor, scr_rot, perf_mode, theme, revision u8 getUCharnvs_get_u8
metric_tmp, metric_alt u8 0/1 getBool is getUChar == 1
sea_pres 4-byte blob getFloat/putFloat are getBytes/putBytesnvs_*_blob
armed_time u16 getUShortnvs_get_u16
tz_offset i32 getIntnvs_get_i32

Namespace remains openppg. Missing keys still fall back to the same defaults. Factory reset still wipes the namespace (nvs_erase_all + commit, equivalent to Preferences.clear()) and writes defaults. USBSerial / WebSerial strings and command behavior are unchanged.

2) Time shim: replace Arduino millis() / delay()

Added inc/sp140/time_utils.h:

  • timeMillis()esp_timer_get_time() / 1000ULL, returns unsigned long (same as Arduino millis())
  • timeDelay(ms)vTaskDelay(pdMS_TO_TICKS(ms)) (same as Arduino-ESP32 2.0.17 delay(), which is vTaskDelay(ms / portTICK_PERIOD_MS) with no busy-wait remainder)

On this firmware CONFIG_FREERTOS_HZ=1000, so one tick is 1 ms. Production src/ call sites now use the shim, including BLE files that only needed a clock (NimBLE itself is untouched). Arduino.h includes that are still needed for other APIs are left in place.

Native / screenshot tests keep compiling: the header’s non-ESP_PLATFORM path delegates to the existing Arduino stub millis() / delay() so screenshot timestamps stay on the same clock.

How to test

platformio test -e native-test
platformio run -e OpenPPG-CESP32S3-CAN-SP140

Optional (CI also runs this): ./test/test_screenshots/build_and_run.sh

Validation so far

  • platformio test -e native-test: 60/60 passed (local)
  • cpplint on inc/ and src/: clean (local)
  • First CI pio-build failed on -Werror=format (%lu vs uint32_t). Fixed by returning unsigned long from timeMillis().
  • Local full firmware build cannot fetch private openppg/SINE-ESC-CAN; CI pio-build is the equivalent check.

Intentionally left

  • src/sp140/diagnostics.cpp still uses Arduino Preferences — boot-diag history lives in a different namespace and is out of scope.
  • BLE log strings that say “saved to Preferences” — USB/BLE wording only; settings I/O is already writeDeviceData().
  • millis() / delay() in test/native_stubs and test/screenshot_stubs — native clock for unit/screenshot tests.
  • millis() in test/test_screenshots — test harness, not firmware.
  • Comments in relay headers that mention not calling delay().
  • Third-party / Adafruit / NimBLE internals — out of scope.
  • Arduino.h includes still required for Serial, GPIO, and other Arduino APIs.

Do not merge until reviewed.

Open in Web Open in Cursor 

cursoragent and others added 5 commits September 7, 2026 15:17
Drop Arduino Preferences from the load and factory-reset paths so reads
use the same nvs_* keys and types as the existing single-commit write
path. Field devices stay byte-compatible (u8/u16/i32, 4-byte float blob).

Co-authored-by: Zach Whitehead <zjwhitehead@users.noreply.github.com>
Add timeMillis() (esp_timer_get_time()/1000) and timeDelay()
(vTaskDelay + pdMS_TO_TICKS) and switch production firmware call sites.
Native and screenshot tests keep compiling via the header's non-ESP path.

Co-authored-by: Zach Whitehead <zjwhitehead@users.noreply.github.com>
Arduino 2.0.17 delay() is vTaskDelay(ms / portTICK_PERIOD_MS) with no
busy-wait remainder. Drop the delay(0) early-return so timeDelay(0)
still yields, matching that implementation.

Co-authored-by: Zach Whitehead <zjwhitehead@users.noreply.github.com>
ESP32 treats uint32_t and unsigned long as distinct for -Wformat, so
%lu prints of timeMillis() failed the firmware build. Keep the Arduino
millis() return type so existing format strings stay valid.

Co-authored-by: Zach Whitehead <zjwhitehead@users.noreply.github.com>
Load sites call nvs_get_* directly with the same keys, u8 0/1 bools,
4-byte float blob, and miss defaults. Factory reset inlines erase_all
so nvsEraseNamespace is no longer needed.

Co-authored-by: Zach Whitehead <zjwhitehead@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants