Adafruit_CCS811.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. #include "Adafruit_CCS811.h"
  2. /**************************************************************************/
  3. /*!
  4. @brief Setups the I2C interface and hardware and checks for communication.
  5. @param addr Optional I2C address the sensor can be found on. Default is 0x5A
  6. @returns True if device is set up, false on any failure
  7. */
  8. /**************************************************************************/
  9. sint8_t Adafruit_CCS811::begin(uint8_t addr)
  10. {
  11. _i2caddr = addr;
  12. _i2c_init();
  13. SWReset();
  14. delay(100);
  15. //check that the HW id is correct
  16. if(this->read8(CCS811_HW_ID) != CCS811_HW_ID_CODE) {
  17. return -1;
  18. }
  19. //try to start the app
  20. this->write(CCS811_BOOTLOADER_APP_START, NULL, 0);
  21. delay(100);
  22. //make sure there are no errors and we have entered application mode
  23. if(checkError()) {
  24. return -2;
  25. }
  26. if(!_status.FW_MODE) {
  27. return -3;
  28. }
  29. disableInterrupt();
  30. //default to read every second
  31. setDriveMode(CCS811_DRIVE_MODE_1SEC);
  32. return 0;
  33. }
  34. /**************************************************************************/
  35. /*!
  36. @brief sample rate of the sensor.
  37. @param mode one of CCS811_DRIVE_MODE_IDLE, CCS811_DRIVE_MODE_1SEC, CCS811_DRIVE_MODE_10SEC, CCS811_DRIVE_MODE_60SEC, CCS811_DRIVE_MODE_250MS.
  38. */
  39. void Adafruit_CCS811::setDriveMode(uint8_t mode)
  40. {
  41. _meas_mode.DRIVE_MODE = mode;
  42. this->write8(CCS811_MEAS_MODE, _meas_mode.get());
  43. }
  44. /**************************************************************************/
  45. /*!
  46. @brief enable the data ready interrupt pin on the device.
  47. */
  48. /**************************************************************************/
  49. void Adafruit_CCS811::enableInterrupt()
  50. {
  51. _meas_mode.INT_DATARDY = 1;
  52. this->write8(CCS811_MEAS_MODE, _meas_mode.get());
  53. }
  54. /**************************************************************************/
  55. /*!
  56. @brief disable the data ready interrupt pin on the device
  57. */
  58. /**************************************************************************/
  59. void Adafruit_CCS811::disableInterrupt()
  60. {
  61. _meas_mode.INT_DATARDY = 0;
  62. this->write8(CCS811_MEAS_MODE, _meas_mode.get());
  63. }
  64. /**************************************************************************/
  65. /*!
  66. @brief checks if data is available to be read.
  67. @returns True if data is ready, false otherwise.
  68. */
  69. /**************************************************************************/
  70. bool Adafruit_CCS811::available()
  71. {
  72. _status.set(read8(CCS811_STATUS));
  73. if(!_status.DATA_READY)
  74. return false;
  75. else return true;
  76. }
  77. /**************************************************************************/
  78. /*!
  79. @brief read and store the sensor data. This data can be accessed with getTVOC() and geteCO2()
  80. @returns 0 if no error, error code otherwise.
  81. */
  82. /**************************************************************************/
  83. uint8_t Adafruit_CCS811::readData()
  84. {
  85. if(!available())
  86. return false;
  87. else{
  88. uint8_t buf[8];
  89. this->read(CCS811_ALG_RESULT_DATA, buf, 8);
  90. _eCO2 = ((uint16_t)buf[0] << 8) | ((uint16_t)buf[1]);
  91. _TVOC = ((uint16_t)buf[2] << 8) | ((uint16_t)buf[3]);
  92. if(_status.ERROR)
  93. return buf[5];
  94. else return 0;
  95. }
  96. }
  97. /**************************************************************************/
  98. /*!
  99. @brief set the humidity and temperature compensation for the sensor.
  100. @param humidity the humidity data as a percentage. For 55% humidity, pass in integer 55.
  101. @param temperature the temperature in degrees C as a decimal number. For 25.5 degrees C, pass in 25.5
  102. */
  103. /**************************************************************************/
  104. void Adafruit_CCS811::setEnvironmentalData(uint8_t humidity, double temperature)
  105. {
  106. /* Humidity is stored as an unsigned 16 bits in 1/512%RH. The
  107. default value is 50% = 0x64, 0x00. As an example 48.5%
  108. humidity would be 0x61, 0x00.*/
  109. /* Temperature is stored as an unsigned 16 bits integer in 1/512
  110. degrees; there is an offset: 0 maps to -25°C. The default value is
  111. 25°C = 0x64, 0x00. As an example 23.5% temperature would be
  112. 0x61, 0x00.
  113. The internal algorithm uses these values (or default values if
  114. not set by the application) to compensate for changes in
  115. relative humidity and ambient temperature.*/
  116. uint8_t hum_perc = humidity << 1;
  117. float fractional = modf(temperature, &temperature);
  118. uint16_t temp_high = (((uint16_t)temperature + 25) << 9);
  119. uint16_t temp_low = ((uint16_t)(fractional / 0.001953125) & 0x1FF);
  120. uint16_t temp_conv = (temp_high | temp_low);
  121. uint8_t buf[] = {hum_perc, 0x00,
  122. (uint8_t)((temp_conv >> 8) & 0xFF), (uint8_t)(temp_conv & 0xFF)};
  123. this->write(CCS811_ENV_DATA, buf, 4);
  124. }
  125. /**************************************************************************/
  126. /*!
  127. @brief calculate the temperature using the onboard NTC resistor.
  128. @returns temperature as a double.
  129. */
  130. /**************************************************************************/
  131. double Adafruit_CCS811::calculateTemperature()
  132. {
  133. uint8_t buf[4];
  134. this->read(CCS811_NTC, buf, 4);
  135. uint32_t vref = ((uint32_t)buf[0] << 8) | buf[1];
  136. uint32_t vntc = ((uint32_t)buf[2] << 8) | buf[3];
  137. //from ams ccs811 app note
  138. uint32_t rntc = vntc * CCS811_REF_RESISTOR / vref;
  139. double ntc_temp;
  140. ntc_temp = log((double)rntc / CCS811_REF_RESISTOR); // 1
  141. ntc_temp /= 3380; // 2
  142. ntc_temp += 1.0 / (25 + 273.15); // 3
  143. ntc_temp = 1.0 / ntc_temp; // 4
  144. ntc_temp -= 273.15; // 5
  145. return ntc_temp - _tempOffset;
  146. }
  147. /**************************************************************************/
  148. /*!
  149. @brief set interrupt thresholds
  150. @param low_med the level below which an interrupt will be triggered.
  151. @param med_high the level above which the interrupt will ge triggered.
  152. @param hysteresis optional histeresis level. Defaults to 50
  153. */
  154. /**************************************************************************/
  155. void Adafruit_CCS811::setThresholds(uint16_t low_med, uint16_t med_high, uint8_t hysteresis)
  156. {
  157. uint8_t buf[] = {(uint8_t)((low_med >> 8) & 0xF), (uint8_t)(low_med & 0xF),
  158. (uint8_t)((med_high >> 8) & 0xF), (uint8_t)(med_high & 0xF), hysteresis};
  159. this->write(CCS811_THRESHOLDS, buf, 5);
  160. }
  161. /**************************************************************************/
  162. /*!
  163. @brief trigger a software reset of the device
  164. */
  165. /**************************************************************************/
  166. void Adafruit_CCS811::SWReset()
  167. {
  168. //reset sequence from the datasheet
  169. uint8_t seq[] = {0x11, 0xE5, 0x72, 0x8A};
  170. this->write(CCS811_SW_RESET, seq, 4);
  171. }
  172. /**************************************************************************/
  173. /*!
  174. @brief read the status register and store any errors.
  175. @returns the error bits from the status register of the device.
  176. */
  177. /**************************************************************************/
  178. bool Adafruit_CCS811::checkError()
  179. {
  180. _status.set(read8(CCS811_STATUS));
  181. return _status.ERROR;
  182. }
  183. /**************************************************************************/
  184. /*!
  185. @brief write one byte of data to the specified register
  186. @param reg the register to write to
  187. @param value the value to write
  188. */
  189. /**************************************************************************/
  190. void Adafruit_CCS811::write8(byte reg, byte value)
  191. {
  192. this->write(reg, &value, 1);
  193. }
  194. /**************************************************************************/
  195. /*!
  196. @brief read one byte of data from the specified register
  197. @param reg the register to read
  198. @returns one byte of register data
  199. */
  200. /**************************************************************************/
  201. uint8_t Adafruit_CCS811::read8(byte reg)
  202. {
  203. uint8_t ret;
  204. this->read(reg, &ret, 1);
  205. return ret;
  206. }
  207. void Adafruit_CCS811::_i2c_init()
  208. {
  209. Wire.begin();
  210. #ifdef ESP8266
  211. Wire.setClockStretchLimit(1000);
  212. #endif
  213. }
  214. void Adafruit_CCS811::read(uint8_t reg, uint8_t *buf, uint8_t num)
  215. {
  216. uint8_t value;
  217. uint8_t pos = 0;
  218. //on arduino we need to read in 32 byte chunks
  219. while(pos < num){
  220. uint8_t read_now = min((uint8_t)32, (uint8_t)(num - pos));
  221. Wire.beginTransmission((uint8_t)_i2caddr);
  222. Wire.write((uint8_t)reg + pos);
  223. Wire.endTransmission();
  224. Wire.requestFrom((uint8_t)_i2caddr, read_now);
  225. for(int i=0; i<read_now; i++){
  226. buf[pos] = Wire.read();
  227. pos++;
  228. }
  229. }
  230. }
  231. void Adafruit_CCS811::write(uint8_t reg, uint8_t *buf, uint8_t num)
  232. {
  233. Wire.beginTransmission((uint8_t)_i2caddr);
  234. Wire.write((uint8_t)reg);
  235. Wire.write((uint8_t *)buf, num);
  236. Wire.endTransmission();
  237. }