/ firmware / tests / i2c.rs
i2c.rs
  1  //! `describe("I2C Bus Scanner")`
  2  //!
  3  //! Scans both I2C buses (wired per `config::i2c`)
  4  //! for devices in the 7-bit 0x03..=0x77 range and prints each ACKing
  5  //! address with a best-guess label via defmt. Flash this test when you
  6  //! want to find out which bus a physical sensor is wired to.
  7  //!
  8  //! Canonical pin assignments come from `firmware/src/config.rs`:
  9  //!   i2c.0 → sda=GPIO8  scl=GPIO9
 10  //!   i2c.1 → sda=GPIO17 scl=GPIO18
 11  //!
 12  //! `#[ignore]` by default (requires hardware); opt in with
 13  //! `--include-ignored`.
 14  
 15  #![no_std]
 16  #![no_main]
 17  
 18  #[path = "common/mod.rs"]
 19  mod common;
 20  use common::{Device, tasks};
 21  use defmt::info;
 22  
 23  esp_bootloader_esp_idf::esp_app_desc!();
 24  
 25  #[cfg(test)]
 26  #[embedded_test::setup]
 27  fn setup() {
 28      rtt_target::rtt_init_defmt!();
 29  }
 30  
 31  #[cfg(test)]
 32  #[embedded_test::tests(default_timeout = 15, executor = esp_rtos::embassy::Executor::new())]
 33  mod tests {
 34      use super::*;
 35  
 36      #[init]
 37      fn init() -> Device {
 38          info!("=== I2C Bus Scanner — describe block ===");
 39          common::setup::boot_device()
 40      }
 41  
 42      /// `it("user scans both buses and sees every wired I2C device")`
 43      #[test]
 44      async fn user_scans_both_buses_and_sees_every_wired_i2c_device(
 45          mut device: Device,
 46      ) -> Result<(), &'static str> {
 47          common::setup::delay_seconds(1).await;
 48  
 49          let (bus_0, bus_1) = tasks::i2c::scan_both_buses(&mut device)?;
 50          let total = bus_0.found_addresses.len() + bus_1.found_addresses.len();
 51  
 52          info!("BUS     ADDR    DEVICE");
 53          for addr in bus_0.found_addresses.iter() {
 54              info!(
 55                  "i2c.0   0x{=u8:02x}    {=str}",
 56                  *addr,
 57                  firmware::hardware::i2c::device_name_at(*addr)
 58              );
 59          }
 60          for addr in bus_1.found_addresses.iter() {
 61              info!(
 62                  "i2c.1   0x{=u8:02x}    {=str}",
 63                  *addr,
 64                  firmware::hardware::i2c::device_name_at(*addr)
 65              );
 66          }
 67  
 68          info!(
 69              "scan summary: bus0={=usize} bus1={=usize} total={=usize}",
 70              bus_0.found_addresses.len(),
 71              bus_1.found_addresses.len(),
 72              total,
 73          );
 74  
 75          defmt::assert!(total > 0, "no I2C devices found on either bus");
 76          Ok(())
 77      }
 78  
 79      /// `it("user probes for TCA9548A mux on bus 1")`
 80      #[test]
 81      async fn user_probes_mux_on_bus_1(
 82          mut device: Device,
 83      ) -> Result<(), &'static str> {
 84          if !tasks::i2c::is_mux_present(&mut device) {
 85              info!("mux not present on this board — skipping");
 86              return Ok(());
 87          }
 88          info!("TCA9548A mux detected at 0x70 on i2c.1");
 89          Ok(())
 90      }
 91  
 92      /// `it("user selects a mux channel and verifies the mask")`
 93      #[test]
 94      async fn user_selects_mux_channel_and_verifies(
 95          mut device: Device,
 96      ) -> Result<(), &'static str> {
 97          if !tasks::i2c::is_mux_present(&mut device) {
 98              info!("mux not present — skipping");
 99              return Ok(());
100          }
101  
102          let bus = device.i2c_bus_1.as_mut().ok_or("i2c bus 1 consumed")?;
103  
104          tasks::i2c::select_mux_channel(bus, 0)?;
105          let mask = tasks::i2c::access_channel_mask(bus)?;
106          defmt::assert_eq!(mask & (1 << 0), 1, "bit 0 should be set after select(0)");
107  
108          tasks::i2c::select_mux_channel(bus, 3)?;
109          let mask = tasks::i2c::access_channel_mask(bus)?;
110          defmt::assert_eq!(mask & (1 << 3), (1 << 3), "bit 3 should be set after select(3)");
111          defmt::assert_eq!(mask & (1 << 0), 0, "bit 0 should be clear after select(3)");
112  
113          tasks::i2c::disable_all_channels(bus)?;
114          info!("mux channel select and verify passed");
115          Ok(())
116      }
117  
118      /// `it("user disables all mux channels and sees mask 0x00")`
119      #[test]
120      async fn user_disables_all_mux_channels(
121          mut device: Device,
122      ) -> Result<(), &'static str> {
123          if !tasks::i2c::is_mux_present(&mut device) {
124              info!("mux not present — skipping");
125              return Ok(());
126          }
127  
128          let bus = device.i2c_bus_1.as_mut().ok_or("i2c bus 1 consumed")?;
129  
130          tasks::i2c::select_mux_channel(bus, 0)?;
131          tasks::i2c::select_mux_channel(bus, 3)?;
132          tasks::i2c::select_mux_channel(bus, 7)?;
133  
134          tasks::i2c::disable_all_channels(bus)?;
135          let mask = tasks::i2c::access_channel_mask(bus)?;
136          defmt::assert_eq!(mask, 0x00, "mask should be 0x00 after disable all");
137  
138          info!("disable all channels verified");
139          Ok(())
140      }
141  
142      /// `it("user enables then disables a single mux channel")`
143      #[test]
144      async fn user_enables_then_disables_single_channel(
145          mut device: Device,
146      ) -> Result<(), &'static str> {
147          if !tasks::i2c::is_mux_present(&mut device) {
148              info!("mux not present — skipping");
149              return Ok(());
150          }
151  
152          let bus = device.i2c_bus_1.as_mut().ok_or("i2c bus 1 consumed")?;
153  
154          tasks::i2c::disable_all_channels(bus)?;
155  
156          tasks::i2c::select_mux_channel(bus, 2)?;
157          let mask = tasks::i2c::access_channel_mask(bus)?;
158          defmt::assert_eq!(mask & (1 << 2), (1 << 2), "bit 2 should be set");
159  
160          tasks::i2c::disable_all_channels(bus)?;
161          let mask = tasks::i2c::access_channel_mask(bus)?;
162          defmt::assert_eq!(mask, 0x00, "mask should be 0x00 after disable");
163  
164          info!("enable/disable roundtrip verified");
165          Ok(())
166      }
167  }