#pragma once /** * air quality sensor * https://sensirion.com/media/documents/984E0DD5/61644B8B/Sensirion_Gas_Sensors_Datasheet_SGP30.pdf */ template class SGP30 { private: I2C& i2c; static constexpr const uint8_t ADDR = 0x58; static constexpr const char* NAME = "SGP30"; static constexpr const uint16_t CMD_IAQ_INIT = 0x2003; static constexpr const uint16_t CMD_IAQ_MEASURE = 0x2008; static constexpr const uint16_t CMD_GET_SERIAL = 0x3682; public: SGP30(I2C& i2c) : i2c(i2c) { } void init() { sendCommand(CMD_IAQ_INIT); } /** is the device present on the bus? */ bool isPresent() { return i2c.query(ADDR); } struct Result { uint16_t co2e; uint16_t tvoc; }; /** should be called ~ every second */ Result measure() { sendCommand(CMD_IAQ_MEASURE); vTaskDelay(100 / portTICK_PERIOD_MS); uint8_t tmp[6]; i2c.readRaw(ADDR, 6, tmp); Result res; res.co2e = (tmp[0]<<8) | (tmp[1]<<0); res.tvoc = (tmp[3]<<8) | (tmp[4]<<0); return res; } void getSerial() { struct Serial { uint8_t v1[2]; uint8_t crc1; uint8_t v2[2]; uint8_t crc2; uint8_t v3[2]; uint8_t crc3; } serial; sendCommand(CMD_GET_SERIAL); uint8_t tmp[9]; i2c.readRaw(ADDR, 9, &serial); /* uint8_t a = calcCRC(tmp+0, 2); uint8_t b = calcCRC(tmp+3, 2); uint8_t c = calcCRC(tmp+6, 2); printf("%d %d %d %d\n", tmp[0], tmp[1], tmp[2], a); printf("%d %d %d %d\n", tmp[3], tmp[4], tmp[5], b); printf("%d %d %d %d\n", tmp[6], tmp[7], tmp[8], c); */ } private: void sendCommand(uint16_t cmd) { uint8_t tmp[2]; tmp[0] = cmd >> 8; tmp[1] = cmd >> 0; i2c.writeRaw(ADDR, 2, tmp); } static uint8_t calcCRC(const uint8_t* data, uint8_t len) { uint8_t crc = 0xff; for (uint8_t i = 0; i < len; i++) { crc ^= data[i]; for (uint8_t j = 0; j < 8; j++) { if ((crc & 0x80) != 0) { crc = (uint8_t)((crc << 1) ^ 0x31); } else { crc <<= 1; } } } return crc; } };