feat: X3 gyroscope-based tilt page turning via QMI8658 IMU (#1636)
Co-authored-by: justinian <juicecutlus@gmail.com> Co-authored-by: Vincent Politzer <vincent@skipwithjoy.com>
This commit is contained in:
co-authored by
justinian
Vincent Politzer
parent
bc9651b664
commit
5d2e5596b4
@@ -290,3 +290,4 @@ STR_NO_FOOTNOTES: "На гэтай старонцы няма зносак"
|
||||
STR_LINK: "[спасылка]"
|
||||
STR_AUTO_TURN_ENABLED: "Аўтаперагортванне: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Аўтаперагортванне (старонак за хвіліну)"
|
||||
STR_TILT_PAGE_TURN: "Перагортванне нахілам"
|
||||
|
||||
@@ -290,3 +290,4 @@ STR_LINK: "[enllaç]"
|
||||
STR_SCREENSHOT_BUTTON: "Fes una captura de pantalla"
|
||||
STR_AUTO_TURN_ENABLED: "Passar automàtic activat: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Passar automàtic (pàgines per minut)"
|
||||
STR_TILT_PAGE_TURN: "Pas de pàgina per inclinació"
|
||||
|
||||
@@ -262,3 +262,4 @@ STR_BOOK_S_STYLE: "Styl knihy"
|
||||
STR_EMBEDDED_STYLE: "Vložený styl"
|
||||
STR_OPDS_SERVER_URL: "URL serveru OPDS"
|
||||
STR_SCREENSHOT_BUTTON: "Udělat snímek obrazovky"
|
||||
STR_TILT_PAGE_TURN: "Otáčení stránek nakloněním"
|
||||
|
||||
@@ -290,3 +290,4 @@ STR_LINK: "[link]"
|
||||
STR_SCREENSHOT_BUTTON: "Tag skærmbillede"
|
||||
STR_AUTO_TURN_ENABLED: "Automatisk sidevendning aktiveret: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Automatisk vending (sider per minut)"
|
||||
STR_TILT_PAGE_TURN: "Vip for at vende side"
|
||||
|
||||
@@ -289,4 +289,5 @@ STR_NO_FOOTNOTES: "Geen voetnoten op deze pagina"
|
||||
STR_LINK: "[link]"
|
||||
STR_SCREENSHOT_BUTTON: "Screenshot maken"
|
||||
STR_AUTO_TURN_ENABLED: "Automatisch omslaan ingeschakeld: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Autom. omslaan (pagina's per minuut)"
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Autom. omslaan (pagina's per minuut)"
|
||||
STR_TILT_PAGE_TURN: "Kantel om te bladeren"
|
||||
@@ -305,6 +305,7 @@ STR_CRASH_TITLE: "System Crash"
|
||||
STR_CRASH_DESCRIPTION: "A detailed report was saved to crash_report.txt. Please include this file in your bug report."
|
||||
STR_CRASH_REASON: "Crash reason:"
|
||||
STR_CRASH_NO_REASON: "(No reason was recorded)"
|
||||
STR_TILT_PAGE_TURN: "Tilt Page Turn"
|
||||
STR_KB_HINT_MOVE_CURSOR: "Press LEFT or RIGHT to move cursor"
|
||||
STR_KB_HINT_RETURN_CURSOR: "Press LEFT to return to cursor position"
|
||||
STR_KB_HINT_HIDE_PASSWORD: "Hold RIGHT then press [***] to hide password"
|
||||
|
||||
@@ -260,3 +260,4 @@ STR_BOOK_S_STYLE: "Kirjan tyyli"
|
||||
STR_EMBEDDED_STYLE: "Upotettu tyyli"
|
||||
STR_OPDS_SERVER_URL: "OPDS-palvelimen osoite"
|
||||
STR_SCREENSHOT_BUTTON: "Ota kuvakaappaus"
|
||||
STR_TILT_PAGE_TURN: "Sivunkääntö kallistamalla"
|
||||
|
||||
@@ -291,3 +291,4 @@ STR_LINK: "[lien]"
|
||||
STR_SCREENSHOT_BUTTON: "Capture d'écran"
|
||||
STR_AUTO_TURN_ENABLED: "Tourne-page auto : "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Tourne-page auto (pages par minute)"
|
||||
STR_TILT_PAGE_TURN: "Tourner par inclinaison"
|
||||
|
||||
@@ -321,3 +321,4 @@ STR_UPTIME: "Laufzeit"
|
||||
STR_CHARGING: "Lädt"
|
||||
STR_GATHERING_DATA: "Daten werden gesammelt..."
|
||||
STR_READING: "Lese..."
|
||||
STR_TILT_PAGE_TURN: "Neigung zum Blättern"
|
||||
|
||||
@@ -290,3 +290,4 @@ STR_LINK: "[link]"
|
||||
STR_SCREENSHOT_BUTTON: "Képernyőkép készítése"
|
||||
STR_AUTO_TURN_ENABLED: "Automatikus lapozás bekapcsolva: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Automatikus lapozás (oldal/perc)"
|
||||
STR_TILT_PAGE_TURN: "Döntéses lapozás"
|
||||
|
||||
@@ -290,3 +290,4 @@ STR_LINK: "[link]"
|
||||
STR_SCREENSHOT_BUTTON: "Screenshot"
|
||||
STR_AUTO_TURN_ENABLED: "Cambio pagina automatico: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Cambio pagina automatico (pag/min)"
|
||||
STR_TILT_PAGE_TURN: "Gira pagina con inclinazione"
|
||||
|
||||
@@ -289,3 +289,4 @@ STR_LINK: "[сілтеме]"
|
||||
STR_SCREENSHOT_BUTTON: "Скриншот түсіру"
|
||||
STR_AUTO_TURN_ENABLED: "Автоматты бет аудару қосулы: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Автоматты бет аудару (минутына бет саны)"
|
||||
STR_TILT_PAGE_TURN: "Еңкейту арқылы бет аудару"
|
||||
|
||||
@@ -290,3 +290,4 @@ STR_LINK: "[nuoroda]"
|
||||
STR_SCREENSHOT_BUTTON: "Ekrano nuotrauka"
|
||||
STR_AUTO_TURN_ENABLED: "Auto-vertimas: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Auto-vertimas (psl/min)"
|
||||
STR_TILT_PAGE_TURN: "Puslapio vertimas pakreipiant"
|
||||
|
||||
@@ -290,3 +290,4 @@ STR_LINK: "[link]"
|
||||
STR_SCREENSHOT_BUTTON: "Zrób zrzut ekranu"
|
||||
STR_AUTO_TURN_ENABLED: "Auto-kartkowanie: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Auto-kartkowanie (str./min)"
|
||||
STR_TILT_PAGE_TURN: "Kartkowanie przechyłem"
|
||||
|
||||
@@ -262,3 +262,4 @@ STR_BOOK_S_STYLE: "Estilo do livro"
|
||||
STR_EMBEDDED_STYLE: "Estilo embutido"
|
||||
STR_OPDS_SERVER_URL: "URL do servidor OPDS"
|
||||
STR_SCREENSHOT_BUTTON: "Capturar tela"
|
||||
STR_TILT_PAGE_TURN: "Virar página por inclinação"
|
||||
|
||||
@@ -290,3 +290,4 @@ STR_LINK: "[link]"
|
||||
STR_SCREENSHOT_BUTTON: "Captură ecran"
|
||||
STR_AUTO_TURN_ENABLED: "Răsfoire automată: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Pagini pe minut"
|
||||
STR_TILT_PAGE_TURN: "Întoarcere pagină prin înclinare"
|
||||
|
||||
@@ -291,6 +291,7 @@ STR_OPDS_SERVER_URL: "URL OPDS сервера"
|
||||
STR_SCREENSHOT_BUTTON: "Сделать снимок экрана"
|
||||
STR_AUTO_TURN_ENABLED: "Автоперелистывание: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Автоперелистывание (стр./мин)"
|
||||
STR_TILT_PAGE_TURN: "Перелистывание наклоном"
|
||||
STR_ADD_SERVER: "Добавить сервер"
|
||||
STR_SERVER_NAME: "Имя сервера"
|
||||
STR_NO_SERVERS: "Нет настроенных серверов OPDS"
|
||||
|
||||
@@ -290,3 +290,4 @@ STR_LINK: "[povezava]"
|
||||
STR_SCREENSHOT_BUTTON: "Naredi posnetek zaslona"
|
||||
STR_AUTO_TURN_ENABLED: "Samodejno obračanje: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Samodejno obračanje (strani na minuto)"
|
||||
STR_TILT_PAGE_TURN: "Obračanje s priklonom"
|
||||
|
||||
@@ -289,5 +289,6 @@ STR_FOOTNOTES: "Pie de página"
|
||||
STR_NO_FOOTNOTES: "No hay notas al pie de esta página"
|
||||
STR_LINK: "[enlace]"
|
||||
STR_SCREENSHOT_BUTTON: "Tomar captura de pantalla"
|
||||
STR_TILT_PAGE_TURN: "Pasar página inclinando"
|
||||
STR_AUTO_TURN_ENABLED: "Avance activado: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Avance auto. (pág./min)"
|
||||
|
||||
@@ -305,6 +305,7 @@ STR_CRASH_TITLE: "Systemkrasch"
|
||||
STR_CRASH_DESCRIPTION: "En detaljerad rapport sparades till crash_report.txt. Vänligen inkludera den här filen i din felrapport."
|
||||
STR_CRASH_REASON: "Orsak till kraschen:"
|
||||
STR_CRASH_NO_REASON: "(Ingen orsak registrerades)"
|
||||
STR_TILT_PAGE_TURN: "Luta för att vända sida"
|
||||
STR_KB_HINT_MOVE_CURSOR: "Tryck VÄNSTER eller HÖGER för att flytta markören"
|
||||
STR_KB_HINT_RETURN_CURSOR: "Tryck VÄNSTER för att återgå till markörpositionen"
|
||||
STR_KB_HINT_HIDE_PASSWORD: "Håll HÖGER och tryck sedan på [***] för att dölja lösenordet"
|
||||
|
||||
@@ -290,3 +290,4 @@ STR_SCREENSHOT_BUTTON: "Ekran görüntüsü al"
|
||||
STR_SELECTED: "Seçili"
|
||||
STR_SHOW: "Göster"
|
||||
STR_TITLE: "Başlık"
|
||||
STR_TILT_PAGE_TURN: "Eğerek sayfa çevirme"
|
||||
|
||||
@@ -291,3 +291,4 @@ STR_LINK: "[посилання]"
|
||||
STR_SCREENSHOT_BUTTON: "Знімок екрана"
|
||||
STR_AUTO_TURN_ENABLED: "Автоперегортання увімк: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Автоперегортання (ст/хв)"
|
||||
STR_TILT_PAGE_TURN: "Перегортання нахилом"
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
#include "HalTiltSensor.h"
|
||||
|
||||
#include <Logging.h>
|
||||
|
||||
HalTiltSensor halTiltSensor; // Singleton instance
|
||||
|
||||
bool HalTiltSensor::writeReg(uint8_t reg, uint8_t val) const {
|
||||
Wire.beginTransmission(_i2cAddr);
|
||||
Wire.write(reg);
|
||||
Wire.write(val);
|
||||
return Wire.endTransmission() == 0;
|
||||
}
|
||||
|
||||
bool HalTiltSensor::readReg(uint8_t reg, uint8_t* val) const {
|
||||
Wire.beginTransmission(_i2cAddr);
|
||||
Wire.write(reg);
|
||||
if (Wire.endTransmission(false) != 0) {
|
||||
return false;
|
||||
}
|
||||
Wire.requestFrom(_i2cAddr, (uint8_t)1);
|
||||
if (Wire.available() < 1) {
|
||||
return false;
|
||||
}
|
||||
*val = Wire.read();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HalTiltSensor::readGyro(float& gx, float& gy, float& gz) const {
|
||||
Wire.beginTransmission(_i2cAddr);
|
||||
Wire.write(REG_GX_L); // Start reading at Gyro X Low
|
||||
if (Wire.endTransmission(false) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Wire.requestFrom(_i2cAddr, (uint8_t)6);
|
||||
if (Wire.available() < 6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto readInt16 = [&]() -> int16_t {
|
||||
const uint8_t lo = Wire.read();
|
||||
const uint8_t hi = Wire.read();
|
||||
return static_cast<int16_t>((hi << 8) | lo);
|
||||
};
|
||||
|
||||
// If Full Scale is ±512 dps, the scale factor is 32768 / 512 = 64 LSB/dps
|
||||
constexpr float SCALE = 1.0f / 64.0f;
|
||||
gx = readInt16() * SCALE;
|
||||
gy = readInt16() * SCALE;
|
||||
gz = readInt16() * SCALE;
|
||||
return true;
|
||||
}
|
||||
|
||||
void HalTiltSensor::begin() {
|
||||
if (!gpio.deviceIsX3()) {
|
||||
_available = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Try primary address, then alternate
|
||||
uint8_t whoami = 0;
|
||||
_i2cAddr = I2C_ADDR_QMI8658;
|
||||
if (!readReg(QMI8658_WHO_AM_I_REG, &whoami) || whoami != QMI8658_WHO_AM_I_VALUE) {
|
||||
_i2cAddr = I2C_ADDR_QMI8658_ALT;
|
||||
if (!readReg(QMI8658_WHO_AM_I_REG, &whoami) || whoami != QMI8658_WHO_AM_I_VALUE) {
|
||||
LOG_ERR("GYR", "QMI8658 IMU not found");
|
||||
_available = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INF("GYR", "QMI8658 IMU found at 0x%02X", _i2cAddr);
|
||||
|
||||
if (!writeReg(REG_CTRL7, CTRL7_DISABLE_ALL) || !writeReg(REG_CTRL3, CTRL3_FS_512DPS | CTRL3_ODR_28HZ) ||
|
||||
!writeReg(REG_CTRL1, CTRL1_BASE | CTRL1_SENSOR_DISABLE)) {
|
||||
LOG_ERR("GYR", "QMI8658 register configuration failed");
|
||||
_available = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_available = true;
|
||||
_initMs = millis();
|
||||
_lastPollMs = millis();
|
||||
LOG_INF("GYR", "QMI8658 gyro initialized and put to sleep");
|
||||
}
|
||||
|
||||
bool HalTiltSensor::wake() {
|
||||
if (!_available) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for init to complete before waking
|
||||
if ((millis() - _initMs) < SLEEP_STABILIZE_MS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (writeReg(REG_CTRL1, CTRL1_BASE) && writeReg(REG_CTRL7, CTRL7_GYRO_ENABLE)) {
|
||||
_lastPollMs = millis();
|
||||
_lastTiltMs = millis();
|
||||
_wakeMs = millis();
|
||||
LOG_INF("GYR", "QMI8658 woke up");
|
||||
return true;
|
||||
} else {
|
||||
LOG_ERR("GYR", "Failed to wake QMI8658");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool HalTiltSensor::deepSleep() {
|
||||
if (!_available) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((millis() - _wakeMs) < SLEEP_STABILIZE_MS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (writeReg(REG_CTRL7, CTRL7_DISABLE_ALL) && writeReg(REG_CTRL1, CTRL1_BASE | CTRL1_SENSOR_DISABLE)) {
|
||||
// Clear any residual state so it doesn't immediately trigger upon waking
|
||||
clearPendingEvents();
|
||||
_inTilt = false;
|
||||
LOG_INF("GYR", "QMI8658 entered sleep mode");
|
||||
return true;
|
||||
} else {
|
||||
LOG_ERR("GYR", "Failed to put QMI8658 to sleep");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void HalTiltSensor::update(const uint8_t mode, const uint8_t orientation, const bool inReader) {
|
||||
if (!_available) {
|
||||
return;
|
||||
}
|
||||
|
||||
// State machine: wake up or sleep based on the enabled flag
|
||||
if ((mode != CrossPointTiltPageTurn::TILT_OFF) && !_isAwake) {
|
||||
_isAwake = wake();
|
||||
return;
|
||||
} else if ((mode == CrossPointTiltPageTurn::TILT_OFF) && _isAwake) {
|
||||
_isAwake = !deepSleep();
|
||||
return;
|
||||
}
|
||||
|
||||
// If disabled, skip the rest of the polling logic and avoid unnecessary I2C traffic in non-reader activities
|
||||
if ((mode == CrossPointTiltPageTurn::TILT_OFF) || !inReader) {
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned long now = millis();
|
||||
// Stabilization: discard readings during gyro startup transient
|
||||
if ((now - _wakeMs) < WAKE_STABILIZE_MS) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((now - _lastPollMs) < POLL_INTERVAL_MS) {
|
||||
return;
|
||||
}
|
||||
_lastPollMs = now;
|
||||
|
||||
float gx, gy, gz;
|
||||
if (!readGyro(gx, gy, gz)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Map the gyro axis to left/right tilt based on reader orientation.
|
||||
// On the X3 PCB: X axis = left/right in portrait, Y axis = left/right in landscape.
|
||||
float tiltAxis;
|
||||
switch (orientation) {
|
||||
case CrossPointOrientation::PORTRAIT:
|
||||
tiltAxis = mode == CrossPointTiltPageTurn::TILT_INVERTED ? -gx : gx;
|
||||
break;
|
||||
case CrossPointOrientation::INVERTED:
|
||||
tiltAxis = mode == CrossPointTiltPageTurn::TILT_INVERTED ? gx : -gx;
|
||||
break;
|
||||
case CrossPointOrientation::LANDSCAPE_CW:
|
||||
tiltAxis = mode == CrossPointTiltPageTurn::TILT_INVERTED ? gy : -gy;
|
||||
break;
|
||||
case CrossPointOrientation::LANDSCAPE_CCW:
|
||||
tiltAxis = mode == CrossPointTiltPageTurn::TILT_INVERTED ? -gy : gy;
|
||||
break;
|
||||
default:
|
||||
tiltAxis = gx;
|
||||
break;
|
||||
}
|
||||
|
||||
if (_inTilt) {
|
||||
// Wait for device to return to neutral before allowing next trigger
|
||||
if (fabsf(tiltAxis) < NEUTRAL_RATE_DPS) {
|
||||
_inTilt = false;
|
||||
}
|
||||
} else {
|
||||
// Check for new tilt gesture (with cooldown)
|
||||
if ((now - _lastTiltMs) >= COOLDOWN_MS) {
|
||||
if (tiltAxis > RATE_THRESHOLD_DPS) {
|
||||
_tiltForwardEvent = true;
|
||||
_hadActivity = true;
|
||||
_inTilt = true;
|
||||
_lastTiltMs = now;
|
||||
LOG_INF("GYR", "Forward Trigger=(%.1f) dps", tiltAxis);
|
||||
} else if (tiltAxis < -RATE_THRESHOLD_DPS) {
|
||||
_tiltBackEvent = true;
|
||||
_hadActivity = true;
|
||||
_inTilt = true;
|
||||
_lastTiltMs = now;
|
||||
LOG_INF("GYR", "Backward Trigger=(%.1f) dps", tiltAxis);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool HalTiltSensor::wasTiltedForward() {
|
||||
const bool val = _tiltForwardEvent;
|
||||
_tiltForwardEvent = false;
|
||||
return val;
|
||||
}
|
||||
|
||||
bool HalTiltSensor::wasTiltedBack() {
|
||||
const bool val = _tiltBackEvent;
|
||||
_tiltBackEvent = false;
|
||||
return val;
|
||||
}
|
||||
|
||||
bool HalTiltSensor::hadActivity() {
|
||||
const bool val = _hadActivity;
|
||||
_hadActivity = false;
|
||||
return val;
|
||||
}
|
||||
|
||||
void HalTiltSensor::clearPendingEvents() {
|
||||
_tiltForwardEvent = false;
|
||||
_tiltBackEvent = false;
|
||||
_hadActivity = false;
|
||||
// Intentionally preserve _inTilt so a held tilt doesn't retrigger on next poll
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
|
||||
#include "HalGPIO.h"
|
||||
|
||||
// TODO: Move enums into new header and share with CrossPointSettings.h
|
||||
namespace CrossPointOrientation {
|
||||
enum Value : uint8_t { PORTRAIT = 0, LANDSCAPE_CW = 1, INVERTED = 2, LANDSCAPE_CCW = 3 };
|
||||
}
|
||||
|
||||
namespace CrossPointTiltPageTurn {
|
||||
enum Value : uint8_t { TILT_OFF = 0, TILT_NORMAL = 1, TILT_INVERTED = 2 };
|
||||
}
|
||||
|
||||
class HalTiltSensor;
|
||||
extern HalTiltSensor halTiltSensor; // Singleton
|
||||
|
||||
class HalTiltSensor {
|
||||
bool _available = false;
|
||||
uint8_t _i2cAddr = 0;
|
||||
|
||||
// Tilt gesture state machine
|
||||
bool _tiltForwardEvent = false; // Consumed by wasTiltedForward()
|
||||
bool _tiltBackEvent = false; // Consumed by wasTiltedBack()
|
||||
bool _hadActivity = false; // Non-consuming flag for sleep timer
|
||||
bool _inTilt = false; // Currently tilted past threshold
|
||||
bool _isAwake = false; // Tracks power state
|
||||
unsigned long _initMs = 0; // Timestamp of sensor init
|
||||
unsigned long _lastTiltMs = 0; // Debounce / cooldown
|
||||
unsigned long _wakeMs = 0; // Timestamp of last wake() for stabilization
|
||||
|
||||
// Tuning constants
|
||||
static constexpr float RATE_THRESHOLD_DPS = 270.0f; // Deg/sec speed to trigger flick
|
||||
static constexpr float NEUTRAL_RATE_DPS = 50.0f; // Must stop moving below this rate before next trigger
|
||||
static constexpr unsigned long COOLDOWN_MS = 600; // Minimum ms between triggers
|
||||
static constexpr unsigned long POLL_INTERVAL_MS = 50; // 20 Hz polling
|
||||
static constexpr unsigned long WAKE_STABILIZE_MS = 300; // Ignore readings after wake
|
||||
static constexpr unsigned long SLEEP_STABILIZE_MS = 15; // Sleep turn on/off delay
|
||||
|
||||
mutable unsigned long _lastPollMs = 0;
|
||||
|
||||
// --- QMI8658 registers ---
|
||||
static constexpr uint8_t REG_CTRL1 = 0x02;
|
||||
static constexpr uint8_t REG_CTRL3 = 0x04;
|
||||
static constexpr uint8_t REG_CTRL7 = 0x08;
|
||||
static constexpr uint8_t REG_GX_L = 0x3B;
|
||||
|
||||
// --- Register Bit Flags ---
|
||||
|
||||
// REG_CTRL1 (0x02)
|
||||
static constexpr uint8_t CTRL1_BIG_ENDIAN = (1 << 5); // 0x20: Default state (1 = Big Endian)
|
||||
static constexpr uint8_t CTRL1_AUTO_INC = (1 << 6); // 0x40: Enable address auto-increment
|
||||
static constexpr uint8_t CTRL1_SENSOR_DISABLE = (1 << 0); // 0x01: Power down sensor engine
|
||||
static constexpr uint8_t CTRL1_BASE = CTRL1_AUTO_INC | CTRL1_BIG_ENDIAN; // 0x60
|
||||
|
||||
// REG_CTRL3 (0x04) - Gyro Config
|
||||
static constexpr uint8_t CTRL3_FS_512DPS = (0b101 << 4); // Bits 6:4 = 101
|
||||
static constexpr uint8_t CTRL3_ODR_28HZ = 0b1000; // Bits 3:0 = 1000 (28.025 Hz)
|
||||
|
||||
// REG_CTRL7 (0x08) - Enable
|
||||
static constexpr uint8_t CTRL7_DISABLE_ALL = 0x00;
|
||||
static constexpr uint8_t CTRL7_GYRO_ENABLE = (1 << 1); // Bit 1 = 1
|
||||
|
||||
bool writeReg(uint8_t reg, uint8_t val) const;
|
||||
bool readReg(uint8_t reg, uint8_t* val) const;
|
||||
bool readGyro(float& gx, float& gy, float& gz) const;
|
||||
|
||||
public:
|
||||
// Call after gpio.begin() and powerManager.begin() (I2C already initialised for X3)
|
||||
void begin();
|
||||
|
||||
// Enables the QMI8658 internal sensor engine
|
||||
bool wake();
|
||||
|
||||
// Puts the QMI8658 into a low-power standby state
|
||||
bool deepSleep();
|
||||
|
||||
// True if the QMI8658 IMU is present on this device
|
||||
bool isAvailable() const { return _available; }
|
||||
|
||||
// Poll the accelerometer and update tilt gesture state.
|
||||
void update(const uint8_t mode, const uint8_t orientation, const bool inReader);
|
||||
|
||||
// Returns true once per tilt-forward gesture (next page direction).
|
||||
// Consumed on read — subsequent calls return false until next gesture.
|
||||
bool wasTiltedForward();
|
||||
|
||||
// Returns true once per tilt-back gesture (previous page direction).
|
||||
// Consumed on read.
|
||||
bool wasTiltedBack();
|
||||
|
||||
// Non-consuming: true if any tilt activity occurred since last call.
|
||||
// Used to reset the auto-sleep inactivity timer.
|
||||
bool hadActivity();
|
||||
|
||||
// Discard any pending tilt events (call when leaving reader or disabling tilt).
|
||||
void clearPendingEvents();
|
||||
};
|
||||
Reference in New Issue
Block a user