Library for the AMS CC811 digitial gas sensor

Dependencies:   AMS_ENS210_temp_humid_sensor

Revision:
5:41e97348e9e7
Parent:
4:a6b8881eae87
Child:
6:22c0a7f2ece2
--- a/AMS_CCS811.cpp	Thu Jan 19 14:27:44 2017 +0000
+++ b/AMS_CCS811.cpp	Fri Jan 20 14:34:41 2017 +0000
@@ -1,14 +1,27 @@
 
 #include "AMS_CCS811.h"
 
+const char *byte_to_binary(uint8_t in)
+{
+    static char b[9];
+    b[0] = '\0';
+
+    int z;
+    for (z = 128; z > 0; z >>= 1)
+    {
+        strcat(b, ((in & z) == z) ? "1" : "0");
+    }
+
+    return b;
+}
 
 AMS_CCS811::AMS_CCS811(I2C * i2c, PinName n_wake_pin) { 
-    _n_wake_out = new (std::nothrow) DigitalOut(n_wake_pin);
+    _n_wake_out = new (std::nothrow) DigitalOut(n_wake_pin, 1);
     _i2c = i2c; 
 }
             
 AMS_CCS811::AMS_CCS811(I2C * i2c, PinName n_wake_pin, I2C * ens210_i2c) {
-    _n_wake_out = new (std::nothrow) DigitalOut(n_wake_pin);
+    _n_wake_out = new (std::nothrow) DigitalOut(n_wake_pin, 1);
     _i2c = i2c;
     _ens210_i2c = ens210_i2c;
 }
@@ -20,9 +33,32 @@
 }
 
 bool AMS_CCS811::init() {
-   enable_ens210(true);
+   
+    USBserialComms.printf("Init()\r");
    
-   return set_defaults();
+    bool success = false;
+    
+    _init_errors();
+    set_defaults();
+    
+    if (_n_wake_out) {
+        USBserialComms.printf("_n_wake_out: %d\r", _n_wake_out != NULL);
+        USBserialComms.printf("_n_wake_out I/0: %d\r", _n_wake_out->read());
+    
+        int fw_mode = firmware_mode();
+       
+        if (fw_mode == 1) {  
+            USBserialComms.printf("App Mode\r");
+            enable_ens210(true);
+            success = write_config();
+    
+        } else if (fw_mode == 0) {  // is in boot mode, needs to be loaded into app mode
+            USBserialComms.printf("Boot Mode\r");
+            if (boot_app_start())   // if succesfully writes to app_start, retry init
+                init();
+        }
+    }
+    return success;
 }
 
 void AMS_CCS811::i2c_interface(I2C * i2c) {
@@ -55,6 +91,17 @@
     return _ens210_poll_split;
 }
 
+int AMS_CCS811::firmware_mode() {
+    int firmware_result = -1;
+    
+    read_byte_result read_result = read_status();
+    if (read_result.success) {
+        firmware_result = (read_result.byte >> 7) & 1;
+    } // todo ... add a new "last error" here
+    
+    return firmware_result;
+}
+
 bool AMS_CCS811::mode(OP_MODES mode) {
     OP_MODES old = _mode;               // incase the write fails, to roll back
     _mode = mode;
@@ -67,9 +114,9 @@
 }
 
 AMS_CCS811::OP_MODES AMS_CCS811::mode() {
-    OP_MODES result = _mode;                        // rather not rely on this, but just incase the read fails
+    OP_MODES result = INVALID;
     
-    read_config_result read_result = read_config();
+    read_byte_result read_result = read_config();
     if (read_result.success) {
         int mode = (read_result.byte >> 4) & 0b111;
         result = mode > 4 ? INVALID : (OP_MODES)mode;
@@ -80,7 +127,7 @@
 
 bool AMS_CCS811::addr_mode(bool high) { 
     _addr_dir = high;
-    if (_addr_out != NULL) *_addr_out = _addr_dir;
+    if (_addr_out != NULL) _addr_out->write(_addr_dir);
     
     update_slave_addr();
     
@@ -89,7 +136,7 @@
 
 bool AMS_CCS811::addr_mode() {
     if (_addr_out != NULL) {
-        _addr_dir = *_addr_out; 
+        _addr_dir = _addr_out->read(); 
     }
     
     return _addr_dir;
@@ -112,8 +159,15 @@
 }
 
 
-AMS_CCS811::DATA_STATUS AMS_CCS811::has_new_data() {
-    return (DATA_STATUS) 0;
+int AMS_CCS811::has_new_data() {
+    int result = -1;
+    
+    if (i2c_read(ALG_RESULT_DATA, _alg_result_data, 8) == 8) {
+        result = (_alg_result_data[4] >> 3) & 1;
+    }
+    USBserialComms.printf("has_new_data(addr: 0x%02X, result: %d, byte: %s(%d))\r", ALG_RESULT_DATA, result, byte_to_binary(_alg_result_data[4]), _alg_result_data[4]);
+    
+    return result;
 }
 
 uint16_t AMS_CCS811::co2_read() {
@@ -128,8 +182,49 @@
     return 0;
 }
 
-const char * AMS_CCS811::last_error() {
-    return "TODO";
+bool AMS_CCS811::error_status() {
+    bool result = false;
+    
+    read_byte_result read_result = read_status();
+    if (read_result.success) {
+        result = read_result.byte & 1;
+    }
+    
+    result = result || (_error_count > 0);
+    
+    return result;
+}
+      
+AMS_CCS811::ccs811_errors AMS_CCS811::errors() {
+    ccs811_errors error_result;
+    
+    char byte[1];
+    if (i2c_read(ERROR_ID, byte, 1) == 1) {
+        for(int i = 0; i < CCS811_ERR_NUM; i++) {
+            if ((byte[0] << i) & 1) {
+                error_result.codes[error_result.count++] = i;
+            }
+        }
+    }
+    for(int i = 0; i < CCS811_LIB_ERR_NUM; i++) {
+        if (_errors[i]) {
+            error_result.codes[error_result.count++] = i + CCS811_ERR_NUM;
+        }
+    }
+    
+    return error_result;
+    
+}
+
+const char * AMS_CCS811::error_string(int err_code){
+    static char result[255];
+    result[0] = 0;
+    if (err_code < CCS811_TOTAL_ERR_NUM && err_code > -1) 
+        strcpy(result, _error_strings[err_code]);
+    else 
+        sprintf(result, "Invalid Code: %d is out of range (0 - %d)", err_code, CCS811_TOTAL_ERR_NUM-1);
+    
+    return result;
 }
 
 bool AMS_CCS811::enable_interupt(bool enable) {
@@ -144,10 +239,10 @@
 
 }
 
-bool AMS_CCS811::interupt_enabled() {
-    bool enabled = _int_data_enabled;                        // rather not rely on this, but just incase the read fails
+int AMS_CCS811::interupt_enabled() {
+    int enabled = -1;
     
-    read_config_result read_result = read_config();
+    read_byte_result read_result = read_config();
     if (read_result.success) {
         enabled = (read_result.byte >> 3) & 1;
     } // todo ... add a new "last error" here? or maybe the read method itself should set that.
@@ -172,7 +267,7 @@
 
 /** Private **/
 
-bool AMS_CCS811::set_defaults() {
+void AMS_CCS811::set_defaults() {
     if (_mode == NULL) 
         _mode = CONFIG_OP_MODE;
     if (_addr_dir == NULL)
@@ -182,9 +277,45 @@
     if (_ens210_poll_split == NULL)
         _ens210_poll_split = CONFIG_ENS210_POLL;
         
-    update_slave_addr();
+    USBserialComms.printf("Defaults set: _mode(%d), _addr_dir(%d), _int_data_enabled(%d), _ens210_poll_split(%d)\r", _mode, _addr_dir, _int_data_enabled, _ens210_poll_split);
         
-    return write_config();
+    update_slave_addr();
+    
+    USBserialComms.printf("_slave_addr: 0x%02X\r", _slave_addr);
+}
+
+void AMS_CCS811::_init_errors() {
+    clear_errors();
+    /* Sensor errors */
+    strcpy(_error_strings[0], CCS811_WRITE_REG_INVALID);
+    strcpy(_error_strings[1], CCS811_READ_REG_INVALID);
+    strcpy(_error_strings[2], CCS811_MEASMODE_INVALID);
+    strcpy(_error_strings[3], CCS811_MAX_RESISTANCE);
+    strcpy(_error_strings[4], CCS811_HEATER_FAULT);
+    strcpy(_error_strings[5], CCS811_HEATER_SUPPLY);
+    strcpy(_error_strings[6], CCS811_RESERVED);
+    strcpy(_error_strings[7], CCS811_RESERVED);
+    /* Library errors */
+    strcpy(_error_strings[CCS811_LIB_N_WAKE_ID+CCS811_ERR_NUM], CCS811_LIB_N_WAKE);
+    strcpy(_error_strings[CCS811_LIB_I2C_ID+CCS811_ERR_NUM], CCS811_LIB_I2C);
+    strcpy(_error_strings[CCS811_LIB_SLAVE_W_ID+CCS811_ERR_NUM], CCS811_LIB_SLAVE_W);
+    strcpy(_error_strings[CCS811_LIB_REG_ADDR_ID+CCS811_ERR_NUM], CCS811_LIB_REG_ADDR);
+    strcpy(_error_strings[CCS811_LIB_I2CWRITE_ID+CCS811_ERR_NUM], CCS811_LIB_I2CWRITE);
+    strcpy(_error_strings[CCS811_LIB_SLAVE_R_ID+CCS811_ERR_NUM], CCS811_LIB_SLAVE_R);
+}
+
+void AMS_CCS811::clear_errors() {
+    _error_count = 0;
+    for (int i = 0; i < CCS811_LIB_ERR_NUM; i++) {
+        _errors[i] = false;
+    }
+}
+
+void AMS_CCS811::new_error(int error_id) {
+    if (!_errors[error_id]) {
+        _errors[error_id] = true;
+        _error_count++;
+    }
 }
 
 void AMS_CCS811::update_ens210_timer() {
@@ -197,7 +328,7 @@
 }
 
 void AMS_CCS811::update_slave_addr() {
-    slave_addr = addr_mode() ? SLAVE_ADDR_RAW_H : SLAVE_ADDR_RAW_L; 
+    _slave_addr = addr_mode() ? CCS811_SLAVE_ADDR_RAW_H : CCS811_SLAVE_ADDR_RAW_L; 
 }
         
 void AMS_CCS811::_isr_data() {
@@ -206,60 +337,93 @@
 
 bool AMS_CCS811::write_config() {
     char cmd[1] = {0 | (_int_data_enabled << 3) | (_mode << 4)};
-    return i2c_write(SYS_MODE, cmd, 1) == 1;
+    USBserialComms.printf("write_config(addr: 0x%02X, byte: %s(%d))\r", MEAS_MODE, byte_to_binary(cmd[0]), cmd[0]);
+    return i2c_write(MEAS_MODE, cmd, 1) == 1;
+}
+
+AMS_CCS811::read_byte_result AMS_CCS811::read_config() {
+    read_byte_result result;
+    char byte[1];
+    if (i2c_read(MEAS_MODE, byte, 1) == 1) {
+        result.success = true;
+        result.byte = byte[0];
+    }
+    USBserialComms.printf("read_config(addr: 0x%02X, success: %s, byte: %s(%d))\r", MEAS_MODE, result.success ? "true" : "false", byte_to_binary(result.byte), result.byte);
+    return result;
 }
 
-AMS_CCS811::read_config_result AMS_CCS811::read_config() {
-    read_config_result result;
+AMS_CCS811::read_byte_result AMS_CCS811::read_status() {
+    read_byte_result result;
     char byte[1];
-    if (i2c_read(SYS_MODE, byte, 1) == 1) {
+    if (i2c_read(STATUS, byte, 1) == 1) {
         result.success = true;
-        result.byte = byte[1];
+        result.byte = byte[0];
+    }
+    USBserialComms.printf("read_status(addr: 0x%02X, success: %s, byte: %s(%d))\r", STATUS, result.success ? "true" : "false", byte_to_binary(result.byte), result.byte);
+    return result;
+}
+
+bool AMS_CCS811::boot_app_start() {
+    bool success = false;
+    
+    if (i2c_write(APP_START, NULL, 0) == 0) {
+        wait_ms(70);
+        success = true;
     }
     
-    return result;
+    return success;
 }
 
 int AMS_CCS811::i2c_read(char reg_addr, char* output, int len) {
     
     int read_count = 0;
     if (_n_wake_out != NULL) {                                          // check nWAKE pin is set 
+        _n_wake_out->write(0);                                          // Hold low 
+        wait_us(CCS811_T_AWAKE);                                        // tAWAKE time to allow sensor I2C to wake up
         if (_i2c != NULL) {                                             // check I2C interface is set
             _i2c->start();                                              // send start condition for write
-            if(_i2c->write(SLAVE_ADDR_W) == 1) {                        // write slave address with write bit
+            if(_i2c->write(CCS811_SLAVE_ADDR_W) == 1) {                 // write slave address with write bit
                 if(_i2c->write(reg_addr) == 1) {                        // write register address
                     _i2c->start();                                      // send another start condition for read
-                    if(_i2c->write(SLAVE_ADDR_R) == 1) {                // write slave address with read bit
+                    if(_i2c->write(CCS811_SLAVE_ADDR_R) == 1) {         // write slave address with read bit
                         for (int i = 0; i < len; i++) {                 // read len bytes
                             output[i] = _i2c->read(i < len-1 ? 1 : 0);  // ack all reads aside from the final one (i == len-1)
                             read_count++;
                         }
-                    }
-                }
-            }
+                    } else new_error(CCS811_LIB_SLAVE_R_ID);
+                } else new_error(CCS811_LIB_REG_ADDR_ID);
+            } else new_error(CCS811_LIB_SLAVE_W_ID);
             _i2c->stop();                                               // send stop condition 
-        }
-    }
+        } else new_error(CCS811_LIB_I2C_ID);
+        _n_wake_out->write(1);                                          // Set back to high
+        wait_us(CCS811_T_DWAKE);                                        // tDWAKE time to allow sensor I2C to sleep
+    } else new_error(CCS811_LIB_N_WAKE_ID);
     
     return read_count;
 }
 
-int AMS_CCS811::i2c_write(char reg_addr, char* input, int len) {        // to do... error reporting
+int AMS_CCS811::i2c_write(char reg_addr, char* input, int len) {
     
-    int write_count = 0;
+    int write_count = -1;
     if (_n_wake_out != NULL) {                                          // check nWAKE pin is set
+        _n_wake_out->write(0);                                          // Hold low
+        wait_us(CCS811_T_AWAKE);                                        // tAWAKE time to allow sensor I2C to wake up
         if (_i2c != NULL) {                                             // check I2C interface is set
             _i2c->start();                                              // send start condition for write
-            if(_i2c->write(SLAVE_ADDR_W) == 1) {                        // write slave address
+            if(_i2c->write(CCS811_SLAVE_ADDR_W) == 1) {                 // write slave address
                 if(_i2c->write(reg_addr) == 1) {                        // write register address
+                    write_count = 0;
                     for (int i = 0; i < len; i++) {                     // write len bytes
                         if(_i2c->write(input[i]) == 1) write_count++;   // write each byte, if successful increment count
+                        else new_error(CCS811_LIB_I2CWRITE_ID);
                     }
-                }
-            }
+                } else new_error(CCS811_LIB_REG_ADDR_ID);
+            } else new_error(CCS811_LIB_SLAVE_W_ID);
             _i2c->stop();                                               // send stop condition 
-        }
-    }
+        } else new_error(CCS811_LIB_I2C_ID);
+        _n_wake_out->write(1);                                          // set back to high
+        wait_us(CCS811_T_DWAKE);                                        // tDWAKE time to allow sensor I2C to sleep
+    }else new_error(CCS811_LIB_N_WAKE_ID);
     
     return write_count;
 }
\ No newline at end of file