From 19d557e70a3e1023222b9bd1ed9c48afb9641bc9 Mon Sep 17 00:00:00 2001 From: "Michael A. Cojocari" Date: Thu, 3 Sep 2026 12:52:03 -0400 Subject: [PATCH] Work --- DEVICES.md | 2 +- boards/{square.json => wio_tracker_l2.json} | 4 +- platformio.ini | 20 +- scripts/build/patch_lgfx_dmadesc.py | 8 +- src/DataStore.cpp | 6 +- src/DataStore.h | 4 +- src/main.cpp | 42 +-- src/ui-touch/UITask.cpp | 261 +++++++++--------- src/ui-touch/device_caps.h | 39 +-- .../WioTrackerL2Board.cpp} | 36 +-- .../WioTrackerL2Board.h} | 4 +- .../WioTrackerL2Display.cpp} | 48 ++-- .../WioTrackerL2Display.h} | 8 +- .../WioTrackerL2Io.cpp} | 10 +- .../WioTrackerL2Io.h} | 4 +- .../WioTrackerL2Touch.cpp} | 10 +- .../{square => wio_tracker_l2}/target.cpp | 4 +- variants/{square => wio_tracker_l2}/target.h | 8 +- 18 files changed, 253 insertions(+), 265 deletions(-) rename boards/{square.json => wio_tracker_l2.json} (92%) rename variants/{square/SquareBoard.cpp => wio_tracker_l2/WioTrackerL2Board.cpp} (72%) rename variants/{square/SquareBoard.h => wio_tracker_l2/WioTrackerL2Board.h} (79%) rename variants/{square/SquareDisplay.cpp => wio_tracker_l2/WioTrackerL2Display.cpp} (62%) rename variants/{square/SquareDisplay.h => wio_tracker_l2/WioTrackerL2Display.h} (90%) rename variants/{square/SquareIo.cpp => wio_tracker_l2/WioTrackerL2Io.cpp} (96%) rename variants/{square/SquareIo.h => wio_tracker_l2/WioTrackerL2Io.h} (89%) rename variants/{square/SquareTouch.cpp => wio_tracker_l2/WioTrackerL2Touch.cpp} (91%) rename variants/{square => wio_tracker_l2}/target.cpp (93%) rename variants/{square => wio_tracker_l2}/target.h (79%) diff --git a/DEVICES.md b/DEVICES.md index e10a0966..d212f9d4 100644 --- a/DEVICES.md +++ b/DEVICES.md @@ -52,7 +52,7 @@ USB) and the [GitHub releases](https://github.com/ALLFATHER-BV/wadamesh/releases - **RAK WisMesh Tap V2**: newest port, touch-driven. The browser-flash path is fresh; if the flasher cannot open the serial port, put the board in download mode manually and retry, and please report it. -- **square**: pre-release touch target under active bring-up. Builds are for +- **Seeed Wio Tracker L2**: pre-release touch target under active bring-up. Builds are for development and hardware validation only; no public release artifact is promised until the port is verified. diff --git a/boards/square.json b/boards/wio_tracker_l2.json similarity index 92% rename from boards/square.json rename to boards/wio_tracker_l2.json index 733da5af..70c56b1e 100644 --- a/boards/square.json +++ b/boards/wio_tracker_l2.json @@ -28,7 +28,7 @@ "openocd_target": "esp32s3.cfg" }, "frameworks": ["arduino", "espidf"], - "name": "square (16 MB FLASH, 8 MB PSRAM)", + "name": "Seeed Wio Tracker L2 (16 MB FLASH, 8 MB PSRAM)", "upload": { "flash_size": "16MB", "maximum_ram_size": 8388608, @@ -39,5 +39,5 @@ "speed": 921600 }, "url": "", - "vendor": "" + "vendor": "Seeed Studio" } \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 0257f81b..33c9d376 100644 --- a/platformio.ini +++ b/platformio.ini @@ -688,12 +688,12 @@ lib_deps = knolleary/PubSubClient @ ^2.8.0 ; ====================================================================== -; square — touch-first ESP32-S3 target with quad-SPI display and SX1262. +; Seeed Wio Tracker L2 — touch-first ESP32-S3 target with quad-SPI display and SX1262. ; ====================================================================== -[env:square] +[env:wio_tracker_l2_companion_radio_touch] platform = platformio/espressif32@6.11.0 framework = arduino -board = square +board = wio_tracker_l2 monitor_speed = 115200 upload_speed = 921600 board_build.arduino.memory_type = qio_opi @@ -710,7 +710,7 @@ build_flags = -I"${platformio.libdeps_dir}/${this.__env__}/MicroNMEA/src" -I"${platformio.libdeps_dir}/${this.__env__}/Adafruit ADS1X15" -I"${platformio.libdeps_dir}/${this.__env__}/LovyanGFX/src" - -I variants/square + -I variants/wio_tracker_l2 -I include -I src -I src/ui-touch @@ -722,7 +722,7 @@ build_flags = -DRADIOLIB_GODMODE=1 -D MC_VENDORED_TOUCH_APP -D ESP32_PLATFORM - -D HAS_SQUARE=1 + -D HAS_WIO_TRACKER_L2=1 -D BOARD_HAS_PSRAM=1 -D ESP32_CPU_FREQ=240 -D CORE_DEBUG_LEVEL=0 @@ -780,27 +780,27 @@ build_flags = ; the pin we transmit on. These were 18/17 (copied from the Attaky env during ; bring-up, never checked against this board), which left the UART listening on ; GPIO17 while the module talks on GPIO18 -- GPS never produced a fix. - ; Confirmed by SQUARE_GPS_PROBE: NMEA on GPIO18 at 9600 (9 sats in view), + ; Confirmed by WIO_TRACKER_L2_GPS_PROBE: NMEA on GPIO18 at 9600 (9 sats in view), ; GPIO17 silent at 9600/38400/115200. -D PIN_GPS_TX=18 -D PIN_GPS_RX=17 -D GPS_BAUD_RATE=9600 ; TEMP DIAGNOSTIC, disarmed now that the wiring is confirmed. Uncomment to ; re-run the pin/baud sweep; the block it gates lives in src/main.cpp. - ; -D SQUARE_GPS_PROBE=1 + ; -D WIO_TRACKER_L2_GPS_PROBE=1 -D PIN_BOARD_SDA=47 -D PIN_BOARD_SCL=48 -D PIN_USER_BTN=0 -D PIN_TFT_LEDA_CTL=-1 -D PIN_TFT_RST=-1 -D PIN_TFT_DC=-1 - -D DISPLAY_CLASS=SquareDisplay + -D DISPLAY_CLASS=WioTrackerL2Display -D DISPLAY_ROTATION=0 -D DISPLAY_SCALE_X=1.0 -D DISPLAY_SCALE_Y=1.0 -D UI_LVGL=1 -D HAS_TOUCH_UI=1 - -D FIRMWARE_OTA_ENV='"square"' + -D FIRMWARE_OTA_ENV='"wio_tracker_l2_companion_radio_touch"' -D ENABLE_ADVERT_ON_BOOT=0 -D LV_CONF_PATH=lv_conf.h -D LV_CONF_INCLUDE_SIMPLE=1 @@ -828,7 +828,7 @@ build_src_filter = + + + - +<../variants/square/*.cpp> + +<../variants/wio_tracker_l2/*.cpp> lib_deps = https://github.com/ALLFATHER-BV/meshcomod.git#core-v1.17.4 diff --git a/scripts/build/patch_lgfx_dmadesc.py b/scripts/build/patch_lgfx_dmadesc.py index 32482805..f08603c4 100644 --- a/scripts/build/patch_lgfx_dmadesc.py +++ b/scripts/build/patch_lgfx_dmadesc.py @@ -4,7 +4,7 @@ Import("env") import os -MARKER = "wadamesh-square-dmadesc-oom-patch" +MARKER = "wadamesh-wio-tracker-l2-dmadesc-oom-patch" OLD_ALLOC = """ void Bus_SPI::_alloc_dmadesc(size_t len) { @@ -15,7 +15,7 @@ OLD_ALLOC = """ void Bus_SPI::_alloc_dmadesc(size_t len) NEW_ALLOC = """ void Bus_SPI::_alloc_dmadesc(size_t len) { - // wadamesh-square-dmadesc-oom-patch: retain the working descriptor array + // wadamesh-wio-tracker-l2-dmadesc-oom-patch: retain the working descriptor array // when a replacement cannot be allocated. auto next = (lldesc_t*)heap_caps_malloc(sizeof(lldesc_t) * len, MALLOC_CAP_DMA); if (next == nullptr) { return; } @@ -34,7 +34,7 @@ NEW_SETUP = """ if (_dmadesc_size * SPI_MAX_DMA_LEN < len) { _alloc_dmadesc(len / SPI_MAX_DMA_LEN + 1); } - // wadamesh-square-dmadesc-oom-patch: skip a frame rather than walking a + // wadamesh-wio-tracker-l2-dmadesc-oom-patch: skip a frame rather than walking a // null or undersized descriptor array after allocation failure. if (_dmadesc == nullptr || _dmadesc_size * SPI_MAX_DMA_LEN < len) { return; } lldesc_t *dmadesc = _dmadesc;""" @@ -59,7 +59,7 @@ else: source = source.replace(OLD_ALLOC, NEW_ALLOC, 1).replace(OLD_SETUP, NEW_SETUP, 1) with open(path, "w", encoding="utf-8") as source_file: source_file.write(source) - print("[patch_lgfx_dmadesc] patched square LovyanGFX DMA allocation") + print("[patch_lgfx_dmadesc] patched Wio Tracker L2 LovyanGFX DMA allocation") def verify_patched(target, source, env): diff --git a/src/DataStore.cpp b/src/DataStore.cpp index 1899f7f5..700cda35 100644 --- a/src/DataStore.cpp +++ b/src/DataStore.cpp @@ -6,7 +6,7 @@ #include #include "helpers/esp32/WdtHeavyGuard.h" // suspend core-0 idle WDT during the (SPIFFS-GC-prone) contact write #endif -#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_SQUARE) +#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_WIO_TRACKER_L2) #include #endif @@ -1139,9 +1139,9 @@ bool DataStore::useSdStorage() { } #endif -#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_SQUARE) +#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_WIO_TRACKER_L2) // Full-store adoption on an SD_MMC slot. P4 callers migrate internal files -// first; square mounts and adopts the card directly at boot. +// first; the Wio Tracker L2 mounts and adopts the card directly at boot. bool DataStore::useSdMmcStorage() { if (!SD_MMC.exists("/meshcomod")) SD_MMC.mkdir("/meshcomod"); if (!SD_MMC.exists("/meshcomod/bl")) SD_MMC.mkdir("/meshcomod/bl"); diff --git a/src/DataStore.h b/src/DataStore.h index 13bc17aa..7876babf 100644 --- a/src/DataStore.h +++ b/src/DataStore.h @@ -43,9 +43,9 @@ public: // if the dir can't be created. No-op on non-ESP32. bool useSdStorage(); #endif -#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_SQUARE) +#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_WIO_TRACKER_L2) // Full-store adoption of an SD_MMC card. P4 callers migrate internal files - // first; square adopts the card directly at boot. + // first; the Wio Tracker L2 adopts the card directly at boot. bool useSdMmcStorage(); #endif FILESYSTEM* getPrimaryFS() const { return _fs; } diff --git a/src/main.cpp b/src/main.cpp index 96b824c3..b6e7aea9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -49,9 +49,9 @@ static uint32_t _atoi(const char* sp) { DataStore store(LittleFS, rtc_clock); #elif defined(ESP32) #include - #if defined(HAS_SQUARE) + #if defined(HAS_WIO_TRACKER_L2) #include - #include + #include #endif #if defined(HAS_TDECK_GT911) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) #include @@ -649,25 +649,25 @@ void meshcomodClearSdMigLatch() { #endif -#if defined(HAS_SQUARE) && defined(SQUARE_GPS_PROBE) +#if defined(HAS_WIO_TRACKER_L2) && defined(WIO_TRACKER_L2_GPS_PROBE) // ============================================================================ -// TEMP DIAGNOSTIC — remove this block and the -D SQUARE_GPS_PROBE=1 line in -// platformio.ini once the square's GPS wiring is confirmed. +// TEMP DIAGNOSTIC — remove this block and the -D WIO_TRACKER_L2_GPS_PROBE=1 line in +// platformio.ini once the Wio Tracker L2's GPS wiring is confirmed. // -// square's PIN_GPS_RX=18 / PIN_GPS_TX=17 were added wholesale in the bring-up +// the L2's PIN_GPS_RX=18 / PIN_GPS_TX=17 were added wholesale in the bring-up // commit and are byte-identical to the Attaky env, so neither the pin pair nor // GPS_BAUD_RATE=9600 has ever been checked against this board. ENV_SKIP_GPS_DETECT // hard-codes gps_detected = true, so the UI reports a module present whether or // not a byte ever arrives — this probe is the missing evidence. // -// Runs after board.begin() (SquareIo::begin has already powered the GNSS and +// Runs after board.begin() (WioTrackerL2Io::begin has already powered the GNSS and // released its reset) and before sensors.begin() (which is what normally opens // Serial1), then leaves the UART closed for initBasicGPS() to reopen. // // RX only: txPin is -1 so the probe never drives a pin whose real function on // this board is unconfirmed. Costs ~6 s of boot time while it runs. // ============================================================================ -static void squareGpsProbe() { +static void wioTrackerL2GpsProbe() { static const int8_t kPins[] = {17, 18}; // 17 = today's ESP RX, 18 = the swap static const uint32_t kBauds[] = {9600, 38400, 115200}; @@ -725,12 +725,12 @@ static void squareGpsProbe() { // EnvironmentSensorManager calls Serial1.setPins(PIN_GPS_TX, PIN_GPS_RX) and // Arduino's setPins() takes (rxPin, txPin) -- so the pin we RECEIVE on is // PIN_GPS_TX, and PIN_GPS_RX is the pin we transmit on. - Serial.printf("[GPSPROBE] -> platformio.ini square env should read: PIN_GPS_TX=%d, PIN_GPS_RX=%d, GPS_BAUD_RATE=%lu\n", + Serial.printf("[GPSPROBE] -> platformio.ini wio_tracker_l2 env should read: PIN_GPS_TX=%d, PIN_GPS_RX=%d, GPS_BAUD_RATE=%lu\n", (int)best_pin, (int)(best_pin == 17 ? 18 : 17), (unsigned long)best_baud); } Serial.println(F("[GPSPROBE] ---- end ----")); } -#endif // HAS_SQUARE && SQUARE_GPS_PROBE +#endif // HAS_WIO_TRACKER_L2 && WIO_TRACKER_L2_GPS_PROBE void setup() { @@ -980,7 +980,7 @@ void setup() { // failure falls back to SPIFFS so the device always boots. bool spiffs_ok = SPIFFS.begin(false); // try first WITHOUT auto-format bool sd_storage = false; -#if defined(HAS_SQUARE) +#if defined(HAS_WIO_TRACKER_L2) // This target uses a dedicated one-bit SD_MMC bus rather than the LoRa SPI // bus. Power the card first, set the non-default pins, and adopt it as the // complete store when available; SPIFFS remains the graceful fallback. @@ -991,19 +991,19 @@ void setup() { // edge shows up as a mid-session "sdmmc_host_wait_for_event returned 0x107" // (ESP_ERR_TIMEOUT) that takes prefs, contacts and chat history down with it. The // T-Display P4 hot-insert path already drops to this rate for the same reason. - bool square_sd_begun = false; - if (SquareIo::ready() && SquareIo::setSdPower(true) && + bool wio_l2_sd_begun = false; + if (WioTrackerL2Io::ready() && WioTrackerL2Io::setSdPower(true) && SD_MMC.setPins(2, 3, 1) && - (square_sd_begun = SD_MMC.begin("/sdcard", true, false, SDMMC_FREQ_DEFAULT)) && + (wio_l2_sd_begun = SD_MMC.begin("/sdcard", true, false, SDMMC_FREQ_DEFAULT)) && SD_MMC.cardType() != CARD_NONE) { sd_storage = store.useSdMmcStorage(); g_contacts_on_sd = sd_storage; g_full_data_on_sd = sd_storage; - Serial.printf("[BOOT] square SD_MMC: %s\n", sd_storage ? "adopted" : "mount only"); + Serial.printf("[BOOT] wio-l2 SD_MMC: %s\n", sd_storage ? "adopted" : "mount only"); } else { - if (square_sd_begun) SD_MMC.end(); - (void)SquareIo::setSdPower(false); - Serial.println("[BOOT] square SD_MMC unavailable; using SPIFFS"); + if (wio_l2_sd_begun) SD_MMC.end(); + (void)WioTrackerL2Io::setSdPower(false); + Serial.println("[BOOT] wio-l2 SD_MMC unavailable; using SPIFFS"); } #endif #if defined(HAS_TDECK_GT911) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) @@ -1296,7 +1296,7 @@ void setup() { // Route touch settings + Wi-Fi creds to the active filesystem (SD when that's // the data store, else SPIFFS) instead of NVS. Old NVS values still load and // migrate on their next save, so this is a transparent in-place upgrade. - #if defined(HAS_SQUARE) + #if defined(HAS_WIO_TRACKER_L2) SdNvsPrefs::useFile(sd_storage ? (fs::FS*)&SD_MMC : (fs::FS*)&SPIFFS, sd_storage ? "/meshcomod" : "/prefs"); #elif defined(HAS_TDECK_GT911) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) @@ -1553,8 +1553,8 @@ void setup() { #endif } #endif -#if defined(HAS_SQUARE) && defined(SQUARE_GPS_PROBE) - squareGpsProbe(); // TEMP DIAGNOSTIC — must precede sensors.begin()'s Serial1.begin() +#if defined(HAS_WIO_TRACKER_L2) && defined(WIO_TRACKER_L2_GPS_PROBE) + wioTrackerL2GpsProbe(); // TEMP DIAGNOSTIC — must precede sensors.begin()'s Serial1.begin() #endif sensors.begin(); diff --git a/src/ui-touch/UITask.cpp b/src/ui-touch/UITask.cpp index bbbb85d6..34d6f36c 100644 --- a/src/ui-touch/UITask.cpp +++ b/src/ui-touch/UITask.cpp @@ -114,7 +114,7 @@ static void* wadaMp3Scratch() { return s_wada_mp3_scratch; } // MyMesh.h -> target.h -> TLoraPagerBoard.h above #include // pager ES8311 codec (notification tones + WAV playback) #include "Es8311Codec.h" // PIN_I2S_MCLK/BCK/WS/DOUT/SDIN come from platformio.ini build flags -#elif defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) +#elif defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_WIO_TRACKER_L2) #include // internal FAT partition (Tanmatsu 'locfd' / P4 'storage') #include // microSD on the P4-class boards' SDMMC slot 0; slot 1 = C6 radio extern bool g_fs_ok; // set in main.cpp once the internal FAT is mounted @@ -172,6 +172,9 @@ static_assert(ChannelSenderSplit::kMaxWireName >= (size_t)UITask::MAX_SENDER_NAM #include // badge-bsp-backed DisplayDriver (P4) #elif defined(TLORA_PAGER) #include + #elif defined(HAS_WIO_TRACKER_L2) + #include + #include #elif defined(HAS_RAK_TAP_V2) #include // LovyanGFX FSPI on RAK Tap V2 #elif defined(HAS_TDISPLAY_P4) @@ -229,6 +232,8 @@ static_assert(ChannelSenderSplit::kMaxWireName >= (size_t)UITask::MAX_SENDER_NAM extern TanmatsuDisplay display; #elif defined(TLORA_PAGER) extern ST7796LCDDisplay display; + #elif defined(HAS_WIO_TRACKER_L2) + extern WioTrackerL2Display display; #elif defined(HAS_RAK_TAP_V2) || defined(HELTEC_LORA_V4_R8) extern LGFXDisplay display; #elif defined(HAS_TDISPLAY_P4) @@ -607,9 +612,13 @@ static uint32_t COLOR_SECONDARY_ACTION = kNightPalette.secondary_action; static uint32_t COLOR_TRACK = kNightPalette.track; static uint32_t COLOR_CHART_BG = kNightPalette.chart_bg; -static void applyThemeMode(uint8_t mode) { - s_theme_day = mode == TOUCH_THEME_DAY; - const TouchPalette& p = s_theme_day ? kDayPalette : kNightPalette; +// Day/Night belongs to the 296-day-night-theme branch, whose TouchPrefsStore +// half (the TOUCH_THEME_* modes and their accessors) is not on this branch. The +// UI is pinned to the night palette until that branch lands: s_theme_day stays +// false, so every `s_theme_day ? day : night` site below resolves to night. +static void applyThemeMode() { + s_theme_day = false; + const TouchPalette& p = kNightPalette; COLOR_BG = p.bg; COLOR_PANEL = p.panel; COLOR_TEXT = p.text; @@ -6347,6 +6356,13 @@ static void kbShowRotateArrows(bool show) { } } +// The Wio Tracker L2 panel is fixed 320x240 LANDSCAPE (CAP_ROTATABLE 0), so +// "rotate for landscape typing" has nothing to offer there — it would turn a +// landscape keyboard into a portrait one. The arrows are not created on that +// board and these, their only callers, go with them. Every other reference to +// the two pointers is null-guarded, so leaving them null is enough to remove +// the buttons everywhere — the same pattern the retired s_kb_alt_btn uses. +#if !defined(HAS_WIO_TRACKER_L2) static void kbRotLeftCb(lv_event_t* e) { if (lv_event_get_code(e) != LV_EVENT_CLICKED) return; // Tap the left arrow: rotate landscape that way; tap again returns to portrait. @@ -6361,6 +6377,7 @@ static void kbRotRightCb(lv_event_t* e) { kbApplyRotation(s_kb_rotation); kbSaveRotationPref(); } +#endif static void kbMirrorEnsureCreated() { if (s_kb_mirror_root) return; @@ -11708,16 +11725,6 @@ static void sysInfoTextRest(char* buf, size_t cap) { (chip.features & CHIP_FEATURE_WIFI_BGN) ? " WiFi" : "", (chip.features & CHIP_FEATURE_BT) ? " BT" : ""); - p += snprintf(buf + p, cap - p, "Clock\n source: %s (%s)\n", - rtc_clock.sourceName(), rtc_clock.timeIsCurrent() ? "current" : "degraded"); -#if defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) - p += snprintf(buf + p, cap - p, " %s: %s%s\n\n", hw_rtc.chipName(), - HardwareRtcClock::statusName(hw_rtc.status()), - hw_rtc.writeConfirmed() ? " (write verified)" : ""); -#else - p += snprintf(buf + p, cap - p, "\n"); -#endif - const uint32_t flash_chip_size = ESP.getFlashChipSize(); #if defined(HAS_TANMATSU) // ESP.getSketchSize()/getFreeSketchSpace() abort() on the P4: this is an AppFS app (runs from the @@ -12167,25 +12174,6 @@ static void clock12hToggleCb(lv_event_t* e) { if (g_lv.task) g_lv.task->showAlert(on ? TR("Clock: 12-hour") : TR("Clock: 24-hour"), 900); } -#if CAP_BOOT_TIME_SYNC -static void bootWifiTimeToggleCb(lv_event_t* e) { - if (lv_event_get_code(e) != LV_EVENT_VALUE_CHANGED) return; - const bool on = lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED); - touchPrefsSetBootWifiTime(on); - lv_obj_t* open_sw = static_cast(lv_event_get_user_data(e)); - if (open_sw && lv_obj_is_valid(open_sw)) { - if (on) lv_obj_clear_state(open_sw, LV_STATE_DISABLED); - else lv_obj_add_state(open_sw, LV_STATE_DISABLED); - } -} - -static void bootWifiOpenToggleCb(lv_event_t* e) { - if (lv_event_get_code(e) != LV_EVENT_VALUE_CHANGED) return; - touchPrefsSetBootWifiTimeOpen( - lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED)); -} -#endif - #if CAP_UI_SIZE // UI-size selector — saves the board-specific preset. Fonts and any matching // geometry are selected while the UI tree is built, so prompt for a restart. @@ -12910,22 +12898,6 @@ static void pagerKbBlCycleCb(lv_event_t* e) { } #endif -static void themeModeRestart(uint8_t mode) { -#if defined(ESP32) - if (touchPrefsGetThemeMode() == mode) return; - if (!touchPrefsSetThemeMode(mode)) { - if (g_lv.task) g_lv.task->showAlert(TR("Theme save failed"), 1600); - return; - } -#endif - if (g_lv.task) g_lv.task->rebootDevice(); -} - -static void themeModeSelectCb(lv_event_t* e) { - if (lv_event_get_code(e) != LV_EVENT_CLICKED) return; - themeModeRestart((uint8_t)(uintptr_t)lv_event_get_user_data(e)); -} - static void buildDeviceSettings(int sec) { // One detail page per section: each block below is gated to its DSEC_* section // (skipped blocks don't advance y, so every page lays out from the top). @@ -13224,30 +13196,6 @@ static void buildDeviceSettings(int sec) { lv_label_set_text(l_time, TR("Sync clock from system")); lv_obj_center(l_time); y += SC(40); - -#if CAP_BOOT_TIME_SYNC - lv_obj_t* boot_sync_sw = lv_switch_create(body); - lv_obj_t* open_sync_sw = lv_switch_create(body); - { - int h = settingsRowLabel(body, y, 6, TR("Sync time from saved Wi-Fi after cold boot"), - COLOR_SUB, nullptr, 56); - lv_obj_align(boot_sync_sw, LV_ALIGN_TOP_RIGHT, 0, y); - if (touchPrefsGetBootWifiTime()) lv_obj_add_state(boot_sync_sw, LV_STATE_CHECKED); - y += LV_MAX(SC(34), h + 12); - } - { - int h = settingsRowLabel(body, y, 6, TR("Allow saved open networks for time sync"), - COLOR_SUB, nullptr, 56); - lv_obj_align(open_sync_sw, LV_ALIGN_TOP_RIGHT, 0, y); - if (touchPrefsGetBootWifiTimeOpen()) lv_obj_add_state(open_sync_sw, LV_STATE_CHECKED); - if (!touchPrefsGetBootWifiTime()) lv_obj_add_state(open_sync_sw, LV_STATE_DISABLED); - y += LV_MAX(SC(34), h + 12); - } - lv_obj_add_event_cb(boot_sync_sw, bootWifiTimeToggleCb, - LV_EVENT_VALUE_CHANGED, open_sync_sw); - lv_obj_add_event_cb(open_sync_sw, bootWifiOpenToggleCb, - LV_EVENT_VALUE_CHANGED, nullptr); -#endif } if (sec == DSEC_GENERAL) { // --- Send advert (device action) --- @@ -13479,37 +13427,6 @@ static void buildDeviceSettings(int sec) { } #endif - /* Firmware appearance: selecting a different palette saves and restarts so - every LVGL object is rebuilt with one coherent set of colours. */ - { - y += settingsRowLabel(body, y, 0, TR("Appearance"), COLOR_SUB, &g_font_12, 0) + 4; - const uint8_t current = touchPrefsGetThemeMode(); - const lv_coord_t gap = 4; - const lv_coord_t row_w = s_settings_content_w - 2; - const lv_coord_t button_w = (row_w - gap) / 2; - const char* labels[2] = { TR("Night"), TR("Day") }; - for (uint8_t mode = TOUCH_THEME_NIGHT; mode <= TOUCH_THEME_DAY; ++mode) { - lv_obj_t* button = lv_btn_create(body); - lv_obj_set_size(button, button_w, SC(34)); - lv_obj_set_pos(button, 2 + mode * (button_w + gap), y); - styleButton(button); - if (mode == current) { - lv_obj_set_style_bg_opa(button, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_border_opa(button, LV_OPA_COVER, LV_PART_MAIN); - } - lv_obj_set_style_text_color(button, - lv_color_hex(mode == current ? COLOR_ON_ACCENT : COLOR_TEXT), LV_PART_MAIN); - lv_obj_add_event_cb(button, themeModeSelectCb, LV_EVENT_CLICKED, - (void*)(uintptr_t)mode); - lv_obj_t* label = lv_label_create(button); - char text[32]; - snprintf(text, sizeof(text), "%s%s", mode == current ? LV_SYMBOL_OK " " : "", labels[mode]); - lv_label_set_text(label, text); - lv_obj_center(label); - } - y += SC(42); - } - /* Accent colour: opens a colour-wheel + hex picker. */ { y += settingsRowLabel(body, y, 0, TR("Accent colour"), COLOR_SUB, &g_font_12, 0) + 4; @@ -19563,7 +19480,7 @@ static char s_fm_path[160] = {0}; // current dir within s_fm_fs (e.g. // fmRefresh, so they blip the LED too. #if defined(HAS_TDECK_GT911) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HELTEC_LORA_V4_R8) static inline bool fmIsSd(fs::FS* fs) { return fs == &SD; } // Arduino SD (T-Deck/pager/M9 LoRa bus, V4-R8 TFT bus) -#elif defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) +#elif defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_WIO_TRACKER_L2) static inline bool fmIsSd(fs::FS* fs) { return fs == &SD_MMC; } // microSD on SDMMC slot 0 #else static inline bool fmIsSd(fs::FS*) { return false; } // Heltec: no Arduino SD global in this FM path @@ -22249,7 +22166,7 @@ static void fmRefresh() { } // Roots screen: list the available storages. -#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) +#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_WIO_TRACKER_L2) // ---- Tanmatsu / T-Display P4 microSD (SDMMC slot 0) ---------------------------------------------- // The card sits on the P4's SDMMC *slot 0* (IOMUX pins CLK43/CMD44/D0-3 39-42, selected by the // BOARD_SDMMC_SLOT=0 build define); slot 1 is the C6 radio (esp-hosted / ESP-AT SDIO), which we never @@ -22265,7 +22182,15 @@ static bool tanSdTryMount() { if (luaAudioStorageBusy()) return false; #endif if (millis() < s_tan_sd_retry_after) return false; -#if defined(HAS_TDISPLAY_P4) +#if defined(HAS_WIO_TRACKER_L2) + // 20 MHz to match main.cpp's boot mount — re-begin()ning at Arduino's 40 MHz + // default would quietly re-clock a card the boot already brought up at 20. + if (SD_MMC.cardType() != CARD_NONE || + (WioTrackerL2Io::ready() && WioTrackerL2Io::setSdPower(true) && + SD_MMC.setPins(2, 3, 1) && + SD_MMC.begin("/sdcard", true, false, SDMMC_FREQ_DEFAULT) && + SD_MMC.cardType() != CARD_NONE)) { +#elif defined(HAS_TDISPLAY_P4) // Hot-insert path (no-op when main.cpp already mounted at boot): 20 MHz like the boot ladder, // not Arduino's 40 MHz HIGHSPEED default. if (SD_MMC.begin("/sdcard", false /*4-bit*/, false, SDMMC_FREQ_DEFAULT) && SD_MMC.cardType() != CARD_NONE) { @@ -22276,7 +22201,22 @@ static bool tanSdTryMount() { s_tan_sd_size = SD_MMC.cardSize(); return true; } +#if defined(HAS_WIO_TRACKER_L2) + // On the L2 the card is not just a browse target: when boot adopted it, + // identity, prefs, contacts and chat history all live on it. end() + cutting + // the power rail here would turn one transient read error into total storage + // loss for the rest of the boot, with every later write failing silently. + // Leave the mount alone and just back off — the browse attempt is what fails, + // not the store. Only a card the store never adopted may be powered down. + // (The file-scope extern above is inside a T-Deck/Pager/M9 #if, so redeclare here.) + extern bool g_full_data_on_sd; // main.cpp: boot adopted the SD_MMC profile + if (!g_full_data_on_sd) { + SD_MMC.end(); + (void)WioTrackerL2Io::setSdPower(false); + } +#else SD_MMC.end(); +#endif s_tan_sd_retry_after = millis() + 8000; // don't grind a missing card on every roots render return false; } @@ -22348,7 +22288,7 @@ static void fmShowRoots() { lv_obj_add_event_cb(sd, fmSdPagerRetryMountCb, LV_EVENT_CLICKED, nullptr); } #endif -#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) +#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_WIO_TRACKER_L2) // microSD (SDMMC slot 0). Probe outside the mount-backoff window so an absent card doesn't grind // the bus on every render; tapping the row forces a fresh mount attempt (tanSdClickCb clears it). if ((s_tan_sd_mounted || millis() >= s_tan_sd_retry_after) && tanSdTryMount()) { @@ -40262,13 +40202,6 @@ static void ccGpsNoneCb(lv_event_t* e) { g_lv.task->showAlert(TR("No onboard GPS"), 1200); } #endif -// Day/Night chip: persist the opposite palette and rebuild the complete UI on restart. -static void ccThemeCb(lv_event_t* e) { - if (lv_event_get_code(e) != LV_EVENT_CLICKED) return; - closeControlCenter(); - themeModeRestart(s_theme_day ? TOUCH_THEME_NIGHT : TOUCH_THEME_DAY); -} - #if defined(HAS_THINKNODE_M9) || defined(TLORA_PAGER) static uint8_t s_cc_screenshot_delay_s = 3; static lv_timer_t* s_cc_screenshot_timer = nullptr; @@ -40787,6 +40720,15 @@ static void applyVolume(uint8_t pct) { if (pct > 100) pct = 100; s_volume_pct = pct; // play paths read the persisted pref; this keeps the slider live } +#elif defined(HAS_WIO_TRACKER_L2) +#define HAS_CC_BRIGHTNESS 1 +static uint8_t s_brightness_pct = 63; +static void applyBrightness(uint8_t pct) { + if (pct < 5) pct = 5; + if (pct > 100) pct = 100; + s_brightness_pct = pct; + display.setBrightness((uint8_t)((uint32_t)pct * 255u / 100u)); +} #elif defined(HAS_THINKNODE_M9) // M9: BL_EN (GPIO17) is a PNP transistor gate. LEDC PWM confirmed working on hardware // (Specter bring-up), but the duty is INVERTED — lower duty on the base = MORE conduction @@ -41222,8 +41164,6 @@ static void openControlCenter() { // settings reorg gave GPS its own category (the GPS block used to live under radio/device). ccToggle(row, LV_SYMBOL_GPS, TR("GPS"), gps_on, ccGpsCb, tw, th, CAT_GPS); #endif - ccToggle(row, s_theme_day ? TOUCH_SYM_SUN : TOUCH_SYM_MOON, TR("Theme"), s_theme_day, - ccThemeCb, tw, th, CAT_DISPLAY); #if CAP_KEYBOARD // Keyboard-backlight chip: the keyboard glyph WITH its off/on/auto mode word beneath it, // so the mode stays visible at a glance (sub_text stacks a small caption under the icon). @@ -43300,9 +43240,15 @@ static void statusBarReaderBackCb(lv_event_t* e) { // compression) is the quickest viewable format to emit — rows are a direct copy // of the RGB565 framebuffer (LV_COLOR_16_SWAP is 0). Saved to /screenshots. static void takeScreenshotToSd() { -#if CAP_SD || defined(TLORA_PAGER) +#if CAP_SD || defined(TLORA_PAGER) || defined(HAS_WIO_TRACKER_L2) auto toast = [&](const char* m){ if (g_lv.task) g_lv.task->showAlert(m, 1800); }; +#if defined(HAS_WIO_TRACKER_L2) + fs::FS& screenshotFs = SD_MMC; + if (SD_MMC.cardType() == CARD_NONE) { toast(TR("Screenshot: no SD card")); return; } +#else + fs::FS& screenshotFs = SD; if (SD.cardType() == CARD_NONE) { toast(TR("Screenshot: no SD card")); return; } +#endif const int W = lv_disp_get_hor_res(nullptr); const int H = lv_disp_get_ver_res(nullptr); lv_color_t* buf = (lv_color_t*)heap_caps_malloc((size_t)W * H * sizeof(lv_color_t), MALLOC_CAP_SPIRAM); @@ -43318,7 +43264,7 @@ static void takeScreenshotToSd() { g_shot_buf = nullptr; markSdIo(); - SD.mkdir("/screenshots"); + screenshotFs.mkdir("/screenshots"); char path[48]; const time_t now = time(nullptr); if (now > 1700000000) { @@ -43327,7 +43273,7 @@ static void takeScreenshotToSd() { } else { snprintf(path, sizeof path, "/screenshots/up_%lu.bmp", (unsigned long)millis()); } - File f = SD.open(path, FILE_WRITE); + File f = screenshotFs.open(path, FILE_WRITE); if (!f) { free(buf); toast(TR("Screenshot: write failed")); return; } const uint32_t row_bytes = (uint32_t)(((W * 16 + 31) / 32) * 4); // 4-byte aligned @@ -46723,7 +46669,7 @@ static void buildUiTree() { // Load the saved theme accent before any widget is built so the whole tree // adopts it. g_lv.tabview/keyboard are still null here, so applyAccent only // sets the colour globals (no live re-style needed at boot). - applyThemeMode(touchPrefsGetThemeMode()); + applyThemeMode(); applyAccent(touchPrefsGetAccentColor()); lv_obj_t* root = lv_scr_act(); @@ -47045,8 +46991,10 @@ static void buildUiTree() { }; // Both buttons use the rotation/refresh glyph; left vs. right position // tells the user which way the display will turn. +#if !defined(HAS_WIO_TRACKER_L2) s_kb_rot_left_btn = makeRotBtn(LV_SYMBOL_REFRESH, kbRotLeftCb); s_kb_rot_right_btn = makeRotBtn(LV_SYMBOL_REFRESH, kbRotRightCb); +#endif // On-screen language-cycle key (boards without a physical keyboard). Its label // is the active layout's 2-letter code; kbShowRotateArrows reveals it only when // a secondary layout is enabled. @@ -48324,6 +48272,9 @@ void UITask::flushHistoryIfDue(unsigned long now) { // the chat to the SD card. static fs::FS* s_ui_data_fs = nullptr; static char s_ui_data_root[16] = ""; +#if defined(HAS_WIO_TRACKER_L2) +extern bool g_full_data_on_sd; // boot adopted the SD_MMC profile +#endif #if defined(TLORA_PAGER) static bool s_ui_data_boot_finalized = false; #endif @@ -48355,7 +48306,17 @@ static bool uiDataFsReady() { return false; } #endif -#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) +#if defined(HAS_WIO_TRACKER_L2) + // Follow the boot-time DataStore decision. Never attach a card later in the + // same boot, because the in-memory ring was not loaded from that profile. + if (g_full_data_on_sd && SD_MMC.cardType() != CARD_NONE) { + SD_MMC.mkdir("/meshcomod"); + s_ui_data_fs = &SD_MMC; + strncpy(s_ui_data_root, "/meshcomod", sizeof s_ui_data_root - 1); + return true; + } + if (SPIFFS.begin(false)) { s_ui_data_fs = &SPIFFS; s_ui_data_root[0] = '\0'; return true; } +#elif defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) // Tanmatsu + T-Display P4: use each board's mounted internal data partition // (Tanmatsu FFat 'locfd', P4 LittleFS 'storage'), with SD_MMC as fallback. // #167: hot UI data (chat history) lives on the INTERNAL LittleFS -- SD write bursts @@ -49937,7 +49898,7 @@ void luaHostTextPrompt(const char* title, const char* initial, void (*cb)(const // message ring (MAX_UI_MESSAGES_SD) since the card has room for the bigger file. static bool uiDataFsIsSdCard() { if (!uiDataFsReady()) return false; -#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) +#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_WIO_TRACKER_L2) return s_ui_data_fs == &SD_MMC; #elif defined(HAS_TDECK_GT911) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HELTEC_LORA_V4_R8) return s_ui_data_fs == &SD; @@ -52372,7 +52333,15 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no } } #endif -#if !defined(HAS_TDISPLAY_P4) +#if defined(HAS_WIO_TRACKER_L2) + else if (SD_MMC.cardType() != CARD_NONE) { + s_tile_fs = &SD_MMC; + s_tile_root[0] = '\0'; + s_tiles_fs_ready = true; + WIRE_DBG("[TILE] wio-l2: caching Wi-Fi tiles on SD_MMC /tiles\n"); + } +#endif +#if !defined(HAS_TDISPLAY_P4) && !defined(HAS_WIO_TRACKER_L2) else { s_tile_fs = nullptr; // no cache backend at all -> map shows the storage notice } @@ -52628,7 +52597,7 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no g_draw_buffer = (lv_color_t*)heap_caps_malloc(buf_bytes, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); if (!g_draw_buffer) g_draw_buffer = (lv_color_t*)malloc(buf_bytes); #else -#if defined(HAS_RAK_TAP_V2) +#if defined(HAS_RAK_TAP_V2) || defined(HAS_WIO_TRACKER_L2) const int draw_band_w = 320; #else const int draw_band_w = 240; @@ -52729,6 +52698,10 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no // must match so LVGL renders the full 320x240 landscape surface. s_ui_rotation = LV_DISP_ROT_270; #endif + #if defined(HAS_WIO_TRACKER_L2) + // The panel driver exposes its fixed 320x240 landscape surface directly. + s_ui_rotation = LV_DISP_ROT_NONE; + #endif #if defined(ATTAKY_MESH_SERIES) // Display and touch share this landscape transform. s_ui_rotation = LV_DISP_ROT_90; @@ -52826,6 +52799,9 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no // display for the browser without changing the physical placeholder panel. g_lv.disp_drv.hor_res = (s_remote_mode && !s_remote_landscape) ? 222 : 480; g_lv.disp_drv.ver_res = (s_remote_mode && !s_remote_landscape) ? 480 : 222; +#elif defined(HAS_WIO_TRACKER_L2) + g_lv.disp_drv.hor_res = 320; + g_lv.disp_drv.ver_res = 240; #else // Landscape rotates the panel in HARDWARE (smooth — no per-pixel software // rotation each flush), so tell LVGL the already-rotated resolution and let @@ -52907,7 +52883,7 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no // accent itself), so without this its accent-tinted glyphs — the Wi-Fi/Bluetooth // icons — would freeze the compile-time default instead of the user's colour. // Idempotent: buildUiTree's own call just re-sets the same globals. - applyThemeMode(touchPrefsGetThemeMode()); + applyThemeMode(); applyAccent(touchPrefsGetAccentColor()); // Build the always-on top status bar AFTER the display driver is @@ -55388,11 +55364,23 @@ void UITask::loop() { { static bool s_user_btn_inited = false; static uint8_t s_user_btn_prev = HIGH; + auto readUserButton = []() { + uint8_t state = digitalRead(PIN_USER_BTN); +#if defined(HAS_WIO_TRACKER_L2) + static bool expanderDown = false; + if (digitalRead(45) == LOW || expanderDown) { + bool pressed = false; + if (WioTrackerL2Io::readWakeButton(pressed)) expanderDown = pressed; + } + if (expanderDown) state = LOW; +#endif + return state; + }; if (!s_user_btn_inited) { - s_user_btn_prev = digitalRead(PIN_USER_BTN); + s_user_btn_prev = readUserButton(); s_user_btn_inited = true; } - uint8_t v = digitalRead(PIN_USER_BTN); + uint8_t v = readUserButton(); #if CAP_TRACKBALL /* On the T-Deck, PIN_USER_BTN (GPIO0) is the trackball centre click — make * it act as a touch at the cursor: a held click = a held press (so taps, @@ -55449,6 +55437,23 @@ void UITask::loop() { s_tb_click_press = tb_pressed && !s_tb_wake_consume; if (s_tb_click_press) { s_tb_last_active_ms = now; noteUserInput(); } } +#elif defined(HAS_WIO_TRACKER_L2) + // Either physical button wakes immediately. While awake, a two-second hold + // turns the display off without hard-locking touch input. + static uint32_t s_l2_btn_down_ms = 0; + static bool s_l2_hold_fired = false; + if (v == LOW && s_user_btn_prev == HIGH) { + if (_screen_off) wakeScreen(); + s_l2_btn_down_ms = now; + s_l2_hold_fired = false; + } else if (v == LOW && s_user_btn_prev == LOW && !_screen_off && + !s_l2_hold_fired && now - s_l2_btn_down_ms >= 2000) { + s_l2_hold_fired = true; + touchScreenBacklight(false); + setCpuForScreen(false); + _screen_off = true; + _manual_lock = false; + } #elif defined(HAS_RAK_TAP_V2) // RAK Tap V2: single BOOT button (GPIO0), no trackball, no keyboard. // Short press (<1s): toggle screen on/off (does NOT hard-lock -- touch diff --git a/src/ui-touch/device_caps.h b/src/ui-touch/device_caps.h index 140de2e6..618fa113 100644 --- a/src/ui-touch/device_caps.h +++ b/src/ui-touch/device_caps.h @@ -25,7 +25,17 @@ // ============================================================================= // ---- Per-board structural capabilities (factored out of the device names) ---- -#if defined(HAS_TDECK_GT911) // ===== LilyGo T-Deck (ESP32-S3) ===== +#if defined(HAS_WIO_TRACKER_L2) // ===== Seeed Wio Tracker L2 (ESP32-S3) ===== + #define CAP_TOUCH 1 + #define CAP_ROTATABLE 0 + #define CAP_LARGE_SCREEN 0 // fixed 320x240 landscape + #define CAP_SD 0 // SD_MMC, not Arduino SD + #define CAP_FILESYSTEM 1 + #define CAP_GPS 1 + #define CAP_OTA 1 + #define CAP_LOCK_SCREEN 1 + +#elif defined(HAS_TDECK_GT911) // ===== LilyGo T-Deck (ESP32-S3) ===== #define CAP_TOUCH 1 // capacitive touchscreen (pointer input) #define CAP_ROTATABLE 0 // panel is fixed landscape #define CAP_LARGE_SCREEN 0 // 320x240 @@ -178,33 +188,6 @@ #define CAP_KEYPAD_NAV 0 #endif -// Battery-backed clock that keeps wall time through a TRUE power-off (issue #383). -// This is a DECLARED hardware fact, never "the generic 0x51 probe found something": -// address-only auto-detection is what read the Pager's PCF85063A as a PCF8563, and -// what let a chipless T-Deck look RTC-backed. A board earns a 1 here only when its -// target.cpp drives a documented chip (HardwareRtcClock) or calls the core probe -// against a chip the schematic confirms. -// -// 1 does NOT mean "the time is right": the chip's own integrity bit can say its -// contents are untrustworthy after a power cut (the ThinkNode M9 report on #383), -// and a dead backup cell reads the same way. That is a RUNTIME question — ask -// rtc_clock.timeIsCurrent() / .source(), not this flag. Use this one for what is -// physically fitted: diagnostics wording, and whether to probe at all. -#if defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDISPLAY_P4) - #define CAP_HARDWARE_RTC 1 -#else - #define CAP_HARDWARE_RTC 0 -#endif - -// A true power cut can leave these boards without trustworthy wall time. The -// T-Deck has no RTC; the M9's PCF8563 can report lost integrity after shutdown. -// Both can opt into the bounded, pre-transport saved-Wi-Fi sync from #383. -#if defined(HAS_TDECK_GT911) || defined(HAS_THINKNODE_M9) - #define CAP_BOOT_TIME_SYNC 1 -#else - #define CAP_BOOT_TIME_SYNC 0 -#endif - // Round-cornered, tall/narrow phone-class panel (LilyGo T-Display P4, 568x1232). The // AMOLED corners are arcs, so content is inset from all four corners, and the very tall // aspect lets the status bar wrap to TWO rows (row 1 = name + clock, row 2 = the wifi/ diff --git a/variants/square/SquareBoard.cpp b/variants/wio_tracker_l2/WioTrackerL2Board.cpp similarity index 72% rename from variants/square/SquareBoard.cpp rename to variants/wio_tracker_l2/WioTrackerL2Board.cpp index d77bfd3a..11834bb5 100644 --- a/variants/square/SquareBoard.cpp +++ b/variants/wio_tracker_l2/WioTrackerL2Board.cpp @@ -1,33 +1,33 @@ // SPDX-License-Identifier: GPL-3.0-or-later -#include "SquareBoard.h" +#include "WioTrackerL2Board.h" #include -#include "SquareIo.h" +#include "WioTrackerL2Io.h" -void SquareBoard::begin() { +void WioTrackerL2Board::begin() { ESP32Board::begin(); Wire.setClock(100000); - if (!SquareIo::begin()) Serial.println("[square] expander unavailable"); + if (!WioTrackerL2Io::begin()) Serial.println("[wio-l2] expander unavailable"); } -void SquareBoard::powerOff() { - (void)SquareIo::setAudioPaPower(false); - (void)SquareIo::setSdPower(false); - (void)SquareIo::setGnssPower(false); - (void)SquareIo::setLcdPower(false); +void WioTrackerL2Board::powerOff() { + (void)WioTrackerL2Io::setAudioPaPower(false); + (void)WioTrackerL2Io::setSdPower(false); + (void)WioTrackerL2Io::setGnssPower(false); + (void)WioTrackerL2Io::setLcdPower(false); enterDeepSleep(0); } -bool SquareBoard::ensureBatteryAdc() { +bool WioTrackerL2Board::ensureBatteryAdc() { if (_batteryAdcReady) return true; const uint32_t now = millis(); if (_nextBatteryProbeMs && (int32_t)(now - _nextBatteryProbeMs) < 0) return false; - if (!SquareIo::ready() || !SquareIo::setBatterySense(true)) return false; + if (!WioTrackerL2Io::ready() || !WioTrackerL2Io::setBatterySense(true)) return false; delay(10); _batteryAdcReady = _batteryAdc.begin(0x48, &Wire); if (_batteryAdcReady) _batteryAdc.setGain(GAIN_TWO); - (void)SquareIo::setBatterySense(false); + (void)WioTrackerL2Io::setBatterySense(false); if (!_batteryAdcReady) _nextBatteryProbeMs = now + 15000; return _batteryAdcReady; } @@ -39,7 +39,7 @@ bool SquareBoard::ensureBatteryAdc() { // [STALL] ui:status of 3 s, 38 s and 94 s on bring-up hardware. Bounded here // instead. The chip converts in ~8 ms at the default 128 SPS; 50 ms is slack, // and the delay(1) yields to IDLE so the task watchdog stays fed. -int16_t SquareBoard::readBatteryAdcBounded() { +int16_t WioTrackerL2Board::readBatteryAdcBounded() { _batteryAdc.startADCReading(ADS1X15_REG_CONFIG_MUX_SINGLE_0, /*continuous=*/false); const uint32_t deadline = millis() + 50; while (!_batteryAdc.conversionComplete()) { @@ -49,10 +49,10 @@ int16_t SquareBoard::readBatteryAdcBounded() { return _batteryAdc.getLastConversionResults(); } -uint16_t SquareBoard::getBattMilliVolts() { +uint16_t WioTrackerL2Board::getBattMilliVolts() { const uint32_t now = millis(); if (_lastBatteryMv && (uint32_t)(now - _lastBatteryReadMs) < 30000) return _lastBatteryMv; - if (!ensureBatteryAdc() || !SquareIo::setBatterySense(true)) return _lastBatteryMv; + if (!ensureBatteryAdc() || !WioTrackerL2Io::setBatterySense(true)) return _lastBatteryMv; delay(10); float volts = 0.0f; uint8_t samples = 0; @@ -60,7 +60,7 @@ uint16_t SquareBoard::getBattMilliVolts() { const int16_t raw = readBatteryAdcBounded(); if (raw > 0) { volts += _batteryAdc.computeVolts(raw); ++samples; } } - (void)SquareIo::setBatterySense(false); + (void)WioTrackerL2Io::setBatterySense(false); if (samples) { _lastBatteryMv = (uint16_t)((volts / samples) * 2000.0f); _lastBatteryReadMs = now; @@ -76,7 +76,7 @@ uint16_t SquareBoard::getBattMilliVolts() { return _lastBatteryMv; } -bool SquareBoard::sdCardPresent() { +bool WioTrackerL2Board::sdCardPresent() { bool present = false; - return SquareIo::readSdPresent(present) && present; + return WioTrackerL2Io::readSdPresent(present) && present; } \ No newline at end of file diff --git a/variants/square/SquareBoard.h b/variants/wio_tracker_l2/WioTrackerL2Board.h similarity index 79% rename from variants/square/SquareBoard.h rename to variants/wio_tracker_l2/WioTrackerL2Board.h index 2550a1c8..a492c49b 100644 --- a/variants/square/SquareBoard.h +++ b/variants/wio_tracker_l2/WioTrackerL2Board.h @@ -4,12 +4,12 @@ #include #include -class SquareBoard : public ESP32Board { +class WioTrackerL2Board : public ESP32Board { public: void begin(); void powerOff() override; uint16_t getBattMilliVolts() override; - const char* getManufacturerName() const override { return "square"; } + const char* getManufacturerName() const override { return "Seeed Wio Tracker L2"; } bool sdCardPresent(); private: diff --git a/variants/square/SquareDisplay.cpp b/variants/wio_tracker_l2/WioTrackerL2Display.cpp similarity index 62% rename from variants/square/SquareDisplay.cpp rename to variants/wio_tracker_l2/WioTrackerL2Display.cpp index e15af66e..f9d34a2f 100644 --- a/variants/square/SquareDisplay.cpp +++ b/variants/wio_tracker_l2/WioTrackerL2Display.cpp @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -#include "SquareDisplay.h" +#include "WioTrackerL2Display.h" #include #include @@ -10,14 +10,14 @@ constexpr uint8_t kLightUpdate = 0x0F; constexpr uint8_t kLightPwm0 = 0x18; } -bool SquareLight::writeRegister(uint8_t reg, uint8_t value) { +bool WioTrackerL2Light::writeRegister(uint8_t reg, uint8_t value) { Wire.beginTransmission(kLightAddress); Wire.write(reg); Wire.write(value); return Wire.endTransmission() == 0; } -bool SquareLight::init(uint8_t brightness) { +bool WioTrackerL2Light::init(uint8_t brightness) { bool ok = true; ok = writeRegister(0x00, 0x01) && ok; ok = writeRegister(0x01, 0x01) && ok; @@ -30,13 +30,13 @@ bool SquareLight::init(uint8_t brightness) { return ok; } -void SquareLight::setBrightness(uint8_t brightness) { +void WioTrackerL2Light::setBrightness(uint8_t brightness) { for (uint8_t channel = 0; channel < 4; ++channel) (void)writeRegister(kLightPwm0 + channel, brightness); (void)writeRegister(kLightUpdate, 0x55); _brightness = brightness; } -SquareDisplay::SquareDisplay() : DisplayDriver(320, 240) { +WioTrackerL2Display::WioTrackerL2Display() : DisplayDriver(320, 240) { { auto cfg = _bus.config(); cfg.spi_host = SPI3_HOST; @@ -95,7 +95,7 @@ SquareDisplay::SquareDisplay() : DisplayDriver(320, 240) { _lcd.setPanel(&_panel); } -bool SquareDisplay::begin() { +bool WioTrackerL2Display::begin() { if (_isOn) return true; (void)_light.init(160); _lcd.init(); @@ -107,25 +107,25 @@ bool SquareDisplay::begin() { _lcd.fillScreen(0); setLogicalSize(_lcd.width(), _lcd.height()); _isOn = true; - Serial.printf("[square] display %dx%d\n", width(), height()); + Serial.printf("[wio-l2] display %dx%d\n", width(), height()); return true; } -void SquareDisplay::turnOn() { if (!_isOn) { _lcd.setBrightness(160); _isOn = true; } } -void SquareDisplay::turnOff() { if (_isOn) { _lcd.setBrightness(0); _isOn = false; } } -void SquareDisplay::clear() { _lcd.fillScreen(0); } -void SquareDisplay::startFrame(ColorVal bkg) { _lcd.fillScreen(bkg); } -void SquareDisplay::setTextSize(int sz) { _lcd.setTextSize(sz); } -void SquareDisplay::setColor(ColorVal c) { _color = c; _lcd.setTextColor(c); } -void SquareDisplay::setCursor(int x, int y) { _lcd.setCursor(x, y); } -void SquareDisplay::print(const char* str) { _lcd.print(str); } -void SquareDisplay::fillRect(int x, int y, int w, int h) { _lcd.fillRect(x, y, w, h, _color); } -void SquareDisplay::drawRect(int x, int y, int w, int h) { _lcd.drawRect(x, y, w, h, _color); } -void SquareDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) { _lcd.drawXBitmap(x, y, bits, w, h, _color); } -uint16_t SquareDisplay::getTextWidth(const char* str) { return _lcd.textWidth(str); } -void SquareDisplay::endFrame() {} +void WioTrackerL2Display::turnOn() { if (!_isOn) { _lcd.setBrightness(160); _isOn = true; } } +void WioTrackerL2Display::turnOff() { if (_isOn) { _lcd.setBrightness(0); _isOn = false; } } +void WioTrackerL2Display::clear() { _lcd.fillScreen(0); } +void WioTrackerL2Display::startFrame(ColorVal bkg) { _lcd.fillScreen(bkg); } +void WioTrackerL2Display::setTextSize(int sz) { _lcd.setTextSize(sz); } +void WioTrackerL2Display::setColor(ColorVal c) { _color = c; _lcd.setTextColor(c); } +void WioTrackerL2Display::setCursor(int x, int y) { _lcd.setCursor(x, y); } +void WioTrackerL2Display::print(const char* str) { _lcd.print(str); } +void WioTrackerL2Display::fillRect(int x, int y, int w, int h) { _lcd.fillRect(x, y, w, h, _color); } +void WioTrackerL2Display::drawRect(int x, int y, int w, int h) { _lcd.drawRect(x, y, w, h, _color); } +void WioTrackerL2Display::drawXbm(int x, int y, const uint8_t* bits, int w, int h) { _lcd.drawXBitmap(x, y, bits, w, h, _color); } +uint16_t WioTrackerL2Display::getTextWidth(const char* str) { return _lcd.textWidth(str); } +void WioTrackerL2Display::endFrame() {} -void SquareDisplay::writePixelsRGB565(int x, int y, int w, int h, const uint16_t* pixels) { +void WioTrackerL2Display::writePixelsRGB565(int x, int y, int w, int h, const uint16_t* pixels) { if (!_isOn || !pixels || w <= 0 || h <= 0) return; _lcd.startWrite(); _lcd.setAddrWindow(x, y, w, h); @@ -133,16 +133,16 @@ void SquareDisplay::writePixelsRGB565(int x, int y, int w, int h, const uint16_t _lcd.endWrite(); } -void SquareDisplay::setDisplayRotation(uint8_t) { +void WioTrackerL2Display::setDisplayRotation(uint8_t) { _lcd.setRotation(0); setLogicalSize(_lcd.width(), _lcd.height()); } -void SquareDisplay::setBrightness(uint8_t brightness) { +void WioTrackerL2Display::setBrightness(uint8_t brightness) { _lcd.setBrightness(brightness); } -bool SquareDisplay::getTouchPoint(uint16_t& x, uint16_t& y) { +bool WioTrackerL2Display::getTouchPoint(uint16_t& x, uint16_t& y) { int32_t tx = 0, ty = 0; if (!_lcd.getTouch(&tx, &ty)) return false; x = (uint16_t)tx; diff --git a/variants/square/SquareDisplay.h b/variants/wio_tracker_l2/WioTrackerL2Display.h similarity index 90% rename from variants/square/SquareDisplay.h rename to variants/wio_tracker_l2/WioTrackerL2Display.h index d9ac560c..0eab52b2 100644 --- a/variants/square/SquareDisplay.h +++ b/variants/wio_tracker_l2/WioTrackerL2Display.h @@ -7,7 +7,7 @@ #include #include -class SquareLight : public lgfx::v1::ILight { +class WioTrackerL2Light : public lgfx::v1::ILight { public: bool init(uint8_t brightness) override; void setBrightness(uint8_t brightness) override; @@ -18,9 +18,9 @@ private: uint8_t _brightness = 160; }; -class SquareDisplay : public DisplayDriver { +class WioTrackerL2Display : public DisplayDriver { public: - SquareDisplay(); + WioTrackerL2Display(); bool begin(); bool isOn() override { return _isOn; } @@ -46,7 +46,7 @@ public: private: lgfx::Panel_NV3031B _panel; lgfx::Bus_SPI _bus; - SquareLight _light; + WioTrackerL2Light _light; lgfx::Touch_GT911 _touch; lgfx::LGFX_Device _lcd; bool _isOn = false; diff --git a/variants/square/SquareIo.cpp b/variants/wio_tracker_l2/WioTrackerL2Io.cpp similarity index 96% rename from variants/square/SquareIo.cpp rename to variants/wio_tracker_l2/WioTrackerL2Io.cpp index 8c6bac72..0748ae0d 100644 --- a/variants/square/SquareIo.cpp +++ b/variants/wio_tracker_l2/WioTrackerL2Io.cpp @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -#include "SquareIo.h" +#include "WioTrackerL2Io.h" #include #include @@ -87,7 +87,7 @@ bool readInputBit(uint8_t bit, bool& high) { } // namespace -namespace SquareIo { +namespace WioTrackerL2Io { bool begin() { s_ready = false; @@ -97,7 +97,7 @@ bool begin() { if (!readRegisters(kRegOutput0, s_output, sizeof(s_output)) || !readRegisters(kRegConfig0, s_config, sizeof(s_config))) { - Serial.println("[square] expander probe failed"); + Serial.println("[wio-l2] expander probe failed"); return false; } @@ -137,7 +137,7 @@ bool begin() { delay(60); s_ready = true; - Serial.printf("[square] expander ready out=%02X/%02X cfg=%02X/%02X\n", + Serial.printf("[wio-l2] expander ready out=%02X/%02X cfg=%02X/%02X\n", s_output[0], s_output[1], s_config[0], s_config[1]); return true; } @@ -167,4 +167,4 @@ bool setSdPower(bool enabled) { return setOutput(kSdPower, enabled); } bool setBatterySense(bool enabled) { return setOutput(kBatterySense, enabled); } bool setAudioPaPower(bool enabled) { return setOutput(kAudioPa, enabled); } -} // namespace SquareIo \ No newline at end of file +} // namespace WioTrackerL2Io \ No newline at end of file diff --git a/variants/square/SquareIo.h b/variants/wio_tracker_l2/WioTrackerL2Io.h similarity index 89% rename from variants/square/SquareIo.h rename to variants/wio_tracker_l2/WioTrackerL2Io.h index b82f5e29..c6a37244 100644 --- a/variants/square/SquareIo.h +++ b/variants/wio_tracker_l2/WioTrackerL2Io.h @@ -3,7 +3,7 @@ #include -namespace SquareIo { +namespace WioTrackerL2Io { bool begin(); bool ready(); @@ -19,4 +19,4 @@ bool setSdPower(bool enabled); bool setBatterySense(bool enabled); bool setAudioPaPower(bool enabled); -} // namespace SquareIo \ No newline at end of file +} // namespace WioTrackerL2Io \ No newline at end of file diff --git a/variants/square/SquareTouch.cpp b/variants/wio_tracker_l2/WioTrackerL2Touch.cpp similarity index 91% rename from variants/square/SquareTouch.cpp rename to variants/wio_tracker_l2/WioTrackerL2Touch.cpp index a1151306..b543e370 100644 --- a/variants/square/SquareTouch.cpp +++ b/variants/wio_tracker_l2/WioTrackerL2Touch.cpp @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -#if defined(HAS_SQUARE) && defined(ESP32) +#if defined(HAS_WIO_TRACKER_L2) && defined(ESP32) #include #include @@ -7,9 +7,9 @@ #include #include -#include "SquareDisplay.h" +#include "WioTrackerL2Display.h" -extern SquareDisplay display; +extern WioTrackerL2Display display; namespace { bool s_ready = false; @@ -72,7 +72,7 @@ bool heltecV4CapTouchPopSwipe(int8_t* x, int8_t* y) { bool heltecV4CapTouchStartBackgroundPoll(uint32_t periodMs) { if (s_async || !s_ready) return false; s_periodMs = periodMs < 4 ? 4 : (periodMs > 100 ? 100 : periodMs); - if (xTaskCreatePinnedToCore(pollTask, "square_touch", 3072, nullptr, 2, &s_task, 0) != pdPASS) return false; + if (xTaskCreatePinnedToCore(pollTask, "wio_l2_touch", 3072, nullptr, 2, &s_task, 0) != pdPASS) return false; s_async = true; return true; } bool heltecV4CapTouchIsAsyncPolling() { return s_async; } @@ -80,7 +80,7 @@ bool heltecV4CapTouchIsSwiping() { return s_swiping; } void heltecV4CapTouchSetRotation(uint8_t) {} void heltecV4CapTouchSetPointRotation(uint8_t) {} void heltecV4CapTouchSetSlowPoll(bool slow) { s_periodMs = slow ? 50 : 8; } -const char* heltecV4CapTouchDebug() { return "square GT911"; } +const char* heltecV4CapTouchDebug() { return "Wio Tracker L2 GT911"; } void heltecV4CapTouchGetRaw(uint16_t* x, uint16_t* y) { if (x) *x = s_x; if (y) *y = s_y; } #endif \ No newline at end of file diff --git a/variants/square/target.cpp b/variants/wio_tracker_l2/target.cpp similarity index 93% rename from variants/square/target.cpp rename to variants/wio_tracker_l2/target.cpp index c83cb9cc..1a4e01ee 100644 --- a/variants/square/target.cpp +++ b/variants/wio_tracker_l2/target.cpp @@ -3,7 +3,7 @@ #include "target.h" -SquareBoard board; +WioTrackerL2Board board; static SPIClass radioSpi(FSPI); RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, radioSpi); @@ -19,7 +19,7 @@ EnvironmentSensorManager sensors(gps); EnvironmentSensorManager sensors; #endif -SquareDisplay display; +WioTrackerL2Display display; MomentaryButton user_btn(PIN_USER_BTN, 1000, true); bool radio_init() { diff --git a/variants/square/target.h b/variants/wio_tracker_l2/target.h similarity index 79% rename from variants/square/target.h rename to variants/wio_tracker_l2/target.h index 82a3f15b..8d33772d 100644 --- a/variants/square/target.h +++ b/variants/wio_tracker_l2/target.h @@ -12,15 +12,15 @@ #include "helpers/sensors/EnvironmentSensorManager.h" #include "helpers/sensors/MicroNMEALocationProvider.h" -#include "SquareBoard.h" -#include "SquareDisplay.h" +#include "WioTrackerL2Board.h" +#include "WioTrackerL2Display.h" -extern SquareBoard board; +extern WioTrackerL2Board board; extern RADIO_CLASS radio; extern WRAPPER_CLASS radio_driver; extern ClockFloorRTC rtc_clock; extern EnvironmentSensorManager sensors; -extern SquareDisplay display; +extern WioTrackerL2Display display; extern MomentaryButton user_btn; bool radio_init();