owb_core/utils/controllers/
mod.rs

1//! Controllers and command definitions for Omni-Wheel Bot hardware.
2//!
3//! This module contains submodules and types for managing I2C devices (motors, IMU)
4//! and addressable LEDs, and defines the `SystemCommand` enum for incoming commands.
5//!
6//! Submodules:
7//! - `i2c`: Motor PWM and IMU control over I2C bus
8//! - `leds`: Addressable LED strip control
9
10pub mod i2c;
11pub mod leds;
12
13use core::cell::RefCell;
14use serde::{Deserialize, Serialize};
15
16pub use i2c::I2C_CHANNEL;
17pub use leds::LED_CHANNEL;
18
19#[derive(Debug, Serialize, Deserialize)]
20#[serde(tag = "ct", rename_all = "snake_case")] // ct = command type
21pub enum SystemCommand {
22    I(i2c::I2CCommand),
23    L(leds::LEDCommand),
24}
25
26pub struct SystemController<I2C: 'static> {
27    pub sensors: Option<i2c::I2CDevices<'static, I2C>>,
28    pub robot_dimensions: (f32, f32), // (wheel_radius, robot_radius)
29}
30impl<I2C> SystemController<I2C>
31where
32    I2C: embedded_hal::i2c::I2c + 'static,
33{
34    /// Create a new system controller for motor and IMU devices.
35    ///
36    /// `i2c_bus` is the shared I2C bus reference. `wheel_radius` and `robot_radius`
37    /// (in meters) default to 0.148 and 0.195 respectively if `None`.
38    pub fn new(
39        i2c_bus: &'static RefCell<I2C>,
40        wheel_radius: Option<f32>,
41        robot_radius: Option<f32>,
42    ) -> Self {
43        let wr = wheel_radius.unwrap_or(0.148f32);
44        let rr = robot_radius.unwrap_or(0.195f32);
45
46        let mut i2c_dev = i2c::I2CDevices::new(i2c_bus, wr, rr);
47
48        let sensors = match i2c_dev.init_devices() {
49            Ok(()) => {
50                let _ = i2c_dev.configure_pwm();
51                i2c_dev.init_imu_data();
52                Some(i2c_dev)
53            }
54            Err(e) => {
55                tracing::warn!("I2C init failed, scanning instead: {:?}", e);
56                i2c_dev.scan_bus();
57                None
58            }
59        };
60
61        SystemController {
62            sensors,
63            robot_dimensions: (wr, rr),
64        }
65    }
66
67    /// Start processing incoming `SystemCommand` messages indefinitely.
68    ///
69    /// This loop receives commands from the global I2C_CHANNEL and dispatches
70    /// motor/IMU operations. Never returns.
71    pub async fn i2c_ch(&mut self) -> ! {
72        loop {
73            let i2c_channel = i2c::I2C_CHANNEL.receiver().receive().await;
74            tracing::info!("Received I2C Command: {:?}", i2c_channel);
75            if let Some(devs) = self.sensors.as_mut() {
76                match devs.execute_command(i2c_channel) {
77                    Ok(Some((accel, gyro, temp))) => {
78                        tracing::info!(?accel, ?gyro, ?temp, "IMU Data Read");
79                    }
80                    Ok(None) => tracing::info!("I2C command executed successfully"),
81                    Err(_) => tracing::error!("I2C command failed"),
82                }
83            } else {
84                tracing::warn!(
85                    "I2C command received but devices not initialized: {:?}",
86                    i2c_channel
87                );
88            }
89        }
90    }
91}