embedded-ccs811 0.2.0

Platform-agnostic Rust driver for the CCS811 ultra-low power digital gas sensor for monitoring indoor air quality.
Documentation
use crate::{hal, Ccs811Awake, DeviceErrors, ErrorAwake};

pub(crate) struct Register {}
impl Register {
    pub const STATUS: u8 = 0x00;
    pub const MEAS_MODE: u8 = 0x01;
    pub const ALG_RESULT_DATA: u8 = 0x02;
    pub const RAW_DATA: u8 = 0x03;
    pub const ENV_DATA: u8 = 0x05;
    pub const THRESHOLDS: u8 = 0x10;
    pub const BASELINE: u8 = 0x11;
    pub const HW_ID: u8 = 0x20;
    pub const HW_VERSION: u8 = 0x21;
    pub const FW_BOOT_VERSION: u8 = 0x23;
    pub const FW_APP_VERSION: u8 = 0x24;
    pub const ERROR_ID: u8 = 0xE0;
    pub const APP_ERASE: u8 = 0xF1;
    pub const REG_BOOT_APP: u8 = 0xF2;
    pub const APP_VERIFY: u8 = 0xF3;
    pub const APP_START: u8 = 0xF4;
    pub const SW_RESET: u8 = 0xFF;
}

pub(crate) struct BitFlags {}
impl BitFlags {
    pub const DATA_READY: u8 = 1 << 3;
    pub const APP_VALID: u8 = 1 << 4;
    pub const APP_VERIFY: u8 = 1 << 5;
    pub const APP_ERASE: u8 = 1 << 6;
    pub const FW_MODE: u8 = 1 << 7;
    pub const ERROR: u8 = 1;
    pub const WRITE_REG_INVALID: u8 = 1;
    pub const READ_REG_INVALID: u8 = 1 << 1;
    pub const MEASMODE_INVALID: u8 = 1 << 2;
    pub const MAX_RESISTANCE: u8 = 1 << 3;
    pub const HEATER_FAULT: u8 = 1 << 4;
    pub const HEATER_SUPPLY: u8 = 1 << 5;
    pub const INTERRUPT: u8 = 1 << 3;
    pub const THRESH: u8 = 1 << 2;
}

impl<I2C, E, MODE> Ccs811Awake<I2C, MODE>
where
    I2C: hal::blocking::i2c::Write<Error = E> + hal::blocking::i2c::WriteRead<Error = E>,
{
    pub(crate) fn check_status_error(&mut self) -> Result<(), ErrorAwake<E>> {
        self.read_status().map(drop)
    }

    pub(crate) fn read_status(&mut self) -> Result<u8, ErrorAwake<E>> {
        let mut data = [0];
        self.i2c
            .write_read(self.address, &[Register::STATUS], &mut data)
            .map_err(ErrorAwake::I2C)?;
        let status = data[0];
        if (status & BitFlags::ERROR) != 0 {
            self.i2c
                .write_read(self.address, &[Register::ERROR_ID], &mut data)
                .map_err(ErrorAwake::I2C)?;
            get_errors(data[0]).map_err(ErrorAwake::Device)?;
        }
        Ok(status)
    }

    pub(crate) fn read_register_1byte(&mut self, register: u8) -> Result<u8, ErrorAwake<E>> {
        let mut data = [0];
        self.read_register(register, &mut data).and(Ok(data[0]))
    }

    pub(crate) fn read_register_2bytes(&mut self, register: u8) -> Result<[u8; 2], ErrorAwake<E>> {
        let mut data = [0; 2];
        self.read_register(register, &mut data).and(Ok(data))
    }

    pub(crate) fn read_register(
        &mut self,
        register: u8,
        data: &mut [u8],
    ) -> Result<(), ErrorAwake<E>> {
        self.i2c
            .write_read(self.address, &[register], data)
            .map_err(ErrorAwake::I2C)?;
        self.check_status_error()
    }

    pub(crate) fn write_register_no_data(&mut self, register: u8) -> Result<(), ErrorAwake<E>> {
        self.i2c
            .write(self.address, &[register])
            .map_err(ErrorAwake::I2C)?;
        self.check_status_error()
    }

    pub(crate) fn write_register_1byte(
        &mut self,
        register: u8,
        data: u8,
    ) -> Result<(), ErrorAwake<E>> {
        self.i2c
            .write(self.address, &[register, data])
            .map_err(ErrorAwake::I2C)?;
        self.check_status_error()
    }
}

pub(crate) fn get_errors(error_id: u8) -> Result<(), DeviceErrors> {
    let mut has_error = false;
    let mut errors = DeviceErrors::default();
    if (error_id & BitFlags::WRITE_REG_INVALID) != 0 {
        errors.invalid_register_write = true;
        has_error = true;
    }
    if (error_id & BitFlags::READ_REG_INVALID) != 0 {
        errors.invalid_register_read = true;
        has_error = true;
    }
    if (error_id & BitFlags::MEASMODE_INVALID) != 0 {
        errors.invalid_measurement = true;
        has_error = true;
    }
    if (error_id & BitFlags::MAX_RESISTANCE) != 0 {
        errors.max_resistance = true;
        has_error = true;
    }
    if (error_id & BitFlags::HEATER_FAULT) != 0 {
        errors.heater_fault = true;
        has_error = true;
    }
    if (error_id & BitFlags::HEATER_SUPPLY) != 0 {
        errors.heater_supply = true;
        has_error = true;
    }
    if has_error {
        Err(errors)
    } else {
        Ok(())
    }
}