i2c_multiplexer/
lib.rs

1#![no_std]
2
3#[cfg(feature = "bus")]
4pub mod bus;
5pub mod error;
6
7use embedded_hal::i2c::I2c;
8use error::{MultiplexerError, Result};
9
10pub mod prelude {
11    #[cfg(feature = "bus")]
12    pub use crate::bus::{BusPort, MultiplexerBus};
13    pub use crate::{error::MultiplexerError, Multiplexer, PortState};
14}
15
16#[derive(Copy, Clone, Debug)]
17pub enum PortState {
18    Enabled,
19    Disabled,
20}
21
22impl From<bool> for PortState {
23    fn from(value: bool) -> Self {
24        match value {
25            true => PortState::Enabled,
26            false => PortState::Disabled,
27        }
28    }
29}
30
31#[derive(Copy, Clone, Debug)]
32pub struct Multiplexer<I2C: 'static + Send + Sync> {
33    i2c: I2C,
34    address: u8,
35    state: [bool; 4],
36}
37
38pub(crate) fn address_from_pins(a0: bool, a1: bool, a2: bool) -> u8 {
39    let mut address = 0b1110_0000;
40    if a0 {
41        address |= 0b0000_0001;
42    }
43    if a1 {
44        address |= 0b0000_0010;
45    }
46    if a2 {
47        address |= 0b0000_0100;
48    }
49    address
50}
51
52impl<I2C> Multiplexer<I2C>
53where
54    I2C: I2c + Send + Sync,
55{
56    pub fn new(i2c: I2C) -> Self {
57        Self {
58            i2c,
59            address: 0x70,
60            state: [false; 4],
61        }
62    }
63
64    /// Sets the address according to the enabled hardware settings
65    pub fn with_address_pins(mut self, a0: bool, a1: bool, a2: bool) -> Self {
66        self.address = address_from_pins(a0, a1, a2);
67        self
68    }
69
70    /// Sets the address
71    pub fn with_address(mut self, address: u8) -> Self {
72        self.address = address;
73        self
74    }
75
76    fn port_code(states: [bool; 4]) -> u8 {
77        let mut code = 0;
78        if states[0] {
79            code |= 0b000_0001;
80        }
81        if states[1] {
82            code |= 0b000_0010;
83        }
84        if states[2] {
85            code |= 0b000_0100;
86        }
87        if states[3] {
88            code |= 0b000_1000;
89        }
90
91        code
92    }
93}
94
95impl<I2C> Multiplexer<I2C>
96where
97    I2C: I2c + Send + Sync,
98{
99    /// Disables all ports
100    pub fn with_ports_disabled(self) -> Result<Self, I2C::Error> {
101        self.with_ports([false; 4])
102    }
103
104    /// Disables all ports
105    pub fn set_ports_disabled(mut self) -> Result<(), I2C::Error> {
106        self.set_ports([false; 4])
107    }
108
109    /// Enables all ports
110    pub fn with_ports_enabled(self) -> Result<Self, I2C::Error> {
111        self.with_ports([true; 4])
112    }
113
114    /// Enables all ports
115    pub fn set_ports_enabled(mut self) -> Result<(), I2C::Error> {
116        self.set_ports([true; 4])
117    }
118
119    /// Enables / Disables the selected port
120    pub fn set_port(&mut self, port: u8, state: impl Into<bool>) -> Result<(), I2C::Error> {
121        if port >= 4 {
122            return Err(MultiplexerError::PortError);
123        }
124
125        self.state[port as usize] = state.into();
126
127        let code = Self::port_code(self.state);
128
129        self.i2c_write(&[code])
130    }
131
132    /// Sets the selected port
133    pub fn with_port(mut self, port: u8, state: impl Into<bool>) -> Result<Self, I2C::Error> {
134        self.set_port(port, state.into())?;
135        Ok(self)
136    }
137
138    /// Enables / Disables the selected ports
139    pub fn set_ports(&mut self, ports: [bool; 4]) -> Result<(), I2C::Error> {
140        let code = Self::port_code(ports);
141        self.i2c_write(&[code])
142    }
143
144    /// Enables / Disables the selected ports
145    pub fn with_ports(mut self, ports: [bool; 4]) -> Result<Self, I2C::Error> {
146        self.set_ports(ports)?;
147        Ok(self)
148    }
149
150    fn i2c_write(&mut self, bytes: &[u8]) -> Result<(), I2C::Error> {
151        self.i2c
152            .write(self.address, bytes)
153            .map_err(MultiplexerError::I2CError)
154    }
155}
156
157#[cfg(test)]
158mod test {
159    use crate::prelude::*;
160    use embedded_hal_mock::common::Generic;
161    use embedded_hal_mock::eh1::i2c::{Mock, Transaction};
162    use rstest::*;
163
164    impl Multiplexer<Generic<Transaction>> {
165        fn done(mut self) {
166            self.i2c.done();
167        }
168    }
169
170    #[rstest]
171    #[case([true;4], 0b0000_1111)]
172    #[case([false;4], 0b0000_0000)]
173    #[case([true, false, true, false], 0b0000_0101)]
174    fn setup_ports(#[case] ports: [bool; 4], #[case] result: u8) {
175        assert_eq!(Multiplexer::<Mock>::port_code(ports), result)
176    }
177
178    #[rstest]
179    #[case([true;3], 0b1110_0111)]
180    #[case([false;3], 0b1110_0000)]
181    #[case([true, false, false], 0b1110_0001)]
182    #[case([false, true, false], 0b1110_0010)]
183    #[case([true, false, true], 0b1110_0101)]
184    fn setup_address(#[case] addr: [bool; 3], #[case] result: u8) {
185        let i2c = Mock::new(&[]);
186        let multiplexer = Multiplexer::new(i2c).with_address_pins(addr[0], addr[1], addr[2]);
187        assert_eq!(multiplexer.address, result);
188        multiplexer.done();
189    }
190}