1use embedded_hal::delay::DelayNs;
4use embedded_hal::i2c::I2c;
5use pamoja_core::Sensor;
6
7use super::{conversion_micros, register, Config, DataRate, Mode, Mux, Pga, Sample};
8use crate::driver::{read_word, write_word};
9use crate::error::{DriverError, SensorError};
10
11pub const CONVERSION_POLLS: u8 = 10;
14
15#[derive(Debug)]
48pub struct Ads1115<I2C, D> {
49 bus: I2C,
50 delay: D,
51 address: u8,
52 config: Config,
53 initialized: bool,
54}
55
56impl<I2C, D> Ads1115<I2C, D> {
57 pub fn new(bus: I2C, address: u8, delay: D) -> Self {
70 Ads1115 {
71 bus,
72 delay,
73 address,
74 config: Config {
75 start_conversion: false,
76 mode: Mode::SingleShot,
77 ..Config::default()
78 },
79 initialized: false,
80 }
81 }
82
83 pub fn with_input(mut self, mux: Mux) -> Self {
93 self.config.mux = mux;
94 self.initialized = false;
95 self
96 }
97
98 pub fn with_gain(mut self, pga: Pga) -> Self {
108 self.config.pga = pga;
109 self.initialized = false;
110 self
111 }
112
113 pub fn with_data_rate(mut self, rate: DataRate) -> Self {
123 self.config.data_rate = rate;
124 self.initialized = false;
125 self
126 }
127
128 pub fn config(&self) -> Config {
130 self.config
131 }
132
133 pub fn release(self) -> (I2C, D) {
139 (self.bus, self.delay)
140 }
141}
142
143impl<I2C: I2c, D: DelayNs> Ads1115<I2C, D> {
144 pub fn init(&mut self) -> Result<(), DriverError<I2C::Error>> {
155 let written = self.config.bits();
156 write_word(&mut self.bus, self.address, register::CONFIG, written)
157 .map_err(DriverError::Bus)?;
158 let read =
159 read_word(&mut self.bus, self.address, register::CONFIG).map_err(DriverError::Bus)?;
160 if Config::from_bits(read).bits() & 0x7FFF != written & 0x7FFF {
161 return Err(SensorError::Identity.into());
162 }
163 self.initialized = true;
164 Ok(())
165 }
166
167 pub fn sample(&mut self) -> Result<Sample, DriverError<I2C::Error>> {
183 if !self.initialized {
184 self.init()?;
185 }
186 let start = Config {
187 start_conversion: true,
188 ..self.config
189 };
190 write_word(&mut self.bus, self.address, register::CONFIG, start.bits())
191 .map_err(DriverError::Bus)?;
192 self.delay
193 .delay_us(conversion_micros(self.config.data_rate));
194
195 let mut idle = false;
196 for _ in 0..CONVERSION_POLLS {
197 let status = read_word(&mut self.bus, self.address, register::CONFIG)
198 .map_err(DriverError::Bus)?;
199 if Config::from_bits(status).start_conversion {
200 idle = true;
201 break;
202 }
203 self.delay.delay_ms(1);
204 }
205 if !idle {
206 return Err(DriverError::Timeout);
207 }
208
209 let raw = read_word(&mut self.bus, self.address, register::CONVERSION)
210 .map_err(DriverError::Bus)?;
211 Ok(Sample {
212 raw: raw as i16,
213 pga: self.config.pga,
214 })
215 }
216
217 pub fn sample_input(&mut self, mux: Mux) -> Result<Sample, DriverError<I2C::Error>> {
231 let configured = self.config.mux;
232 self.config.mux = mux;
233 self.initialized = false;
234 let result = self.sample();
235 self.config.mux = configured;
236 self.initialized = false;
237 result
238 }
239}
240
241impl<I2C, D> Sensor for Ads1115<I2C, D>
242where
243 I2C: I2c + Send,
244 I2C::Error: core::fmt::Debug,
245 D: DelayNs + Send,
246{
247 type Reading = Sample;
248
249 async fn read(&mut self) -> pamoja_core::Result<Sample> {
250 self.sample().map_err(pamoja_core::Error::from)
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257 use crate::ads1115::address;
258 use alloc::vec;
259 use alloc::vec::Vec;
260 use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource};
261 use pamoja_hal::script::{block_on, DelayLog, I2cScript, I2cStep};
262
263 fn config(mux: Mux, pga: Pga, rate: DataRate, start: bool) -> [u8; 2] {
264 Config {
265 start_conversion: start,
266 mux,
267 pga,
268 mode: Mode::SingleShot,
269 data_rate: rate,
270 ..Config::default()
271 }
272 .bits()
273 .to_be_bytes()
274 }
275
276 fn init_steps(address: u8, settings: [u8; 2]) -> Vec<I2cStep> {
277 vec![
278 I2cStep::write(address, [register::CONFIG, settings[0], settings[1]]),
279 I2cStep::write_read(address, [register::CONFIG], settings),
280 ]
281 }
282
283 fn sample_steps(address: u8, started: [u8; 2], raw: i16) -> Vec<I2cStep> {
284 vec![
285 I2cStep::write(address, [register::CONFIG, started[0], started[1]]),
286 I2cStep::write_read(address, [register::CONFIG], started),
287 I2cStep::write_read(address, [register::CONVERSION], raw.to_be_bytes()),
288 ]
289 }
290
291 #[test]
292 fn a_single_shot_conversion_follows_the_datasheet_sequence() {
293 let (mux, pga, rate) = (Mux::Ain0Gnd, Pga::Fsr4_096, DataRate::Sps860);
294 let mut steps = init_steps(address::SDA, config(mux, pga, rate, false));
295 steps.extend(sample_steps(
296 address::SDA,
297 config(mux, pga, rate, true),
298 -1024,
299 ));
300 let mut adc = Ads1115::new(I2cScript::new(steps), address::SDA, DelayLog::new())
301 .with_input(mux)
302 .with_gain(pga)
303 .with_data_rate(rate);
304 let sample = adc.sample().unwrap();
305 assert_eq!(sample.raw, -1024);
306 assert_eq!(sample.pga, pga);
307 assert!((sample.volts() + 0.128).abs() < 1e-6);
308 let (bus, delay) = adc.release();
309 assert!(bus.done());
310 assert_eq!(delay.waits_ns(), [1_280_000], "860 SPS plus ten percent");
311 }
312
313 #[test]
314 fn the_readback_identity_check_refuses_a_part_that_holds_something_else() {
315 let settings = config(Mux::Ain0Ain1, Pga::Fsr2_048, DataRate::Sps128, false);
316 let steps = [
317 I2cStep::write(address::GND, [register::CONFIG, settings[0], settings[1]]),
318 I2cStep::write_read(address::GND, [register::CONFIG], [0x00, 0x00]),
319 ];
320 let mut adc = Ads1115::new(I2cScript::new(steps), address::GND, DelayLog::new());
321 assert_eq!(adc.init(), Err(DriverError::Sensor(SensorError::Identity)));
322 }
323
324 #[test]
325 fn a_conversion_that_never_finishes_is_a_timeout() {
326 let settings = config(Mux::Ain0Ain1, Pga::Fsr2_048, DataRate::Sps128, false);
327 let started = config(Mux::Ain0Ain1, Pga::Fsr2_048, DataRate::Sps128, true);
328 let mut steps = init_steps(address::GND, settings);
329 steps.push(I2cStep::write(
330 address::GND,
331 [register::CONFIG, started[0], started[1]],
332 ));
333 for _ in 0..CONVERSION_POLLS {
334 steps.push(I2cStep::write_read(
335 address::GND,
336 [register::CONFIG],
337 settings,
338 ));
339 }
340 let mut adc = Ads1115::new(I2cScript::new(steps), address::GND, DelayLog::new());
341 assert_eq!(adc.sample(), Err(DriverError::Timeout));
342 let (bus, delay) = adc.release();
343 assert!(bus.done());
344 assert_eq!(delay.total_micros(), 8_595 + 10_000);
345 }
346
347 #[test]
348 fn a_bus_fault_surfaces_as_a_bus_error() {
349 let kind = ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address);
350 let mut adc = Ads1115::new(
351 I2cScript::new([I2cStep::fault(address::GND, kind)]),
352 address::GND,
353 DelayLog::new(),
354 );
355 assert!(matches!(adc.init(), Err(DriverError::Bus(_))));
356 }
357
358 #[test]
359 fn sampling_another_input_once_leaves_the_configured_one_in_place() {
360 let (pga, rate) = (Pga::Fsr2_048, DataRate::Sps128);
361 let mut steps = init_steps(address::GND, config(Mux::Ain1Gnd, pga, rate, false));
362 steps.extend(sample_steps(
363 address::GND,
364 config(Mux::Ain1Gnd, pga, rate, true),
365 100,
366 ));
367 steps.extend(init_steps(
368 address::GND,
369 config(Mux::Ain0Gnd, pga, rate, false),
370 ));
371 steps.extend(sample_steps(
372 address::GND,
373 config(Mux::Ain0Gnd, pga, rate, true),
374 3200,
375 ));
376 let mut adc = Ads1115::new(I2cScript::new(steps), address::GND, DelayLog::new())
377 .with_input(Mux::Ain0Gnd);
378 assert_eq!(adc.sample_input(Mux::Ain1Gnd).unwrap().raw, 100);
379 assert_eq!(adc.config().mux, Mux::Ain0Gnd);
380 assert_eq!(adc.sample().unwrap().raw, 3200);
381 assert!(adc.release().0.done());
382 }
383
384 #[test]
385 fn as_a_sensor_it_reads_and_maps_to_volts() {
386 let (mux, pga, rate) = (Mux::Ain0Gnd, Pga::Fsr2_048, DataRate::Sps128);
387 let mut steps = init_steps(address::GND, config(mux, pga, rate, false));
388 steps.extend(sample_steps(
389 address::GND,
390 config(mux, pga, rate, true),
391 3200,
392 ));
393 let adc =
394 Ads1115::new(I2cScript::new(steps), address::GND, DelayLog::new()).with_input(mux);
395 let mut volts = adc.map(|sample| sample.volts());
396 let reading = block_on(volts.read()).unwrap();
397 assert!((reading - 0.2).abs() < 1e-6);
398 }
399
400 #[test]
401 fn a_single_shot_conversion_makes_the_transfers_the_datasheet_gives() {
402 const PART: u8 = crate::ads1115::address::GND;
403 let bus = I2cScript::new([
404 I2cStep::write(PART, [0x01, 0x45, 0x83]),
405 I2cStep::write_read(PART, [0x01], [0x45, 0x83]),
406 I2cStep::write(PART, [0x01, 0xC5, 0x83]),
407 I2cStep::write_read(PART, [0x01], [0xC5, 0x83]),
408 I2cStep::write_read(PART, [0x00], [0x0C, 0x80]),
409 ]);
410
411 let adc = Ads1115::new(bus, PART, DelayLog::new()).with_input(crate::ads1115::Mux::Ain0Gnd);
412 let mut volts = adc.map(|sample| sample.volts());
413 let reading = block_on(Sensor::read(&mut volts)).expect("the scripted part answers");
414 assert!((reading - 0.2).abs() < 1e-6);
415 }
416}