improved wheel handling + tests

This commit is contained in:
Daniel Bulant 2026-05-11 19:50:36 +02:00
parent 6b38c36a28
commit ea1db833f3
No known key found for this signature in database
9 changed files with 2318 additions and 13 deletions

View file

@ -1,14 +1,14 @@
[target.xtensa-esp32-none-elf]
runner = "espflash flash --monitor"
[build]
target = "xtensa-esp32-none-elf"
rustflags = [
"-C", "link-arg=-Wl,-Tlinkall.x",
"-C", "link-arg=-nostartfiles",
]
[build]
target = "xtensa-esp32-none-elf"
[unstable]
build-std = ["core", "alloc", "compiler_builtins"]

View file

@ -1,4 +1,9 @@
fn main() {
let target = std::env::var("TARGET").unwrap_or_default();
if !target.starts_with("xtensa-") {
return;
}
let args: Vec<String> = std::env::args().collect();
if args.len() > 1 {
let kind = &args[1];

2187
esp32/host-tests/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -8,6 +8,7 @@ use esp_hal::time::Rate;
use esp_println::println;
use crate::WHEEL_VALUE;
use esp32::{apply_wheel_delta, wheel_delta};
pub static INPUT: Channel<CriticalSectionRawMutex, u8, 64> = Channel::new();
pub static ANGLE: Mutex<CriticalSectionRawMutex, u16> = Mutex::new(0);
@ -34,18 +35,24 @@ pub async fn rotation_read_task(config: RotationConfig) {
let old = *locked as i32;
*locked = angle;
drop(locked);
let left_diff = old - angle as i32;
let right_diff = angle as i32 - old;
let diff = if left_diff.abs() < right_diff.abs() { -left_diff } else { right_diff };
let diff = wheel_delta(old, angle as i32, crate::WHEEL_INVERTED);
let mut wheel = WHEEL_VALUE.lock().await;
if wheel.max != wheel.min {
let wheel_range = wheel.max - wheel.min;
wheel.value += diff * wheel_range / 4096;
if wheel.value < wheel.min {
wheel.value = wheel.min;
} else if wheel.value > wheel.max {
wheel.value = wheel.max;
}
let min = wheel.min;
let max = wheel.max;
let mut value = wheel.value;
let mut accumulated = wheel.accumulated;
let precision = crate::WHEEL_PRECISION;
apply_wheel_delta(
&mut value,
min,
max,
&mut accumulated,
diff,
precision,
);
wheel.value = value;
wheel.accumulated = accumulated;
}
}
}

5
esp32/src/lib.rs Normal file
View file

@ -0,0 +1,5 @@
#![no_std]
mod wheel;
pub use wheel::{apply_wheel_delta, wheel_delta};

View file

@ -29,6 +29,8 @@ const WIFI_NETWORK: &str = "flamme";
const WIFI_PASSWORD: &str = "12345678";
const TARGET_IP: &str = "84.238.32.253";
const TARGET_PORT: u16 = 7070;
const WHEEL_PRECISION: i32 = 32;
const WHEEL_INVERTED: bool = false;
enum QuestionType {
Choice,
@ -47,6 +49,7 @@ struct WheelData {
value: i32,
min: i32,
max: i32,
accumulated: i32,
}
static QUESTION: Mutex<CriticalSectionRawMutex, Option<QuestionData>> = Mutex::new(None);
@ -55,6 +58,7 @@ static WHEEL_VALUE: Mutex<CriticalSectionRawMutex, WheelData> = Mutex::new(Wheel
value: 0,
min: 0,
max: 0,
accumulated: 0,
});
#[panic_handler]
@ -79,6 +83,7 @@ pub async fn tcp_read_loop(mut read: TcpReader<'_>) {
value: 0,
min: 0,
max: 0,
accumulated: 0,
};
for line in str.lines() {
if line == "$$" {
@ -120,6 +125,7 @@ pub async fn tcp_read_loop(mut read: TcpReader<'_>) {
if let QuestionType::Numeric { min, max } = q_type {
let diff = max - min;
future_wheel.value = min + diff / 2;
future_wheel.accumulated = 0;
}
question_data = Some(QuestionData {

48
esp32/src/wheel.rs Normal file
View file

@ -0,0 +1,48 @@
pub const WHEEL_TICKS: i32 = 4096;
pub fn wheel_delta(old_angle: i32, current_angle: i32, inverted: bool) -> i32 {
let mut diff = current_angle - old_angle;
if diff.abs() > WHEEL_TICKS / 2 {
diff = if diff > 0 {
diff - WHEEL_TICKS
} else {
diff + WHEEL_TICKS
};
}
if inverted {
-diff
} else {
diff
}
}
pub fn apply_wheel_delta(
value: &mut i32,
min: i32,
max: i32,
accumulated: &mut i32,
diff: i32,
precision: i32,
) {
if max == min || precision <= 0 {
return;
}
*accumulated += diff;
let step_count = *accumulated / precision;
if step_count == 0 {
return;
}
*accumulated -= step_count * precision;
*value += step_count;
if *value < min {
*value = min;
} else if *value > max {
*value = max;
}
if *value == min || *value == max {
*accumulated = 0;
}
}

1
esp32/test.sh Normal file
View file

@ -0,0 +1 @@
rustc --test wheel-tests.rs -o wheel-tests && ./wheel-tests

46
esp32/wheel-tests.rs Normal file
View file

@ -0,0 +1,46 @@
#![allow(dead_code)]
mod wheel {
include!("src/wheel.rs");
}
use wheel::{apply_wheel_delta, wheel_delta};
#[test]
fn wraps_forward_across_zero() {
assert_eq!(wheel_delta(4090, 5, false), 11);
}
#[test]
fn wraps_backward_across_zero() {
assert_eq!(wheel_delta(5, 4090, false), -11);
}
#[test]
fn inverts_direction() {
assert_eq!(wheel_delta(10, 20, true), -10);
}
#[test]
fn accumulates_before_applying_selection() {
let mut value = 5;
let mut accumulated = 0;
apply_wheel_delta(&mut value, 0, 10, &mut accumulated, 10, 32);
assert_eq!(value, 5);
assert_eq!(accumulated, 10);
apply_wheel_delta(&mut value, 0, 10, &mut accumulated, 22, 32);
assert_eq!(value, 6);
assert_eq!(accumulated, 0);
}
#[test]
fn clamps_and_resets_at_bounds() {
let mut value = 10;
let mut accumulated = 0;
apply_wheel_delta(&mut value, 0, 10, &mut accumulated, 64, 32);
assert_eq!(value, 10);
assert_eq!(accumulated, 0);
}