Adafruit_SGP30.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. //@file Adafruit_SGP30.cpp
  2. #include "Arduino.h"
  3. #include "Adafruit_SGP30.h"
  4. //#define I2C_DEBUG
  5. /*!
  6. * @brief Instantiates a new SGP30 class
  7. */
  8. Adafruit_SGP30::Adafruit_SGP30() {}
  9. /*!
  10. * @brief Setups the hardware and detects a valid SGP30. Initializes I2C
  11. * then reads the serialnumber and checks that we are talking to an
  12. * SGP30
  13. * @param theWire
  14. * Optional pointer to I2C interface, otherwise use Wire
  15. * @param initSensor
  16. * Optional pointer to prevent IAQinit to be called. Used for Deep
  17. * Sleep.
  18. * @return True if SGP30 found on I2C, False if something went wrong!
  19. */
  20. boolean Adafruit_SGP30::begin(TwoWire *theWire, boolean initSensor) {
  21. _i2caddr = SGP30_I2CADDR_DEFAULT;
  22. _i2c = theWire;
  23. _i2c->begin();
  24. uint8_t command[2];
  25. command[0] = 0x36;
  26. command[1] = 0x82;
  27. if (!readWordFromCommand(command, 2, 10, serialnumber, 3))
  28. return false;
  29. uint16_t featureset;
  30. command[0] = 0x20;
  31. command[1] = 0x2F;
  32. if (!readWordFromCommand(command, 2, 10, &featureset, 1))
  33. return false;
  34. // Serial.print("Featureset 0x"); Serial.println(featureset, HEX);
  35. if ((featureset & 0xF0) != SGP30_FEATURESET)
  36. return false;
  37. if (initSensor) {
  38. if (!IAQinit())
  39. return false;
  40. }
  41. return true;
  42. }
  43. /*!
  44. * @brief Commands the sensor to perform a soft reset using the "General
  45. * Call" mode. Take note that this is not sensor specific and all devices that
  46. * support the General Call mode on the on the same I2C bus will perform this.
  47. *
  48. * @return True if command completed successfully, false if something went
  49. * wrong!
  50. */
  51. boolean Adafruit_SGP30::softReset(void) {
  52. uint8_t command[2];
  53. command[0] = 0x00;
  54. command[1] = 0x06;
  55. return readWordFromCommand(command, 2, 10);
  56. }
  57. /*!
  58. * @brief Commands the sensor to begin the IAQ algorithm. Must be called
  59. * after startup.
  60. * @returns True if command completed successfully, false if something went
  61. * wrong!
  62. */
  63. boolean Adafruit_SGP30::IAQinit(void) {
  64. uint8_t command[2];
  65. command[0] = 0x20;
  66. command[1] = 0x03;
  67. return readWordFromCommand(command, 2, 10);
  68. }
  69. /*!
  70. * @brief Commands the sensor to take a single eCO2/VOC measurement. Places
  71. * results in {@link TVOC} and {@link eCO2}
  72. * @return True if command completed successfully, false if something went
  73. * wrong!
  74. */
  75. boolean Adafruit_SGP30::IAQmeasure(void) {
  76. uint8_t command[2];
  77. command[0] = 0x20;
  78. command[1] = 0x08;
  79. uint16_t reply[2];
  80. if (!readWordFromCommand(command, 2, 12, reply, 2))
  81. return false;
  82. TVOC = reply[1];
  83. eCO2 = reply[0];
  84. return true;
  85. }
  86. /*!
  87. * @brief Commands the sensor to take a single H2/ethanol raw measurement.
  88. * Places results in {@link rawH2} and {@link rawEthanol}
  89. * @returns True if command completed successfully, false if something went
  90. * wrong!
  91. */
  92. boolean Adafruit_SGP30::IAQmeasureRaw(void) {
  93. uint8_t command[2];
  94. command[0] = 0x20;
  95. command[1] = 0x50;
  96. uint16_t reply[2];
  97. if (!readWordFromCommand(command, 2, 25, reply, 2))
  98. return false;
  99. rawEthanol = reply[1];
  100. rawH2 = reply[0];
  101. return true;
  102. }
  103. /*!
  104. * @brief Request baseline calibration values for both CO2 and TVOC IAQ
  105. * calculations. Places results in parameter memory locaitons.
  106. * @param eco2_base
  107. * A pointer to a uint16_t which we will save the calibration
  108. * value to
  109. * @param tvoc_base
  110. * A pointer to a uint16_t which we will save the calibration value to
  111. * @return True if command completed successfully, false if something went
  112. * wrong!
  113. */
  114. boolean Adafruit_SGP30::getIAQBaseline(uint16_t *eco2_base,
  115. uint16_t *tvoc_base) {
  116. uint8_t command[2];
  117. command[0] = 0x20;
  118. command[1] = 0x15;
  119. uint16_t reply[2];
  120. if (!readWordFromCommand(command, 2, 10, reply, 2))
  121. return false;
  122. *eco2_base = reply[0];
  123. *tvoc_base = reply[1];
  124. return true;
  125. }
  126. /*!
  127. * @brief Assign baseline calibration values for both CO2 and TVOC IAQ
  128. * calculations.
  129. * @param eco2_base
  130. * A uint16_t which we will save the calibration value from
  131. * @param tvoc_base
  132. * A uint16_t which we will save the calibration value from
  133. * @return True if command completed successfully, false if something went
  134. * wrong!
  135. */
  136. boolean Adafruit_SGP30::setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base) {
  137. uint8_t command[8];
  138. command[0] = 0x20;
  139. command[1] = 0x1e;
  140. command[2] = tvoc_base >> 8;
  141. command[3] = tvoc_base & 0xFF;
  142. command[4] = generateCRC(command + 2, 2);
  143. command[5] = eco2_base >> 8;
  144. command[6] = eco2_base & 0xFF;
  145. command[7] = generateCRC(command + 5, 2);
  146. return readWordFromCommand(command, 8, 10);
  147. }
  148. /*!
  149. * @brief Set the absolute humidity value [mg/m^3] for compensation to
  150. * increase precision of TVOC and eCO2.
  151. * @param absolute_humidity
  152. * A uint32_t [mg/m^3] which we will be used for compensation.
  153. * If the absolute humidity is set to zero, humidity compensation
  154. * will be disabled.
  155. * @return True if command completed successfully, false if something went
  156. * wrong!
  157. */
  158. boolean Adafruit_SGP30::setHumidity(uint32_t absolute_humidity) {
  159. if (absolute_humidity > 256000) {
  160. return false;
  161. }
  162. uint16_t ah_scaled =
  163. (uint16_t)(((uint64_t)absolute_humidity * 256 * 16777) >> 24);
  164. uint8_t command[5];
  165. command[0] = 0x20;
  166. command[1] = 0x61;
  167. command[2] = ah_scaled >> 8;
  168. command[3] = ah_scaled & 0xFF;
  169. command[4] = generateCRC(command + 2, 2);
  170. return readWordFromCommand(command, 5, 10);
  171. }
  172. /*!
  173. * @brief I2C low level interfacing
  174. */
  175. boolean Adafruit_SGP30::readWordFromCommand(uint8_t command[],
  176. uint8_t commandLength,
  177. uint16_t delayms,
  178. uint16_t *readdata,
  179. uint8_t readlen) {
  180. _i2c->beginTransmission(_i2caddr);
  181. #ifdef I2C_DEBUG
  182. Serial.print("\t\t-> ");
  183. #endif
  184. for (uint8_t i = 0; i < commandLength; i++) {
  185. _i2c->write(command[i]);
  186. #ifdef I2C_DEBUG
  187. Serial.print("0x");
  188. Serial.print(command[i], HEX);
  189. Serial.print(", ");
  190. #endif
  191. }
  192. #ifdef I2C_DEBUG
  193. Serial.println();
  194. #endif
  195. _i2c->endTransmission();
  196. delay(delayms);
  197. if (readlen == 0)
  198. return true;
  199. uint8_t replylen = readlen * (SGP30_WORD_LEN + 1);
  200. if (_i2c->requestFrom(_i2caddr, replylen) != replylen)
  201. return false;
  202. uint8_t replybuffer[replylen];
  203. #ifdef I2C_DEBUG
  204. Serial.print("\t\t<- ");
  205. #endif
  206. for (uint8_t i = 0; i < replylen; i++) {
  207. replybuffer[i] = _i2c->read();
  208. #ifdef I2C_DEBUG
  209. Serial.print("0x");
  210. Serial.print(replybuffer[i], HEX);
  211. Serial.print(", ");
  212. #endif
  213. }
  214. #ifdef I2C_DEBUG
  215. Serial.println();
  216. #endif
  217. for (uint8_t i = 0; i < readlen; i++) {
  218. uint8_t crc = generateCRC(replybuffer + i * 3, 2);
  219. #ifdef I2C_DEBUG
  220. Serial.print("\t\tCRC calced: 0x");
  221. Serial.print(crc, HEX);
  222. Serial.print(" vs. 0x");
  223. Serial.println(replybuffer[i * 3 + 2], HEX);
  224. #endif
  225. if (crc != replybuffer[i * 3 + 2])
  226. return false;
  227. // success! store it
  228. readdata[i] = replybuffer[i * 3];
  229. readdata[i] <<= 8;
  230. readdata[i] |= replybuffer[i * 3 + 1];
  231. #ifdef I2C_DEBUG
  232. Serial.print("\t\tRead: 0x");
  233. Serial.println(readdata[i], HEX);
  234. #endif
  235. }
  236. return true;
  237. }
  238. uint8_t Adafruit_SGP30::generateCRC(uint8_t *data, uint8_t datalen) {
  239. // calculates 8-Bit checksum with given polynomial
  240. uint8_t crc = SGP30_CRC8_INIT;
  241. for (uint8_t i = 0; i < datalen; i++) {
  242. crc ^= data[i];
  243. for (uint8_t b = 0; b < 8; b++) {
  244. if (crc & 0x80)
  245. crc = (crc << 1) ^ SGP30_CRC8_POLYNOMIAL;
  246. else
  247. crc <<= 1;
  248. }
  249. }
  250. return crc;
  251. }