1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#![no_std]
#![allow(async_fn_in_trait)]

use core::iter::once;

pub use embedded_hal::i2c::{AddressMode, I2c};

/// I2C operation.
///
/// Several operations can be combined as part of a transaction.
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
pub enum Operation<'a, B>
where
    B: IntoIterator<Item = u8>,
{
    /// Read data into the provided buffer.
    Read(&'a mut [u8]),
    /// Write data from the provided iterator.
    WriteIter(B),
}

pub trait I2cIter<A: AddressMode>: I2c<A> {
    /// Execute the provided operations on the I2C bus.
    ///
    /// Same as `I2c::transaction` but with an interator instead of a slice of operations.
    fn transaction_iter<'a, O, B>(&mut self, address: A, operations: O) -> Result<(), Self::Error>
    where
        O: IntoIterator<Item = Operation<'a, B>>,
        B: IntoIterator<Item = u8>;

    /// Writes bytes to slave with address `address`
    ///
    /// Same as `I2c::write` but with an interator instead of a slice of bytes.
    fn write_iter<B>(&mut self, address: A, bytes: B) -> Result<(), Self::Error>
    where
        B: IntoIterator<Item = u8>,
    {
        self.transaction_iter(address, once(Operation::WriteIter(bytes)))
    }

    /// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a
    /// single transaction*
    ///
    /// Same as `I2c::write_read` but with an interator instead of a slice of bytes.
    fn write_iter_read<B>(
        &mut self,
        address: A,
        bytes: B,
        buffer: &mut [u8],
    ) -> Result<(), Self::Error>
    where
        B: IntoIterator<Item = u8>,
    {
        self.transaction_iter(
            address,
            [Operation::WriteIter(bytes), Operation::Read(buffer)],
        )
    }
}

/// Exposes trait for async.await use cases. Requires the `async` feature.
#[cfg(feature = "async")]
pub mod non_blocking {
    use core::iter::once;

    pub use embedded_hal_async::i2c::{AddressMode, I2c};

    use crate::Operation;

    pub trait I2cIter<A: AddressMode>: I2c<A> {
        /// Execute the provided operations on the I2C bus.
        ///
        /// Same as `I2c::transaction` but with an interator instead of a slice.
        async fn transaction_iter<'a, O, B>(
            &mut self,
            address: A,
            operations: O,
        ) -> Result<(), Self::Error>
        where
            O: IntoIterator<Item = Operation<'a, B>>,
            B: IntoIterator<Item = u8>;

        /// Writes bytes to slave with address `address`
        ///
        /// Same as `I2c::write` but with an interator instead of a slice of bytes.
        async fn write_iter<B>(&mut self, address: A, bytes: B) -> Result<(), Self::Error>
        where
            B: IntoIterator<Item = u8>,
        {
            self.transaction_iter(address, once(Operation::WriteIter(bytes)))
                .await
        }

        /// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a
        /// single transaction*
        ///
        /// Same as `I2c::write_read` but with an interator instead of a slice of bytes.
        async fn write_iter_read<B>(
            &mut self,
            address: A,
            bytes: B,
            buffer: &mut [u8],
        ) -> Result<(), Self::Error>
        where
            B: IntoIterator<Item = u8>,
        {
            self.transaction_iter(
                address,
                [Operation::WriteIter(bytes), Operation::Read(buffer)],
            )
            .await
        }
    }
}