Add SDK RTC and IMU hardware abstraction support

Integrate SDK-based RTC and IMU backends as alternatives to direct hardware access. HalClock now attempts SDK RTC initialization and caches time values for reliability. HalTiltSensor adds SDK IMU backend with fallback logic. ClockOffsetActivity gains touch and swipe gesture support for field navigation.
This commit is contained in:
Justin Mitchell
2026-06-19 14:25:51 -04:00
parent 526cf1b6f9
commit 06ff5aa44a
8 changed files with 177 additions and 3 deletions
+62 -1
View File
@@ -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<uint16_t>(timeinfo.tm_year + 1900);
dt.month = static_cast<uint8_t>(timeinfo.tm_mon + 1);
dt.day = static_cast<uint8_t>(timeinfo.tm_mday);
dt.hour = static_cast<uint8_t>(timeinfo.tm_hour);
dt.minute = static_cast<uint8_t>(timeinfo.tm_min);
dt.second = static_cast<uint8_t>(timeinfo.tm_sec);
dt.weekday = static_cast<uint8_t>(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;
+3
View File
@@ -1,6 +1,7 @@
#pragma once
#include <Arduino.h>
#include <Rtc.h>
#include <Wire.h>
#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;
+36 -1
View File
@@ -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;
}
+5
View File
@@ -1,6 +1,7 @@
#pragma once
#include <Arduino.h>
#include <Imu.h>
#include <Wire.h>
#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()
+2
View File
@@ -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
@@ -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<Field>((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<Field>(downId);
requestUpdate();
}
int tappedId = -1;
if (mappedInput.wasItemTapped(tappedId) && tappedId >= 0 && tappedId < FIELD_COUNT) {
const Field tappedField = static_cast<Field>(tappedId);
if (activeField == tappedField) {
adjustActiveField(+1);
} else {
activeField = tappedField;
}
requestUpdate();
return;
}
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
activeField = static_cast<Field>((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<int>(field), TouchRegistry::Item);
};
drawField(signStr, x, signBoxW, FIELD_SIGN);
@@ -34,4 +34,5 @@ class ClockOffsetActivity final : public Activity {
void saveToSettings() const;
void adjustActiveField(int delta);
void clampForSign();
void selectRelativeField(int delta);
};
@@ -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();