From d0ad1b014cbe63dffc4a2598e27d46903b100892 Mon Sep 17 00:00:00 2001 From: Shadowtrance Date: Thu, 15 Oct 2026 07:26:43 +1000 Subject: [PATCH] IMU Updates (#694) * IMU Updates Updated BMI270 and MPU6886 with the mount-matrix. Updated devices i can test with the mount-matrix. papers3, stackchan (cores3), sticks3, tab5, waveshare-s3-lcd-13, waveshare-s3-lcd-128 Devices not changed and not tested because i don't own them are: MPU6886 - core2, stickc-plus2. BMI270 - cardputer-adv * Update device.properties * No USB here! CH343P --- Devices/m5stack-cores3/m5stack,cores3.dts | 4 ++- Devices/m5stack-papers3/m5stack,papers3.dts | 3 ++ .../m5stack-stackchan/m5stack,stackchan.dts | 4 ++- Devices/m5stack-sticks3/m5stack,sticks3.dts | 3 ++ Devices/m5stack-tab5/m5stack,tab5.dts | 3 ++ Devices/waveshare-s3-lcd-13/device.properties | 1 - .../waveshare,s3-lcd-13.dts | 12 ++----- .../device.properties | 1 - .../waveshare,s3-touch-lcd-128.dts | 12 ++----- .../bmi270-module/bindings/bosch,bmi270.yaml | 32 +++++-------------- .../bmi270-module/include/drivers/bmi270.h | 12 +++---- Drivers/bmi270-module/source/bmi270.cpp | 32 ++++++++++++------- .../bindings/invensense,mpu6886.yaml | 32 +++++-------------- .../mpu6886-module/include/drivers/mpu6886.h | 12 +++---- Drivers/mpu6886-module/source/mpu6886.cpp | 32 ++++++++++++------- Modules/lvgl-module/source/symbols.c | 4 +++ Tactility/Source/app/display/Display.cpp | 18 +++++------ .../service/autorotate/OrientationMath.cpp | 9 ++++-- .../Tests/Source/OrientationMathTest.cpp | 18 ++++++----- 19 files changed, 118 insertions(+), 126 deletions(-) diff --git a/Devices/m5stack-cores3/m5stack,cores3.dts b/Devices/m5stack-cores3/m5stack,cores3.dts index 221503afd..150ac1f2a 100644 --- a/Devices/m5stack-cores3/m5stack,cores3.dts +++ b/Devices/m5stack-cores3/m5stack,cores3.dts @@ -65,7 +65,9 @@ bmi270 { compatible = "bosch,bmi270"; reg = <0x69>; - invert-x; + mount-matrix = <1 0 0 + 0 1 0 + 0 0 1>; }; bm8563 { diff --git a/Devices/m5stack-papers3/m5stack,papers3.dts b/Devices/m5stack-papers3/m5stack,papers3.dts index eaccfaa9d..d67823284 100644 --- a/Devices/m5stack-papers3/m5stack,papers3.dts +++ b/Devices/m5stack-papers3/m5stack,papers3.dts @@ -44,6 +44,9 @@ bmi270 { compatible = "bosch,bmi270"; reg = <0x68>; + mount-matrix = <0 1 0 + 1 0 0 + 0 0 -1>; }; bm8563 { diff --git a/Devices/m5stack-stackchan/m5stack,stackchan.dts b/Devices/m5stack-stackchan/m5stack,stackchan.dts index 8a823bcd0..7920f9591 100644 --- a/Devices/m5stack-stackchan/m5stack,stackchan.dts +++ b/Devices/m5stack-stackchan/m5stack,stackchan.dts @@ -70,7 +70,9 @@ bmi270 { compatible = "bosch,bmi270"; reg = <0x69>; - invert-x; + mount-matrix = <1 0 0 + 0 1 0 + 0 0 1>; }; bm8563 { diff --git a/Devices/m5stack-sticks3/m5stack,sticks3.dts b/Devices/m5stack-sticks3/m5stack,sticks3.dts index 1c0b9ff97..d65457557 100644 --- a/Devices/m5stack-sticks3/m5stack,sticks3.dts +++ b/Devices/m5stack-sticks3/m5stack,sticks3.dts @@ -63,6 +63,9 @@ bmi270 { compatible = "bosch,bmi270"; reg = <0x68>; + mount-matrix = <0 1 0 + -1 0 0 + 0 0 1>; }; es8311 { diff --git a/Devices/m5stack-tab5/m5stack,tab5.dts b/Devices/m5stack-tab5/m5stack,tab5.dts index f6c38a07e..ccbc8a381 100644 --- a/Devices/m5stack-tab5/m5stack,tab5.dts +++ b/Devices/m5stack-tab5/m5stack,tab5.dts @@ -83,6 +83,9 @@ bmi270 { compatible = "bosch,bmi270"; reg = <0x68>; + mount-matrix = <-1 0 0 + 0 1 0 + 0 0 -1>; }; es8388 { diff --git a/Devices/waveshare-s3-lcd-13/device.properties b/Devices/waveshare-s3-lcd-13/device.properties index 67abca070..c8d45781e 100644 --- a/Devices/waveshare-s3-lcd-13/device.properties +++ b/Devices/waveshare-s3-lcd-13/device.properties @@ -8,7 +8,6 @@ hardware.flashSize=16MB hardware.spiRam=true hardware.spiRamMode=OCT hardware.spiRamSpeed=120M -hardware.tinyUsbMsc=true hardware.esptoolFlashFreq=120M hardware.bluetooth=true diff --git a/Devices/waveshare-s3-lcd-13/waveshare,s3-lcd-13.dts b/Devices/waveshare-s3-lcd-13/waveshare,s3-lcd-13.dts index 0393c160d..b7fb2afe1 100644 --- a/Devices/waveshare-s3-lcd-13/waveshare,s3-lcd-13.dts +++ b/Devices/waveshare-s3-lcd-13/waveshare,s3-lcd-13.dts @@ -9,7 +9,6 @@ #include #include #include -#include #include #include @@ -41,6 +40,9 @@ qmi8658 { compatible = "qst,qmi8658"; reg = <0x6B>; + mount-matrix = <-1 0 0 + 0 -1 0 + 0 0 -1>; }; }; @@ -89,12 +91,4 @@ frequency-khz = <20000>; }; }; - - usbdevice0 { - compatible = "espressif,esp32-usbdevice"; - - usbdevicemsc0 { - compatible = "espressif,esp32-usbdevice-msc"; - }; - }; }; diff --git a/Devices/waveshare-s3-touch-lcd-128/device.properties b/Devices/waveshare-s3-touch-lcd-128/device.properties index 81959e7cc..c588fd16f 100644 --- a/Devices/waveshare-s3-touch-lcd-128/device.properties +++ b/Devices/waveshare-s3-touch-lcd-128/device.properties @@ -9,7 +9,6 @@ hardware.spiRam=true hardware.spiRamMode=QUAD hardware.spiRamSpeed=120M hardware.spiRamXipDisabled=true -hardware.tinyUsbMsc=true hardware.esptoolFlashFreq=120M hardware.bluetooth=true diff --git a/Devices/waveshare-s3-touch-lcd-128/waveshare,s3-touch-lcd-128.dts b/Devices/waveshare-s3-touch-lcd-128/waveshare,s3-touch-lcd-128.dts index 1d9faa7b2..09a7f5eec 100644 --- a/Devices/waveshare-s3-touch-lcd-128/waveshare,s3-touch-lcd-128.dts +++ b/Devices/waveshare-s3-touch-lcd-128/waveshare,s3-touch-lcd-128.dts @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -42,6 +41,9 @@ qmi8658 { compatible = "qst,qmi8658"; reg = <0x6B>; + mount-matrix = <0 1 0 + 1 0 0 + 0 0 1>; }; touch0 { @@ -93,12 +95,4 @@ backlight = <&display_backlight>; }; }; - - usbdevice0 { - compatible = "espressif,esp32-usbdevice"; - - usbdevicemsc0 { - compatible = "espressif,esp32-usbdevice-msc"; - }; - }; }; diff --git a/Drivers/bmi270-module/bindings/bosch,bmi270.yaml b/Drivers/bmi270-module/bindings/bosch,bmi270.yaml index f35b7d19d..af51f13ea 100644 --- a/Drivers/bmi270-module/bindings/bosch,bmi270.yaml +++ b/Drivers/bmi270-module/bindings/bosch,bmi270.yaml @@ -5,27 +5,11 @@ include: [ "i2c-device.yaml" ] compatible: "bosch,bmi270" properties: - swap-xy: - type: boolean - default: false - description: Swap the X and Y axes - swap-xz: - type: boolean - default: false - description: Swap the X and Z axes - swap-yz: - type: boolean - default: false - description: Swap the Y and Z axes - invert-x: - type: boolean - default: false - description: Invert the X axis - invert-y: - type: boolean - default: false - description: Invert the Y axis - invert-z: - type: boolean - default: false - description: Invert the Z axis + mount-matrix: + type: values + default: [1, 0, 0, 0, 1, 0, 0, 0, 1] + description: > + Row-major 3x3 rotation matrix that maps the sensor frame onto the device frame, following the + Linux IIO "mount-matrix" convention (x' = m[0]*x + m[1]*y + m[2]*z, and so on). It is applied + to the accelerometer and gyroscope readings so consumers always see the device frame, + regardless of how the IMU is physically oriented on the PCB. Defaults to the identity matrix. diff --git a/Drivers/bmi270-module/include/drivers/bmi270.h b/Drivers/bmi270-module/include/drivers/bmi270.h index 3f91b3bc3..147aa11ac 100644 --- a/Drivers/bmi270-module/include/drivers/bmi270.h +++ b/Drivers/bmi270-module/include/drivers/bmi270.h @@ -14,12 +14,12 @@ extern "C" { struct Bmi270Config { /** Address on bus */ uint8_t address; - bool swap_xy; - bool swap_xz; - bool swap_yz; - bool invert_x; - bool invert_y; - bool invert_z; + /** + * Row-major 3x3 rotation matrix mapping the sensor frame onto the device frame (Linux IIO + * "mount-matrix" convention: `out[i] = m[i * 3] * x + m[i * 3 + 1] * y + m[i * 3 + 2] * z`). + * Identity means the sensor frame already matches the device frame. + */ + int32_t mount_matrix[9]; }; /** diff --git a/Drivers/bmi270-module/source/bmi270.cpp b/Drivers/bmi270-module/source/bmi270.cpp index 40c3b94a5..26ae59bad 100644 --- a/Drivers/bmi270-module/source/bmi270.cpp +++ b/Drivers/bmi270-module/source/bmi270.cpp @@ -5,8 +5,6 @@ #include #include -#include - #define TAG "BMI270" static constexpr uint8_t REG_CHIP_ID = 0x00; // read: expect 0x24 @@ -90,6 +88,18 @@ static bool configure(Device* i2c_controller, uint8_t address) { // region Driver lifecycle +// Applies the devicetree mount matrix (sensor frame -> device frame) to a 3-axis reading. +// Flat row-major [9]: x' = m0*x + m1*y + m2*z, y' = m3*x + m4*y + m5*z, z' = m6*x + m7*y + m8*z. +static void applyMountMatrix(const Bmi270Config& config, float& x, float& y, float& z) { + const int32_t* m = config.mount_matrix; + const float rx = m[0] * x + m[1] * y + m[2] * z; + const float ry = m[3] * x + m[4] * y + m[5] * z; + const float rz = m[6] * x + m[7] * y + m[8] * z; + x = rx; + y = ry; + z = rz; +} + static error_t start(Device* device) { auto* i2c_controller = device_get_parent(device); if (device_get_type(i2c_controller) != &I2C_CONTROLLER_TYPE) { @@ -171,13 +181,7 @@ error_t bmi270_read_accel(Device* device, ImuAccelData* data) { toI16(buffer[2], buffer[3]) * ACCEL_SCALE, toI16(buffer[4], buffer[5]) * ACCEL_SCALE, }; - const auto& config = *GET_CONFIG(device); - if (config.swap_xy) std::swap(axes[0], axes[1]); - if (config.swap_xz) std::swap(axes[0], axes[2]); - if (config.swap_yz) std::swap(axes[1], axes[2]); - if (config.invert_x) axes[0] = -axes[0]; - if (config.invert_y) axes[1] = -axes[1]; - if (config.invert_z) axes[2] = -axes[2]; + applyMountMatrix(*GET_CONFIG(device), axes[0], axes[1], axes[2]); data->ax = axes[0]; data->ay = axes[1]; data->az = axes[2]; @@ -197,9 +201,13 @@ error_t bmi270_read_gyro(Device* device, ImuGyroData* data) { return static_cast(static_cast(hi) << 8 | lo); }; - data->gx = toI16(buffer[0], buffer[1]) * GYRO_SCALE; - data->gy = toI16(buffer[2], buffer[3]) * GYRO_SCALE; - data->gz = toI16(buffer[4], buffer[5]) * GYRO_SCALE; + float gx = toI16(buffer[0], buffer[1]) * GYRO_SCALE; + float gy = toI16(buffer[2], buffer[3]) * GYRO_SCALE; + float gz = toI16(buffer[4], buffer[5]) * GYRO_SCALE; + applyMountMatrix(*GET_CONFIG(device), gx, gy, gz); + data->gx = gx; + data->gy = gy; + data->gz = gz; return ERROR_NONE; } diff --git a/Drivers/mpu6886-module/bindings/invensense,mpu6886.yaml b/Drivers/mpu6886-module/bindings/invensense,mpu6886.yaml index 86c0ae88b..97fa77e4c 100644 --- a/Drivers/mpu6886-module/bindings/invensense,mpu6886.yaml +++ b/Drivers/mpu6886-module/bindings/invensense,mpu6886.yaml @@ -5,27 +5,11 @@ include: [ "i2c-device.yaml" ] compatible: "invensense,mpu6886" properties: - swap-xy: - type: boolean - default: false - description: Swap the X and Y axes - swap-xz: - type: boolean - default: false - description: Swap the X and Z axes - swap-yz: - type: boolean - default: false - description: Swap the Y and Z axes - invert-x: - type: boolean - default: false - description: Invert the X axis - invert-y: - type: boolean - default: false - description: Invert the Y axis - invert-z: - type: boolean - default: false - description: Invert the Z axis + mount-matrix: + type: values + default: [1, 0, 0, 0, 1, 0, 0, 0, 1] + description: > + Row-major 3x3 rotation matrix that maps the sensor frame onto the device frame, following the + Linux IIO "mount-matrix" convention (x' = m[0]*x + m[1]*y + m[2]*z, and so on). It is applied + to the accelerometer and gyroscope readings so consumers always see the device frame, + regardless of how the IMU is physically oriented on the PCB. Defaults to the identity matrix. diff --git a/Drivers/mpu6886-module/include/drivers/mpu6886.h b/Drivers/mpu6886-module/include/drivers/mpu6886.h index 7438a9179..65dad1a8d 100644 --- a/Drivers/mpu6886-module/include/drivers/mpu6886.h +++ b/Drivers/mpu6886-module/include/drivers/mpu6886.h @@ -14,12 +14,12 @@ extern "C" { struct Mpu6886Config { /** Address on bus */ uint8_t address; - bool swap_xy; - bool swap_xz; - bool swap_yz; - bool invert_x; - bool invert_y; - bool invert_z; + /** + * Row-major 3x3 rotation matrix mapping the sensor frame onto the device frame (Linux IIO + * "mount-matrix" convention: `out[i] = m[i * 3] * x + m[i * 3 + 1] * y + m[i * 3 + 2] * z`). + * Identity means the sensor frame already matches the device frame. + */ + int32_t mount_matrix[9]; }; /** diff --git a/Drivers/mpu6886-module/source/mpu6886.cpp b/Drivers/mpu6886-module/source/mpu6886.cpp index 35806aa51..b8b329694 100644 --- a/Drivers/mpu6886-module/source/mpu6886.cpp +++ b/Drivers/mpu6886-module/source/mpu6886.cpp @@ -4,8 +4,6 @@ #include #include -#include - #define TAG "MPU6886" // Register map @@ -48,6 +46,18 @@ static constexpr TickType_t I2C_TIMEOUT_TICKS = pdMS_TO_TICKS(10); // region Driver lifecycle +// Applies the devicetree mount matrix (sensor frame -> device frame) to a 3-axis reading. +// Flat row-major [9]: x' = m0*x + m1*y + m2*z, y' = m3*x + m4*y + m5*z, z' = m6*x + m7*y + m8*z. +static void applyMountMatrix(const Mpu6886Config& config, float& x, float& y, float& z) { + const int32_t* m = config.mount_matrix; + const float rx = m[0] * x + m[1] * y + m[2] * z; + const float ry = m[3] * x + m[4] * y + m[5] * z; + const float rz = m[6] * x + m[7] * y + m[8] * z; + x = rx; + y = ry; + z = rz; +} + static error_t start(Device* device) { auto* i2c_controller = device_get_parent(device); if (device_get_type(i2c_controller) != &I2C_CONTROLLER_TYPE) { @@ -145,13 +155,7 @@ error_t mpu6886_read_accel(Device* device, ImuAccelData* data) { toI16(buf[2], buf[3]) * ACCEL_SCALE, toI16(buf[4], buf[5]) * ACCEL_SCALE, }; - const auto& config = *GET_CONFIG(device); - if (config.swap_xy) std::swap(axes[0], axes[1]); - if (config.swap_xz) std::swap(axes[0], axes[2]); - if (config.swap_yz) std::swap(axes[1], axes[2]); - if (config.invert_x) axes[0] = -axes[0]; - if (config.invert_y) axes[1] = -axes[1]; - if (config.invert_z) axes[2] = -axes[2]; + applyMountMatrix(*GET_CONFIG(device), axes[0], axes[1], axes[2]); data->ax = axes[0]; data->ay = axes[1]; data->az = axes[2]; @@ -171,9 +175,13 @@ error_t mpu6886_read_gyro(Device* device, ImuGyroData* data) { error_t error = i2c_controller_read_register(i2c_controller, address, REG_GYRO_XOUT_H, buf, sizeof(buf), I2C_TIMEOUT_TICKS); if (error != ERROR_NONE) return error; - data->gx = toI16(buf[0], buf[1]) * GYRO_SCALE; - data->gy = toI16(buf[2], buf[3]) * GYRO_SCALE; - data->gz = toI16(buf[4], buf[5]) * GYRO_SCALE; + float gx = toI16(buf[0], buf[1]) * GYRO_SCALE; + float gy = toI16(buf[2], buf[3]) * GYRO_SCALE; + float gz = toI16(buf[4], buf[5]) * GYRO_SCALE; + applyMountMatrix(*GET_CONFIG(device), gx, gy, gz); + data->gx = gx; + data->gy = gy; + data->gz = gz; return ERROR_NONE; } diff --git a/Modules/lvgl-module/source/symbols.c b/Modules/lvgl-module/source/symbols.c index 11781a03f..ed5e565ac 100644 --- a/Modules/lvgl-module/source/symbols.c +++ b/Modules/lvgl-module/source/symbols.c @@ -343,6 +343,10 @@ const struct ModuleSymbol lvgl_module_symbols[] = { DEFINE_MODULE_SYMBOL(lv_obj_set_style_height), DEFINE_MODULE_SYMBOL(lv_obj_set_style_clip_corner), DEFINE_MODULE_SYMBOL(lv_obj_set_style_shadow_color), + DEFINE_MODULE_SYMBOL(lv_obj_set_parent), + DEFINE_MODULE_SYMBOL(lv_obj_set_style_bg_image_tiled), + DEFINE_MODULE_SYMBOL(lv_obj_set_style_image_recolor), + DEFINE_MODULE_SYMBOL(lv_obj_set_style_image_opa), // lv_font DEFINE_MODULE_SYMBOL(lv_font_get_bitmap_fmt_txt), DEFINE_MODULE_SYMBOL(lv_font_get_default), diff --git a/Tactility/Source/app/display/Display.cpp b/Tactility/Source/app/display/Display.cpp index 22b396205..352ff7a10 100644 --- a/Tactility/Source/app/display/Display.cpp +++ b/Tactility/Source/app/display/Display.cpp @@ -84,7 +84,7 @@ struct Context { lv_obj_t* timeoutValueLabel = nullptr; lv_obj_t* screensaverValueLabel = nullptr; // Shown when auto-rotate is off or unavailable: the orientation label and chips - lv_obj_t* orientationGroup = nullptr; + lv_obj_t* orientationCard = nullptr; lv_obj_t* orientationChips = nullptr; lv_obj_t* autoRotateSwitch = nullptr; }; @@ -151,10 +151,10 @@ void setCheckedChip(lv_obj_t* chips, settings::display::Orientation orientation) }); } -/** Shows or hides the manual orientation chips, depending on whether auto-rotate is on */ +/** Shows or hides the manual orientation card, depending on whether auto-rotate is on */ void updateOrientationVisibility(Context* ctx) { const bool auto_rotate = ctx->autoRotateSwitch != nullptr && ctx->displaySettings.autoRotateEnabled; - lv_obj_set_hidden(ctx->orientationGroup, auto_rotate); + lv_obj_set_hidden(ctx->orientationCard, auto_rotate); } void onOrientationChipPressed(lv_event_t* event) { @@ -396,12 +396,12 @@ void createWidgets(lv_obj_t* parent, void* userData) { } // The card shows the manual orientation; auto-rotate hides it while it drives the rotation - auto* orientation_card = lvgl_card_create(main_wrapper); - lv_obj_set_size(orientation_card, LV_PCT(100), LV_SIZE_CONTENT); - lv_obj_set_flex_flow(orientation_card, LV_FLEX_FLOW_COLUMN); + ctx->orientationCard = lvgl_card_create(main_wrapper); + lv_obj_set_size(ctx->orientationCard, LV_PCT(100), LV_SIZE_CONTENT); + lv_obj_set_flex_flow(ctx->orientationCard, LV_FLEX_FLOW_COLUMN); - ctx->orientationGroup = createCardGroup(orientation_card, "Orientation"); - ctx->orientationChips = createOrientationChips(ctx->orientationGroup, ctx->displaySettings.orientation, onOrientationChipPressed, ctx); + auto* orientation_group = createCardGroup(ctx->orientationCard, "Orientation"); + ctx->orientationChips = createOrientationChips(orientation_group, ctx->displaySettings.orientation, onOrientationChipPressed, ctx); updateOrientationVisibility(ctx); @@ -447,7 +447,7 @@ void destroyWidgets(void* userData) { ctx->timeoutCard = nullptr; ctx->timeoutValueLabel = nullptr; ctx->screensaverValueLabel = nullptr; - ctx->orientationGroup = nullptr; + ctx->orientationCard = nullptr; ctx->orientationChips = nullptr; ctx->autoRotateSwitch = nullptr; } diff --git a/Tactility/Source/service/autorotate/OrientationMath.cpp b/Tactility/Source/service/autorotate/OrientationMath.cpp index 4057f2a98..ade0a330c 100644 --- a/Tactility/Source/service/autorotate/OrientationMath.cpp +++ b/Tactility/Source/service/autorotate/OrientationMath.cpp @@ -7,7 +7,10 @@ namespace tt::service::autorotate { constexpr float FLAT_THRESHOLD = 0.45F; // gravity in the screen plane below this = lying flat constexpr int DEBOUNCE = 5; // consecutive readings before switching -// gx toward +Y -> rotation 0; each +90 degrees of gx/gy angle is one more quarter-turn. +// ax/ay is the accelerometer support-force vector (points up, opposite gravity). +// The UI top should point along (ax,ay): +Y -> rotation 0. +// LVGL ROT90 is 90 deg counter-clockwise (UI top -> device left, -X), ROT270 is +// 90 deg clockwise (UI top -> device right, +X), so +X maps to 270, not 90. static lv_display_rotation_t gravityToRotation(float gx, float gy, lv_display_rotation_t fallback) { float plane = sqrtf((gx * gx) + (gy * gy)); if (plane < FLAT_THRESHOLD) { @@ -22,9 +25,9 @@ static lv_display_rotation_t gravityToRotation(float gx, float gy, lv_display_ro int idx = (int)((ang + 45.0F) / 90.0F) % 4; static constexpr lv_display_rotation_t map[4] = { LV_DISPLAY_ROTATION_0, - LV_DISPLAY_ROTATION_90, - LV_DISPLAY_ROTATION_180, LV_DISPLAY_ROTATION_270, + LV_DISPLAY_ROTATION_180, + LV_DISPLAY_ROTATION_90, }; return map[idx]; } diff --git a/Tactility/Tests/Source/OrientationMathTest.cpp b/Tactility/Tests/Source/OrientationMathTest.cpp index 0f88664b9..93d872731 100644 --- a/Tactility/Tests/Source/OrientationMathTest.cpp +++ b/Tactility/Tests/Source/OrientationMathTest.cpp @@ -9,7 +9,7 @@ TEST_CASE("a flat reading below the threshold keeps the current rotation") { CHECK_EQ(math.update(0.1F, 0.1F), LV_DISPLAY_ROTATION_0); } -TEST_CASE("gravity toward +X classifies as rotation 90 when no debounce is needed yet") { +TEST_CASE("accel toward +X classifies as rotation 270 when no debounce is needed yet") { OrientationMath math; math.reset(LV_DISPLAY_ROTATION_0); // A single reading only sets the debounce target - the previous rotation is still returned. @@ -27,32 +27,34 @@ TEST_CASE("a new rotation is only applied after enough consecutive stable readin CHECK_EQ(last, LV_DISPLAY_ROTATION_0); // not yet debounced } last = math.update(1.0F, 0.0F); - CHECK_EQ(last, LV_DISPLAY_ROTATION_90); // 6th call (5th consecutive stable reading) switches + CHECK_EQ(last, LV_DISPLAY_ROTATION_270); // 6th call (5th consecutive stable reading) switches } -TEST_CASE("all four gravity directions map to the expected rotation") { +TEST_CASE("all four accel directions map to the expected rotation") { OrientationMath math; math.reset(LV_DISPLAY_ROTATION_0); - auto settle = [&](float gx, float gy) { + auto settle = [&](float ax, float ay) { lv_display_rotation_t result = LV_DISPLAY_ROTATION_0; for (int i = 0; i < 6; i++) { - result = math.update(gx, gy); + result = math.update(ax, ay); } return result; }; + // Accel points up (opposite gravity); UI top follows it. + // LVGL ROT90 is CCW (top -> -X), ROT270 is CW (top -> +X). CHECK_EQ(settle(0.0F, 1.0F), LV_DISPLAY_ROTATION_0); - CHECK_EQ(settle(1.0F, 0.0F), LV_DISPLAY_ROTATION_90); + CHECK_EQ(settle(1.0F, 0.0F), LV_DISPLAY_ROTATION_270); CHECK_EQ(settle(0.0F, -1.0F), LV_DISPLAY_ROTATION_180); - CHECK_EQ(settle(-1.0F, 0.0F), LV_DISPLAY_ROTATION_270); + CHECK_EQ(settle(-1.0F, 0.0F), LV_DISPLAY_ROTATION_90); } TEST_CASE("an unstable target resets the debounce counter") { OrientationMath math; math.reset(LV_DISPLAY_ROTATION_0); - // Get close to switching to rotation 90... + // Get close to switching to rotation 270... for (int i = 0; i < 4; i++) { math.update(1.0F, 0.0F); }