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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
//! # Identifying pixels
//! The Grid-EYE has an 8x8 resolution, with the pixels numbered 1 through 64. When looking at the
//! sensor head-on, vertically oriented with the lens window on top, pixel 1 is on your lower left.
//! Pixels are then counted left to right, bottom to top.
use core::convert::TryFrom;

use embedded_hal::blocking::i2c;
use ndarray::{aview1, Array2};

mod constants;
mod temperature;

pub use constants::*;
use temperature::{
    float_to_pixel_temperature, pixel_buffer_to_float_buffer, pixel_temperature_to_float,
    thermistor_temperature_to_float,
};

pub struct GridEye<I2C> {
    /// The I2C bus to communicate on.
    i2c: I2C,

    /// The I2C address this camera is using.
    address: u8,
}

impl<I2C> GridEye<I2C>
where
    I2C: i2c::WriteRead,
{
    // This function will always succeed, but it's not guaranteed that it'll actually work once
    // later functions try accessing the device over I2C.
    pub fn new(bus: I2C, address: Address) -> Self {
        Self {
            i2c: bus,
            address: (address.into()),
        }
    }

    /// Write a byte of data to a register.
    fn write_byte(&mut self, register: Register, value: &u8) -> Result<(), I2C::Error> {
        let register_data = [register.into(), *value];
        self.i2c.write_read(self.address, &register_data, &mut [] as &mut [u8])
    }

    /// Read a byte of data from a register.
    fn read_byte(&mut self, register: Register) -> Result<u8, I2C::Error> {
        let register_buffer = [register.into()];
        let mut result = [0];
        self.i2c.write_read(self.address, &register_buffer, &mut result).and(Ok(result[0]))
    }

    /// Retrieve the current power mode the sensor is in. See [PowerControlValue] for a description
    /// of the different modes.
    ///
    /// This function will panic if the value read from the device is not one of the documented
    /// values.
    pub fn power_mode(&mut self) -> Result<PowerControlValue, I2C::Error> {
        self.read_byte(Register::PowerControl).map(|x| {
            PowerControlValue::try_from(x).expect(
                "the power control value from the GridEYE to be one of the documented values",
            )
        })
    }

    /// Set the sensor's power mode. See [PowerControlValue] for a description of the different
    /// settings as well as the process for waking up from [PowerControlValue::Sleep].
    pub fn set_power_mode(&mut self, value: PowerControlValue) -> Result<(), I2C::Error> {
        self.write_byte(Register::PowerControl, &(value.into()))
    }

    /// Reset status [flags](flags) and the interrupt table.
    pub fn reset_flags(&mut self) -> Result<(), I2C::Error> {
        self.write_byte(Register::Reset, &(ResetValue::Flag.into()))
    }

    /// Perform a soft-reset, clearing status [flags](flags), the interrupt table, and re-read
    /// calibration data. This should *only* be used when moving from [PowerControlValue::Sleep] to
    /// [PowerControlValue::Normal].
    pub fn reset_initial(&mut self) -> Result<(), I2C::Error> {
        self.write_byte(Register::Reset, &(ResetValue::Initial.into()))
    }

    /// Get the current camera frame rate.
    ///
    /// This function will panic if the value read from the device is not one of the documented
    /// values.
    pub fn frame_rate(&mut self) -> Result<FrameRateValue, I2C::Error> {
        self.read_byte(Register::FrameRate).map(|x| {
            FrameRateValue::try_from(x)
                .expect("the frame rate from the GridEYE to be one of the documented values")
        })
    }

    /// Set the camera frame rate.
    pub fn set_frame_rate(&mut self, value: FrameRateValue) -> Result<(), I2C::Error> {
        self.write_byte(Register::FrameRate, &(value.into()))
    }

    // TODO: I hate how these next three function are named.

    /// Check if external interrupts are being generated.
    pub fn interrupts_enabled(&mut self) -> Result<bool, I2C::Error> {
        self.read_byte(Register::InterruptControl)
            .map(|x| InterruptControlValue::from(x).enabled())
    }

    /// Enable external interrupts.
    pub fn enable_interrupts(&mut self) -> Result<(), I2C::Error> {
        let mut current = InterruptControlValue::from(self.read_byte(Register::InterruptControl)?);
        if current.enabled() {
            return Ok(());
        } else {
            current.enable();
        }
        self.write_byte(Register::InterruptControl, &u8::from(current))
    }

    /// Disable external interrupts.
    pub fn disable_interrupts(&mut self) -> Result<(), I2C::Error> {
        let mut current = InterruptControlValue::from(self.read_byte(Register::InterruptControl)?);
        if !current.enabled() {
            return Ok(());
        } else {
            current.disable();
        }
        self.write_byte(Register::InterruptControl, &u8::from(current))
    }

    /// Get the current interrupt mode. Interrupts are either generated when a pixel's value
    /// exceeds the interrupt levels (absolute mode), or if the change in temperature exceeds the
    /// interrupt levels (difference mode).
    pub fn interrupt_mode(&mut self) -> Result<InterruptControlMode, I2C::Error> {
        self.read_byte(Register::InterruptControl)
            .map(|x| InterruptControlValue::from(x).mode())
    }

    /// Set the interrupt mode.
    pub fn set_interrupt_mode(&mut self, mode: InterruptControlMode) -> Result<(), I2C::Error> {
        let mut current = InterruptControlValue::from(self.read_byte(Register::InterruptControl)?);
        if current.mode() == mode {
            return Ok(());
        } else {
            current.set_mode(mode);
        }
        self.write_byte(Register::InterruptControl, &u8::from(current))
    }

    /// Get the current status flags.
    pub fn flags(&mut self) -> Result<StatusValue, I2C::Error> {
        self.read_byte(Register::Status).map(StatusValue::from)
    }

    /// Check if the temperature overflow flag has been set.
    pub fn overflow_flag(&mut self) -> Result<bool, I2C::Error> {
        Ok(self.flags()?.temperature_overflow())
    }

    /// Check if the interrupt flag has been set.
    pub fn interrupt_flag(&mut self) -> Result<bool, I2C::Error> {
        Ok(self.flags()?.interrupt())
    }

    /// Internal function for clearing one or both of the status flags.
    fn clear_flags(&mut self, overflow: bool, interrupt: bool) -> Result<(), I2C::Error> {
        self.write_byte(
            Register::StatusClear,
            &u8::from(StatusValue::new(overflow, interrupt)),
        )
    }

    /// Clear both status flags.
    pub fn clear_all_flags(&mut self) -> Result<(), I2C::Error> {
        self.clear_flags(true, true)
    }

    /// Clear the temperature overflow flag.
    pub fn clear_overflow_flag(&mut self) -> Result<(), I2C::Error> {
        self.clear_flags(true, false)
    }

    /// Clear the interrupt flag.
    pub fn clear_interrupt_flag(&mut self) -> Result<(), I2C::Error> {
        self.clear_flags(false, true)
    }

    /// Check if moving average mode is enabled.
    pub fn moving_average_enabled(&mut self) -> Result<bool, I2C::Error> {
        self.read_byte(Register::Average)
            .map(AverageValue::from)
            .map(bool::from)
    }

    /// Internal function for setting moving average mode. When changing the mode you have to write
    /// a series of values to an otherwise undocumented register.
    fn set_moving_average(&mut self, new_setting: bool) -> Result<(), I2C::Error> {
        // When setting this value, you first have to write `0x50`, `0x45`, and `0x57` to
        // [Register::AverageData], then write the [AverageValue] to [Register::Average], then
        // write `0x00` to [Register::AverageData].
        self.write_byte(Register::AverageData, &0x50)?;
        self.write_byte(Register::AverageData, &0x45)?;
        self.write_byte(Register::AverageData, &0x57)?;
        let value: u8 = AverageValue::from(new_setting).into();
        self.write_byte(Register::Average, &value)?;
        self.write_byte(Register::AverageData, &0x00)
    }

    /// Enable moving average mode. There're references to it averaging the values of the last 10
    /// frames of a pixel (twice?), but it isn't clear how this interacts with the frame rate
    /// setting.
    pub fn enable_moving_average(&mut self) -> Result<(), I2C::Error> {
        self.set_moving_average(true)
    }

    /// Disable moving average mode.
    pub fn disable_moving_average(&mut self) -> Result<(), I2C::Error> {
        self.set_moving_average(false)
    }

    /// Retrieve and convert one of the interrupt levels.
    fn interrupt_level(&mut self, low_register: Register) -> Result<f32, I2C::Error> {
        let src = [low_register.into()];
        let mut result = [0_u8; 2];
        self.i2c.write_read(self.address, &src, &mut result)?;
        Ok(pixel_temperature_to_float(&result))
    }

    /// Get the current upper interrupt temperature limit in degrees Celsius.
    pub fn upper_interrupt_level(&mut self) -> Result<f32, I2C::Error> {
        self.interrupt_level(Register::InterruptLevelHighLower)
    }

    /// Get the current lower interrupt temperature limit in degrees Celsius.
    pub fn lower_interrupt_level(&mut self) -> Result<f32, I2C::Error> {
        self.interrupt_level(Register::InterruptLevelLowLower)
    }

    /// Get the current interrupt hysteresis level.
    pub fn hysteresis_interrupt_level(&mut self) -> Result<f32, I2C::Error> {
        self.interrupt_level(Register::InterruptHysteresisLower)
    }

    /// Convert the given value to the proper format and set one of the interrupt levels.
    fn set_interrupt_level(
        &mut self,
        low_register: Register,
        value: &f32,
    ) -> Result<(), I2C::Error> {
        // TODO: don't force unwrap (and panic) on out-of-bounds values.
        let data = float_to_pixel_temperature(value).unwrap();
        let mut write_buffer = [0u8; 3];
        write_buffer[0] = low_register.into();
        write_buffer[1..].copy_from_slice(&data);
        self.i2c.write_read(self.address, &write_buffer, &mut [] as &mut [u8])
    }

    /// Set the upper interrupt temperature limit in degrees Celsius.
    pub fn set_upper_interrupt_level(&mut self, value: &f32) -> Result<(), I2C::Error> {
        self.set_interrupt_level(Register::InterruptLevelHighLower, value)
    }

    /// Set the lower interrupt temperature limit in degrees Celsius.
    pub fn set_lower_interrupt_level(&mut self, value: &f32) -> Result<(), I2C::Error> {
        self.set_interrupt_level(Register::InterruptLevelLowLower, value)
    }

    /// Set the interrupt hysteresis level. The format is the same as the upper and lower levels,
    /// but the datasheet isn't clear how it interacts with the interrupt generation.
    pub fn set_hysteresis_interrupt_level(&mut self, value: &f32) -> Result<(), I2C::Error> {
        self.set_interrupt_level(Register::InterruptHysteresisLower, value)
    }

    /// Get the temperature of the sensor itself using the built-in thermistor. It has a range of
    /// -20° to 80° with a resolution of 0.0625° (all units in Celsius).
    pub fn thermistor(&mut self) -> Result<f32, I2C::Error> {
        let src = [Register::ThermistorLower.into()];
        let mut temperature_bytes = [0_u8; 2];
        self.i2c.write_read(self.address, &src, &mut temperature_bytes)?;
        Ok(thermistor_temperature_to_float(&temperature_bytes))
    }

    pub fn image(&mut self) -> Result<Array2<f32>, I2C::Error> {
        // Pull the data out of the sensor
        let src = [Register::PixelTemperatureStart.into()];
        let mut pixel_bytes = [0_u8; 128];
        self.i2c.write_read(self.address, &src, &mut pixel_bytes)?;
        // convert it to an array of floats
        let temps = pixel_buffer_to_float_buffer(&pixel_bytes);
        // reproject the 1D array to a 2D array and we're done
        let view_1d = aview1(&temps);
        Ok(view_1d
            .into_shape((8, 8))
            .expect("a 64 element array to fit in an 8x8 grid")
            .to_owned())
    }

    // TODO: Skipping interrupt table reading for now...
}