simple CCS811 driver

Dependencies:   AMS_ENS210_temp_humid_sensor

Dependents:   TBSense2_Sensor_Demo

Fork of AMS_CCS811_gas_sensor by Marcus Lee

AMS_CCS811.h

Committer:
Steven Cooreman
Date:
10 months ago
Revision:
15:cf658680c53f
Parent:
8:58a36d9218be

File content as of revision 15:cf658680c53f:

/**
* @author Marcus Lee
*
* @section DESCRIPTION
*    A library for the AMS CCS811 digital gas sensor.
*
*/
#ifndef AMS_CCS811_H
#define AMS_CCS811_H

#include "mbed.h"
#include "AMS_ENS210.h"

/* Library defaults */
#define CONFIG_OP_MODE              TEN_SECOND              // Every 10 seconds
#define CONFIG_INTR                 0                       // Interupt off
#define CONFIG_ADDR_DIR             0                       // ADDR n_wake_pin pulled low
#define CONFIG_ENS210_POLL          3000                    // ENS210 is polled every 3 seconds

/* Library Constants */
#define CCS811_SLAVE_ADDR_RAW_H     0x5B
#define CCS811_SLAVE_ADDR_RAW_L     0x5A
#define CCS811_SLAVE_ADDR_RAW       _slave_addr
#define CCS811_SLAVE_ADDR           CCS811_SLAVE_ADDR_RAW << 1
#define CCS811_SLAVE_ADDR_W         CCS811_SLAVE_ADDR
#define CCS811_SLAVE_ADDR_R         CCS811_SLAVE_ADDR | 1

#define MEAS_MODE                   0x01
#define STATUS                      0x00

#define ALG_RESULT_DATA             0x02
#define RAW_DATA                    0x03
#define ENV_DATA                    0x05
#define ERROR_ID                    0xE0

#define HW_ID                       0x20
#define HW_VERSION                  0x21
#define FW_BOOT_VERSION             0x23
#define FW_APP_VERSION              0x24
#define FW_ERASE                    0xF1
#define FW_FLASH                    0xF2
#define FW_VERIFY                   0xF3
#define APP_START                   0xF4

#define CCS811_T_AWAKE              55                      // us - time taken for sensor I2C to become active
#define CCS811_T_DWAKE              25                      // us - time taken for sensor I2C to become inactive

#define CCS811_MAX_HUMID            127.998046875           // maxmium value that can be represented in the register
#define CCS811_MAX_TEMP             102.998046875           // maxmium value that can be represented in the register

/* Error Codes */
#define CCS811_NO_ERROR             "No Error";
/* Sensor Errors */
#define CCS811_ERR_NUM              8
#define CCS811_WRITE_REG_INVALID    "The CCS811 received an I2C write request addressed to this station but with invalid register address ID"
#define CCS811_READ_REG_INVALID     "The CCS811 received an I2C read request to a mailbox ID that is invalid"
#define CCS811_MEASMODE_INVALID     "The CCS811 received an I2C request to write an unsupported mode to MEAS_MODE"
#define CCS811_MAX_RESISTANCE       "The sensor resistance measurement has reached or exceeded the maximum range"
#define CCS811_HEATER_FAULT         "The Heater current in the CCS811 is not in range"
#define CCS811_HEATER_SUPPLY        "The Heater voltage is not being applied correctly"
#define CCS811_RESERVED             "Reserved for Future Use"
/* Library Errors */
#define CCS811_LIB_ERR_NUM          9
#define CCS811_LIB_N_WAKE_ID        0
#define CCS811_LIB_N_WAKE           "nWAKE pin not set"
#define CCS811_LIB_I2C_ID           1
#define CCS811_LIB_I2C              "I2C interface is NULL"
#define CCS811_LIB_SLAVE_W_ID       2
#define CCS811_LIB_SLAVE_W          "Failed to write slave write address"
#define CCS811_LIB_REG_ADDR_ID      3
#define CCS811_LIB_REG_ADDR         "Failed to write register address"
#define CCS811_LIB_I2CWRITE_ID      4
#define CCS811_LIB_I2CWRITE         "Failed to write byte"
#define CCS811_LIB_SLAVE_R_ID       5
#define CCS811_LIB_SLAVE_R          "Failed to write slave read address"
#define CCS811_LIB_INV_MODE_ID      6
#define CCS811_LIB_INV_MODE         "Invalid operation mode"
#define CCS811_LIB_ENS210_INIT_ID   7
#define CCS811_LIB_ENS210_INIT      "Failed to create new AMS_ENS210 object"
#define CCS811_LIB_ENS210_POLL_ID   7
#define CCS811_LIB_ENS210_POLL      "AMS_ENS210 poll error"

#define CCS811_TOTAL_ERR_NUM        CCS811_ERR_NUM+CCS811_LIB_ERR_NUM


/** The AMS CCS811 class
 */
class AMS_CCS811
{
    public:
        /** Sensor operation modes.
         *
         */
        enum OP_MODES {
            IDLE,           /**< Measurements disabled */
            SECOND,         /**< Measurement every second */
            TEN_SECOND,     /**< Measurement every 10 seconds */
            SIXTY_SECOND,   /**< Measurement every 60 seconds */
            CONSTANT,       /**< Measurement every 250ms - Only raw data available */
            INVALID         /**< Invalid bit configuration/Error Occured */
        };

        /** Holds error information.
         *
         */
        struct ccs811_errors {
            int count;                          /**< Number of total errors */
            int codes[CCS811_TOTAL_ERR_NUM];    /**< Array of active error codes */

            ccs811_errors() : count(0) {}
        };

        /** Create an AMS_CCS811 instance
         *
         * @param i2c           The I2C interface to use for communication
         * @param n_wake_pin    Pin nWAKE is attached to
         */
        AMS_CCS811(I2C * i2c, PinName n_wake_pin);

        /** Create an AMS_CCS811 instance
         *
         * @param i2c           The I2C interface to use for communication
         * @param n_wake_pin    Pin nWAKE is attached to
         * @param ens210_i2c    The I2C interface for an attached AMS_ENS210
         */
        AMS_CCS811(I2C * i2c, PinName n_wake_pin, I2C * ens210_i2c);

        /** Destroy the AMS_CCS811 instance
         */
        ~AMS_CCS811();

        /** Initalise the sensor
         *
         * @return Intalisation success
         */
        bool init();

        /** Overwrite the sensor's firmware
         *
         * @return upload success
         */
        bool flash_firmware();

        /** Set the I2C interface
         *
         * @param i2c  The I2C interface to use for communication
         *
         */
        void i2c_interface(I2C * i2c);

        /** Set the ENS210 I2C interface
         *
         * @param i2c  The I2C interface for an attached AMS_ENS210
         *
         * @return Success
         */
        bool ens210_i2c_interface(I2C * i2c);

        /** Set whether the attached AMS_ENS210 is enabled.
         *  If an I2C interface is not set for the ENS210, calling this method will have no effect.
         *
         * @param enabled   True for enabled, false for disabled
         *
         * @return enabled  True for enabled, false for disabled
         */
        bool enable_ens210(bool enable);

        /** Get whether the attached AMS_ENS210 is enabled.
         *
         * @return enabled    True for enabled, false for disabled
         *
         */
        bool ens210_is_enabled();

        /** Set the AMS_ENS210 poll interval
         *
         * @param poll_ms  Poll interval in ms
         *
         */
        void ens210_poll_interval(int poll_ms);

        /** Get the AMS_ENS210 poll interval
         *
         * @return The poll interval in ms
         */
        int ens210_poll_interval();

        /** Get the current firmware mode
         *
         * @return 1 application mode, 0 for boot mode, -1 for error
         */
        int firmware_mode();

        /** Set the operation mode \n
         * Notes: \n 1.\ When a sensor operating mode is changed to a new mode with\n
         *          a lower sample rate (e.g.\ from SECOND to SIXTY_SECOND), it should be\n
         *          placed in IDLE for at least 10 minutes before enabling the new mode.\ \n
         *          When a sensor operating mode is changed to a new mode with a higher\n
         *          sample rate (e.g.\ from SIXTY_SECOND to SECOND), there is no requirement\n
         *          to wait before enabling the new mode.\ \n
         *  2.\ If this method fails, the state of the config register cannot be guaranteed.\ \n
         *  Check errors and ensure all config settings are as expected.
         *
         * @param mode  OP_MODES mode to set
         *
         * @return Write success
         */
        bool mode(OP_MODES mode);

        /** Get the current power mode
         *
         * @return The current OP_MODES mode
         */
        AMS_CCS811::OP_MODES mode();

        /** Set the ADDR mode \n
         *
         * @param high  True sets to high, false to low
         *
         * @return Write success
         */
        bool addr_mode(bool high);

        /** Get the the ADDR mode
         *
         * @return The current ADDR mode, true for high, false for low
         */
        bool addr_mode();

        /** Set the ADDR pin
         *
         * @param pin    Pin ADDR is attached to
         *
         * @return Write success
         */
        bool addr_pin(PinName pin);

        /** Get the the ADDR pin
         *
         * @return The addr pin
         */
        PinName addr_pin();

        /** Set the nWAKE pin
         *
         * @param pin    Pin nWAKE is attached to
         *
         * @return Write success
         */
        bool n_wake_pin(PinName pin);

        /** Get the the nWAKE pin
         *
         * @return The nWAKE pin
         */
        PinName n_wake_pin();

        /** Set the relative humidity (%) and temperature (C).\ \n
         *  Use when AMS ENS210 is not linked.\ \n
         *  Humidity values are clipped between 0 and CCS811_MAX_HUMID.\ \n
         *  Temperature values are clipped between -25 and CCS811_MAX_TEMP.
         *
         * @return Write success
         */
        bool env_data(float humid, float temp);

        /** Get the sensor collection state
         *  Use when interrupts are disabled.
         *
         * @return Current collection state, 1 for new data ready, 0 for data not ready and -1 for error
         */
        int has_new_data();

        /** Get the most recent CO2 measurement.\ \n
         *  Must call has_new_data() first when when interupts are disabled otherwise the same data will be returned
         *
         * @return Most recent eCO2 measurement in ppm
         */
        uint16_t co2_read();

        /** Get the most recent TVOC measurement.\ \n
         *  Must call has_new_data() first when when interupts are disabled otherwise the same data will be returned
         *
         * @return Most recent TVOC measurement in ppb
         */
        uint16_t tvoc_read();

        /** Get the most recent RAW data.\ \n
         *  Must call has_new_data() first when NOT in CONSTANT mode and interupts are disabled otherwise the same data will be returned.\ \n
         *  When in CONSTANT mode only this read method will return anything other than 0 or NULL.\ If 0 is returned, check for errors.
         *
         * @return Most recent RAW data
         */
        uint16_t raw_read();

        /** Get the most recent Tempurature(C) measurement from the ENS210 (if attached and enabled).\ \n
         *  Will be at most as old as the ENS210 poll time
         *
         * @return Most recent Tempurature(C) measurement from the ENS210
         */
        float temp_read();

        /** Get the most recent Relative Humidity(%) measurement from the ENS210 (if attached and enabled).\ \n
         *  Will be at most as old as the ENS210 poll time
         *
         * @return Most recent Relative Humidity(%) measurement from the ENS210
         */
        float humid_read();

        /** Get current error status
         *
         * @return True when error has occured, false when no error has occured
         */
        bool error_status();

        /** Get the latest errors.
         *
         * @return Latest errors.
         */
        ccs811_errors errors();

        /** Get the error string.
         *
         * @param err_code  Error code to be translated
         *
         * @return Error String.
         */
        const char * error_string(int err_code);

        /** Attach a function to be called when data is ready.
         *  Calling this method enables interupts
         *
         * @param func_ptr  A pointer to the function to be called
         * @param pin       Pin attached to nINT
         *
         * @return Attach success
         */
        bool attach(void (*func_ptr)(void), PinName pin) {
            _isr_data_fp.attach(func_ptr);
            interrupt_pin(pin);
            return enable_interupt(true);
        }

        /** Attach a member function to be called when data is ready.
         *  Calling this method enables interupts
         *
         * @param type_ptr  A pointer to the instance of the class
         * @param mem_ptr   A pointer to the member function
         * @param pin       Pin attached to nINT
         *
         * @return Attach success
         */
        template<typename T>
        bool attach(T *type_ptr, void (T::*mem_ptr)(void), PinName pin) {
            _isr_data_fp.attach(callback(type_ptr, mem_ptr));
            interrupt_pin(pin);
            return enable_interupt(true);
        }

        /** Set whether the data ready interupt is enabled.\ \n
         *  Note: If this method fails, the state of the config register cannot be guaranteed.\ \n
         *  Check errors and ensure all config settings are as expected.
         *
         * @param enabled    True for enabled, false for disabled
         *
         * @return Write success
         */
        bool enable_interupt(bool enable);

        /** Get whether the data ready interupt is enabled.
         *
         *
         * @return 1 for enabled, 0 for disabled, -1 for error
         */
        int interupt_enabled();

        /** Set the nINT pin
         *
         * @param pin    Pin nINT is attached to
         *
         * @return Write success
         */
        bool interrupt_pin(PinName pin);

        /** Get the the nINT pin
         *
         * @return The nINT pin
         */
        PinName interrupt_pin();




    private:
        I2C* _i2c;

        bool _addr_dir;
        int _slave_addr;
        void update_slave_addr();

        AMS_ENS210 *_ens210;
        bool _ens210_enabled;
        int _ens210_poll_split;
        void update_ens210_timer();
        Ticker _ens210_poll_t;
        void ens210_isr();
        float temp_reading;
        float humid_reading;

        float fractions[9];
        void _init_fractions();
        void float_to_short(float in, char * output);

        OP_MODES _mode;

        void set_defaults();

        bool _errors[CCS811_LIB_ERR_NUM];
        int _error_count;
        char _error_strings[CCS811_TOTAL_ERR_NUM][255];
        void _init_errors();
        void clear_errors();
        void new_error(int error_id);

        DigitalOut *_n_wake_out;
        DigitalOut *_addr_out;

        char _alg_result_data[8];

        FunctionPointer _isr_data_fp;
        bool _int_data_enabled;
        InterruptIn *_int_data;
        void _isr_data();

        bool write_config();

        struct read_byte_result {
            bool success;
            uint8_t byte;
            read_byte_result() : success(false), byte(0) {}
        };
        read_byte_result read_config();
        read_byte_result read_status();

        bool boot_app_start();

        int i2c_read(char reg_addr, char* output, int len);
        int i2c_write(char reg_addr, char* input, int len);

};


#endif /* AMS_CCS811_H */