aem10900/
device.rs

1use crate::i2c_type;
2use crate::maybe_async_attr;
3use crate::registers::*;
4use crate::Aem10900Config;
5use crate::Error;
6use heapless::Vec;
7
8pub struct Aem10900<I2C> {
9    pub i2c: I2C,
10    pub dev_adr: u8,
11    pub conf: Aem10900Config,
12}
13
14impl<I2C> Aem10900<I2C>
15where
16    I2C: i2c_type::i2c::I2c,
17{
18    /// Creates a new instance of `Aem10900`
19    ///
20    /// Requires the I2C device and it's device address
21    pub fn new(i2c: I2C, dev_adr: u8) -> Self {
22        Self {
23            i2c,
24            dev_adr,
25            conf: Aem10900Config::default(),
26        }
27    }
28
29    /// Allow direct access to the I2C bus
30    pub fn bus(&mut self) -> &mut I2C {
31        &mut self.i2c
32    }
33
34    /// Function to write (consecutive) registers to the device
35    #[maybe_async_attr]
36    pub async fn write_regs(&mut self, start_adr: u8, write: &[u8]) -> Result<(), Error<I2C>> {
37        let mut write_buf: Vec<u8, 26> = Vec::new();
38        write_buf.push(start_adr).unwrap();
39        write_buf.extend_from_slice(write).unwrap();
40        self.i2c
41            .write(self.dev_adr, &write_buf)
42            .await
43            .map_err(Error::Communication)
44    }
45    /// Function to read (consecutive) registers from the device
46    #[maybe_async_attr]
47    pub async fn read_regs(&mut self, start_adr: u8, read: &mut [u8]) -> Result<(), Error<I2C>> {
48        self.i2c
49            .write_read(self.dev_adr, &[start_adr], read)
50            .await
51            .map_err(Error::Communication)
52    }
53
54    /// Initialize function
55    #[maybe_async_attr]
56    pub async fn init(&mut self) -> Result<(), Error<I2C>> {
57        self.check_connection().await?;
58        self.write_cfg_regs().await?;
59        self.verify_cfg_regs().await?;
60        Ok(())
61    }
62
63    /// Function polling the part number registers, to check whether the communication with the AEM10900 works
64    #[maybe_async_attr]
65    async fn check_connection(&mut self) -> Result<(), Error<I2C>> {
66        let mut part_number: [u8; 5] = [0; 5];
67        self.read_regs(Pn0::ADDRESS, &mut part_number).await?;
68
69        // TODO: Somehow part number is wrong
70        if part_number != [0x00, 0x77, 0x2D, 0x33, 0x8F] {
71            return Err(Error::Config("Part Number"));
72        }
73        // if Pn0::default() != Pn0::from_le_bytes(&[part_number[0]]) {
74        //     return Err(Error::Config("Pn0"));
75        // }
76        // if Pn1::default() != Pn1::from_le_bytes(&[part_number[0]]) {
77        //     return Err(Error::Config("Pn1"));
78        // }
79        // if Pn2::default() != Pn2::from_le_bytes(&[part_number[0]]) {
80        //     return Err(Error::Config("Pn2"));
81        // }
82        // if Pn3::default() != Pn3::from_le_bytes(&[part_number[0]]) {
83        //     return Err(Error::Config("Pn3"));
84        // }
85        // if Pn4::default() != Pn4::from_le_bytes(&[part_number[0]]) {
86        //     return Err(Error::Config("Pn4"));
87        // }
88
89        Ok(())
90    }
91
92    /// Function writing all config registers to the AEM10900
93    #[maybe_async_attr]
94    async fn write_cfg_regs(&mut self) -> Result<(), Error<I2C>> {
95        let mut byte_arr: [u8; 11] = [0; 11];
96        byte_arr[0] = self.conf.mpptcfg.to_le_bytes()[0];
97        byte_arr[1] = self.conf.vovdis.to_le_bytes()[0];
98        byte_arr[2] = self.conf.vovch.to_le_bytes()[0];
99        byte_arr[3] = self.conf.tempcold.to_le_bytes()[0];
100        byte_arr[4] = self.conf.temphot.to_le_bytes()[0];
101        byte_arr[5] = self.conf.pwr.to_le_bytes()[0];
102        byte_arr[6] = self.conf.sleep.to_le_bytes()[0];
103        byte_arr[7] = self.conf.stomon.to_le_bytes()[0];
104        byte_arr[8] = self.conf.apm.to_le_bytes()[0];
105        byte_arr[9] = self.conf.irqen.to_le_bytes()[0];
106        byte_arr[10] = self.conf.ctrl.to_le_bytes()[0];
107
108        self.write_regs(Mpptcfg::ADDRESS, &byte_arr).await
109    }
110
111    /// Function comparing all config registers in the AEM10900 to the config struct
112    #[maybe_async_attr]
113    async fn verify_cfg_regs(&mut self) -> Result<(), Error<I2C>> {
114        let mut byte_arr: [u8; 11] = [0; 11];
115
116        self.read_regs(Mpptcfg::ADDRESS, &mut byte_arr).await?;
117
118        if self.conf.mpptcfg
119            != Mpptcfg::try_from_le_bytes(&[byte_arr[0]])
120                .map_err(|_| Error::Value("Mpptcfg could not be extracted"))?
121        {
122            return Err(Error::Config("Mpptcfg"));
123        }
124        if self.conf.vovdis != Vovdis::from_le_bytes(&[byte_arr[1]]) {
125            return Err(Error::Config("Vovdis"));
126        }
127        if self.conf.vovch != Vovch::from_le_bytes(&[byte_arr[2]]) {
128            return Err(Error::Config("Vovch"));
129        }
130        if self.conf.tempcold != Tempcold::from_le_bytes(&[byte_arr[3]]) {
131            return Err(Error::Config("Tempcold"));
132        }
133        if self.conf.temphot != Temphot::from_le_bytes(&[byte_arr[4]]) {
134            return Err(Error::Config("Temphot"));
135        }
136        if self.conf.pwr != Pwr::from_le_bytes(&[byte_arr[5]]) {
137            return Err(Error::Config("Pwr"));
138        }
139        if self.conf.sleep != Sleep::from_le_bytes(&[byte_arr[6]]) {
140            return Err(Error::Config("Sleep"));
141        }
142        if self.conf.stomon
143            != Stomon::try_from_le_bytes(&[byte_arr[7]])
144                .map_err(|_| Error::Value("Stomon could not be extracted"))?
145        {
146            return Err(Error::Config("Stomon"));
147        }
148        if self.conf.apm
149            != Apm::try_from_le_bytes(&[byte_arr[8]])
150                .map_err(|_| Error::Value("Apm could not be extracted"))?
151        {
152            return Err(Error::Config("Apm"));
153        }
154        if self.conf.irqen != Irqen::from_le_bytes(&[byte_arr[9]]) {
155            return Err(Error::Config("Irqen"));
156        }
157        if self.conf.ctrl != Ctrl::from_le_bytes(&[byte_arr[10]]) {
158            return Err(Error::Config("Ctrl"));
159        }
160
161        Ok(())
162    }
163
164    /// Function to readout and clear the IRQ Flag of the AEM10900
165    #[maybe_async_attr]
166    pub async fn read_irq_flag(&mut self) -> Result<Irqflg, Error<I2C>> {
167        let mut byte_arr: [u8; 1] = [0];
168
169        self.read_regs(Irqflg::ADDRESS, &mut byte_arr).await?;
170        Ok(Irqflg::from_le_bytes(&byte_arr))
171    }
172
173    /// Function to readout the APM Data of the AEM10900, based on the mode set in the configs
174    #[maybe_async_attr]
175    pub async fn read_apm_data(&mut self) -> Result<ApmData, Error<I2C>> {
176        let mut byte_arr: [u8; 3] = [0; 3];
177
178        self.read_regs(Apm0::ADDRESS, &mut byte_arr).await?;
179        let apm0 = Apm0::from_le_bytes(&[byte_arr[0]]);
180        let apm1 = Apm1::from_le_bytes(&[byte_arr[1]]);
181        let apm2 = Apm2::from_le_bytes(&[byte_arr[2]]);
182
183        let data: u32 =
184            ((apm2.data & 0x0F) as u32) << 16 | ((apm1.data as u32) << 8) | (apm0.data as u32);
185
186        match self.conf.apm.mode {
187            Mode::PowerMeter => {
188                let shift = (apm2.data >> 4) & 0x0F;
189                // TODO: How to handle different V_SRC...
190                // Assume for now V_SRC > 0.7V
191                Ok(ApmData::PowerMeterResult((data << shift) as f32 * 0.10166))
192            }
193            Mode::PulseCounter => Ok(ApmData::PulseCounterResult(data)),
194        }
195    }
196
197    /// Function to readout the battery voltage of the AEM10900
198    #[maybe_async_attr]
199    pub async fn read_battery_voltage(&mut self) -> Result<f32, Error<I2C>> {
200        let mut byte_arr: [u8; 1] = [0];
201
202        self.read_regs(Sto::ADDRESS, &mut byte_arr).await?;
203        let sto = Sto::from_le_bytes(&[byte_arr[0]]);
204
205        // Convert result to a voltage
206        Ok(4.8 * (sto.data as f32) / 256.0)
207    }
208
209    /// Function to readout the source voltage of the AEM10900
210    #[maybe_async_attr]
211    pub async fn read_source_voltage(&mut self) -> Result<f32, Error<I2C>> {
212        let mut byte_arr: [u8; 1] = [0];
213
214        self.read_regs(Src::ADDRESS, &mut byte_arr).await?;
215        let src = Src::from_le_bytes(&[byte_arr[0]]);
216
217        // Convert result to a source voltage
218        self.source_voltage_lookup(src)
219    }
220
221    /// Function to convert a SRC register reading into a voltage
222    fn source_voltage_lookup(&self, src: Src) -> Result<f32, Error<I2C>> {
223        // implemented with an array instead of a hash map, as this can be defined const
224        const SRC_VOLTAGE_MIN_KEY: u8 = 0x06;
225        const SRC_VOLTAGE_MAX_KEY: u8 = 0x3A;
226        const TABLE_LENGTH: usize = (SRC_VOLTAGE_MAX_KEY - SRC_VOLTAGE_MIN_KEY + 1) as usize;
227
228        const SOURCE_VOLTAGE_LOOKUP_TABLE: [u16; TABLE_LENGTH] = [
229            113, 128, 142, 158, 172, 188, 203, 217, 233, 247, 263, 278, 292, 315, 345, 375, 405,
230            435, 465, 495, 525, 555, 585, 615, 645, 675, 705, 735, 765, 795, 825, 855, 885, 915,
231            945, 975, 1005, 1035, 1065, 1095, 1125, 1155, 1185, 1215, 1245, 1275, 1305, 1335, 1365,
232            1395, 1425, 1455, 1485,
233        ];
234
235        if src.data < SRC_VOLTAGE_MIN_KEY || src.data > SRC_VOLTAGE_MAX_KEY {
236            return Err(Error::Value("Src.data is not within allowed range"));
237        }
238        Ok(SOURCE_VOLTAGE_LOOKUP_TABLE[(src.data - SRC_VOLTAGE_MIN_KEY) as usize] as f32 / 1000.0)
239    }
240}
241
242/// Data Type for the APM Data, depending on the mode (power meter or pulse counter)
243pub enum ApmData {
244    /// Result of the power meter mode, as float f32
245    PowerMeterResult(f32),
246    /// Result of the pulse counter mode, as u32
247    PulseCounterResult(u32),
248}