diff --git a/lib/hal/HalClock.cpp b/lib/hal/HalClock.cpp index 6e3c3a90..6115535d 100644 --- a/lib/hal/HalClock.cpp +++ b/lib/hal/HalClock.cpp @@ -18,8 +18,11 @@ static uint8_t bcdToDec(uint8_t bcd) { return ((bcd >> 4) * 10) + (bcd & 0x0F); static uint8_t decToBcd(uint8_t dec) { return ((dec / 10) << 4) | (dec % 10); } void HalClock::begin() { + _usesSdkRtc = false; if (!gpio.deviceIsX3()) { - _available = false; + _available = _sdkRtc.begin(); + _usesSdkRtc = _available; + LOG_INF("CLK", _available ? "SDK RTC found" : "RTC not found"); return; } @@ -57,6 +60,24 @@ bool HalClock::getTime(uint8_t& hour, uint8_t& minute) const { return true; } + if (_usesSdkRtc) { + Rtc::DateTime dt; + if (!_sdkRtc.now(dt)) { + if (!_hasCachedTime) return false; + _lastPollMs = now; + hour = _cachedHour; + minute = _cachedMinute; + return true; + } + _cachedHour = dt.hour; + _cachedMinute = dt.minute; + _lastPollMs = now; + _hasCachedTime = true; + hour = _cachedHour; + minute = _cachedMinute; + return true; + } + // Read 3 bytes starting at register 0x00: seconds, minutes, hours Wire.beginTransmission(I2C_ADDR_DS3231); Wire.write(DS3231_SEC_REG); @@ -131,6 +152,25 @@ bool HalClock::writeTimeToRTC(uint8_t hour, uint8_t minute, uint8_t second) { assert(hour < 24); assert(minute < 60); assert(second < 60); + if (_usesSdkRtc) { + Rtc::DateTime dt; + dt.hour = hour; + dt.minute = minute; + dt.second = second; + dt.year = 2000; + dt.month = 1; + dt.day = 1; + if (!_sdkRtc.set(dt)) { + LOG_ERR("CLK", "Failed to write time to SDK RTC"); + return false; + } + _lastPollMs = 0; + _cachedHour = hour; + _cachedMinute = minute; + _hasCachedTime = true; + return true; + } + Wire.beginTransmission(I2C_ADDR_DS3231); Wire.write(DS3231_SEC_REG); // Start at register 0x00 Wire.write(decToBcd(second)); // 0x00: Seconds @@ -168,6 +208,27 @@ bool HalClock::syncFromNTP() { struct tm timeinfo; gmtime_r(&now, &timeinfo); + if (_usesSdkRtc) { + Rtc::DateTime dt; + dt.year = static_cast(timeinfo.tm_year + 1900); + dt.month = static_cast(timeinfo.tm_mon + 1); + dt.day = static_cast(timeinfo.tm_mday); + dt.hour = static_cast(timeinfo.tm_hour); + dt.minute = static_cast(timeinfo.tm_min); + dt.second = static_cast(timeinfo.tm_sec); + dt.weekday = static_cast(timeinfo.tm_wday); + if (_sdkRtc.set(dt)) { + _lastPollMs = 0; + _cachedHour = dt.hour; + _cachedMinute = dt.minute; + _hasCachedTime = true; + LOG_INF("CLK", "RTC set to %04u-%02u-%02u %02u:%02u:%02u UTC", dt.year, dt.month, dt.day, dt.hour, + dt.minute, dt.second); + return true; + } + return false; + } + if (writeTimeToRTC(timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec)) { LOG_INF("CLK", "RTC set to %02d:%02d:%02d UTC", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec); return true; diff --git a/lib/hal/HalClock.h b/lib/hal/HalClock.h index a0cc4825..20497332 100644 --- a/lib/hal/HalClock.h +++ b/lib/hal/HalClock.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include "HalGPIO.h" @@ -10,6 +11,8 @@ extern HalClock halClock; // Singleton class HalClock { bool _available = false; + bool _usesSdkRtc = false; + mutable Rtc _sdkRtc; mutable uint8_t _cachedHour = 0; mutable uint8_t _cachedMinute = 0; mutable bool _hasCachedTime = false; diff --git a/lib/hal/HalTiltSensor.cpp b/lib/hal/HalTiltSensor.cpp index b5808cca..a84447e2 100644 --- a/lib/hal/HalTiltSensor.cpp +++ b/lib/hal/HalTiltSensor.cpp @@ -26,6 +26,15 @@ bool HalTiltSensor::readReg(uint8_t reg, uint8_t* val) const { } bool HalTiltSensor::readGyro(float& gx, float& gy, float& gz) const { + if (_backend == Backend::SdkImu) { + Imu::Sample sample; + if (!_sdkImu.read(sample)) return false; + gx = sample.gx; + gy = sample.gy; + gz = sample.gz; + return true; + } + Wire.beginTransmission(_i2cAddr); Wire.write(REG_GX_L); // Start reading at Gyro X Low if (Wire.endTransmission(false) != 0) { @@ -52,11 +61,22 @@ bool HalTiltSensor::readGyro(float& gx, float& gy, float& gz) const { } void HalTiltSensor::begin() { + _backend = Backend::None; if (!gpio.deviceIsX3()) { - _available = false; + _available = _sdkImu.begin(); + if (!_available) { + LOG_ERR("GYR", "SDK IMU not found"); + return; + } + _backend = Backend::SdkImu; + _initMs = millis(); + _lastPollMs = millis(); + LOG_INF("GYR", "SDK IMU initialized"); return; } + _backend = Backend::Qmi8658; + // Try primary address, then alternate uint8_t whoami = 0; _i2cAddr = I2C_ADDR_QMI8658; @@ -89,6 +109,14 @@ bool HalTiltSensor::wake() { return false; } + if (_backend == Backend::SdkImu) { + _lastPollMs = millis(); + _lastTiltMs = millis(); + _wakeMs = millis(); + _isAwake = true; + return true; + } + // Wait for init to complete before waking if ((millis() - _initMs) < SLEEP_STABILIZE_MS) { return false; @@ -111,6 +139,13 @@ bool HalTiltSensor::deepSleep() { return false; } + if (_backend == Backend::SdkImu) { + clearPendingEvents(); + _inTilt = false; + _isAwake = false; + return true; + } + if ((millis() - _wakeMs) < SLEEP_STABILIZE_MS) { return false; } diff --git a/lib/hal/HalTiltSensor.h b/lib/hal/HalTiltSensor.h index ef132183..09576ca3 100644 --- a/lib/hal/HalTiltSensor.h +++ b/lib/hal/HalTiltSensor.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include "HalGPIO.h" @@ -18,8 +19,12 @@ class HalTiltSensor; extern HalTiltSensor halTiltSensor; // Singleton class HalTiltSensor { + enum class Backend : uint8_t { None, Qmi8658, SdkImu }; + bool _available = false; + Backend _backend = Backend::None; uint8_t _i2cAddr = 0; + mutable Imu _sdkImu; // Tilt gesture state machine bool _tiltForwardEvent = false; // Consumed by wasTiltedForward() diff --git a/platformio.ini b/platformio.ini index 5c94f4ac..34339a5e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -66,6 +66,8 @@ lib_deps = SDCardManager=symlink://freeink-sdk/libs/hardware/SDCardManager BoardConfig=symlink://freeink-sdk/libs/hardware/BoardConfig PowerManager=symlink://freeink-sdk/libs/hardware/PowerManager + Rtc=symlink://freeink-sdk/libs/hardware/Rtc + Imu=symlink://freeink-sdk/libs/hardware/Imu Icons=symlink://freeink-sdk/libs/assets/Icons bblanchon/ArduinoJson @ 7.4.2 ricmoo/QRCode @ 0.0.1 diff --git a/src/activities/settings/ClockOffsetActivity.cpp b/src/activities/settings/ClockOffsetActivity.cpp index 2000dacf..bc62402f 100644 --- a/src/activities/settings/ClockOffsetActivity.cpp +++ b/src/activities/settings/ClockOffsetActivity.cpp @@ -9,6 +9,7 @@ #include "CrossPointSettings.h" #include "MappedInputManager.h" +#include "components/TouchRegistry.h" #include "components/UITheme.h" #include "fontIds.h" @@ -108,14 +109,57 @@ void ClockOffsetActivity::adjustActiveField(int delta) { } } +void ClockOffsetActivity::selectRelativeField(const int delta) { + activeField = static_cast((activeField + delta + FIELD_COUNT) % FIELD_COUNT); +} + void ClockOffsetActivity::loop() { if (mappedInput.wasPressed(MappedInputManager::Button::Back)) { finish(); return; } + switch (mappedInput.wasSwipe()) { + case MappedInputManager::SwipeDir::Up: + adjustActiveField(+1); + requestUpdate(); + return; + case MappedInputManager::SwipeDir::Down: + adjustActiveField(-1); + requestUpdate(); + return; + case MappedInputManager::SwipeDir::Left: + selectRelativeField(+1); + requestUpdate(); + return; + case MappedInputManager::SwipeDir::Right: + selectRelativeField(-1); + requestUpdate(); + return; + case MappedInputManager::SwipeDir::None: + break; + } + + int downId = -1; + if (mappedInput.wasItemTouchedDown(downId) && downId >= 0 && downId < FIELD_COUNT) { + activeField = static_cast(downId); + requestUpdate(); + } + + int tappedId = -1; + if (mappedInput.wasItemTapped(tappedId) && tappedId >= 0 && tappedId < FIELD_COUNT) { + const Field tappedField = static_cast(tappedId); + if (activeField == tappedField) { + adjustActiveField(+1); + } else { + activeField = tappedField; + } + requestUpdate(); + return; + } + if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { - activeField = static_cast((activeField + 1) % FIELD_COUNT); + selectRelativeField(+1); requestUpdate(); return; } @@ -183,6 +227,8 @@ void ClockOffsetActivity::render(RenderLock&&) { } const int textX = boxX + (boxWidth - widthOf(text)) / 2; renderer.drawText(UI_12_FONT_ID, textX, centreY, text, true, EpdFontFamily::BOLD); + TouchRegistry::getInstance().add(Rect{boxX - 4, centreY - 8, boxWidth + 8, fieldHeight + 16}, + static_cast(field), TouchRegistry::Item); }; drawField(signStr, x, signBoxW, FIELD_SIGN); diff --git a/src/activities/settings/ClockOffsetActivity.h b/src/activities/settings/ClockOffsetActivity.h index 8f25ee4c..f8a69a76 100644 --- a/src/activities/settings/ClockOffsetActivity.h +++ b/src/activities/settings/ClockOffsetActivity.h @@ -34,4 +34,5 @@ class ClockOffsetActivity final : public Activity { void saveToSettings() const; void adjustActiveField(int delta); void clampForSign(); + void selectRelativeField(int delta); }; diff --git a/src/activities/settings/StatusBarSettingsActivity.cpp b/src/activities/settings/StatusBarSettingsActivity.cpp index 2df09254..ce456a44 100644 --- a/src/activities/settings/StatusBarSettingsActivity.cpp +++ b/src/activities/settings/StatusBarSettingsActivity.cpp @@ -123,6 +123,27 @@ void StatusBarSettingsActivity::loop() { return; } + const int pageItems = UITheme::getInstance().getNumberOfItemsPerPage(renderer, true, false, true, false); + const bool swiped = mappedInput.wasListScroll(selectedIndex, visibleItemCount, pageItems); + if (swiped) { + requestUpdate(); + return; + } + + int downId = -1; + if (mappedInput.wasItemTouchedDown(downId) && downId >= 0 && downId < visibleItemCount) { + selectedIndex = downId; + requestUpdate(); + } + + int tappedId = -1; + if (mappedInput.wasItemTapped(tappedId) && tappedId >= 0 && tappedId < visibleItemCount) { + selectedIndex = tappedId; + handleSelection(); + requestUpdate(); + return; + } + if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { handleSelection(); requestUpdate();