Driver for Sensirion SCD30, CO2 sensor module using I2C. The device also senses Temperature and Humidity.
Dependents: scd30_HelloWorld scd30_HelloWorld
Diff: scd30.cpp
- Revision:
- 0:2a16832400d0
- Child:
- 1:24bb922e179c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scd30.cpp Mon Sep 10 17:51:13 2018 +0000 @@ -0,0 +1,275 @@ +#include "mbed.h" +#include "scd30.h" + +//----------------------------------------------------------------------------- +// Constructor + +scd30::scd30(PinName sda, PinName scl, int i2cFrequency) : _i2c(sda, scl) { + _i2c.frequency(i2cFrequency); +} + +//----------------------------------------------------------------------------- +// Destructor + +scd30::~scd30() { +} + +//----------------------------------------------------------------------------- +// start auto-measurement with barometer reading (in mB) +// + +uint8_t scd30::startMeasurement(uint16_t baro) +{ + i2cBuff[0] = SCD30_CMMD_STRT_CONT_MEAS >> 8; + i2cBuff[1] = SCD30_CMMD_STRT_CONT_MEAS & 255; + i2cBuff[2] = baro >> 8; + i2cBuff[3] = baro & 255; + i2cBuff[4] = scd30::calcCrc2b(baro); + int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 5, false); + if(res) return SCDnoAckERROR; + return SCDnoERROR; +} + +//----------------------------------------------------------------------------- +// Stop auto-measurement + +uint8_t scd30::stopMeasurement() +{ + i2cBuff[0] = SCD30_CMMD_STOP_CONT_MEAS >> 8; + i2cBuff[1] = SCD30_CMMD_STOP_CONT_MEAS & 255; + int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 2, false); + if(res) return SCDnoAckERROR; + return SCDnoERROR; +} + +//----------------------------------------------------------------------------- +// Change the measurement interval (in Secs), + +uint8_t scd30::setMeasInterval(uint16_t mi) +{ + i2cBuff[0] = SCD30_CMMD_SET_MEAS_INTVL >> 8; + i2cBuff[1] = SCD30_CMMD_SET_MEAS_INTVL & 255; + i2cBuff[2] = mi >> 8; + i2cBuff[3] = mi & 255; + i2cBuff[4] = scd30::calcCrc2b(mi); + int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 5, false); + if(res) return SCDnoAckERROR; + return SCDnoERROR; +} + +//----------------------------------------------------------------------------- +// Get ready status value + +uint8_t scd30::getReadyStatus(SCD30_str& sSTR) +{ + i2cBuff[0] = SCD30_CMMD_GET_READY_STAT >> 8; + i2cBuff[1] = SCD30_CMMD_GET_READY_STAT & 255; + int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 2, false); + if(res) return SCDnoAckERROR; + + _i2c.read(SCD30_I2C_ADDR | 1, i2cBuff, 3, false); + uint16_t stat = (i2cBuff[0] << 8) | i2cBuff[1]; + sSTR.ready = stat; + uint8_t dat = scd30::checkCrc2b(stat, i2cBuff[2]); + + if(dat == SCDcrcERROR) return SCDcrcERRORv1; + if(dat == SCDisReady) return SCDisReady; + return SCDnoERROR; +} + +//----------------------------------------------------------------------------- +// Get all the measurement values, stick them into the array + +uint8_t scd30::readMeasurement(SCD30_str& sSTR) +{ + i2cBuff[0] = SCD30_CMMD_READ_MEAS >> 8; + i2cBuff[1] = SCD30_CMMD_READ_MEAS & 255; + int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 2, false); + if(res) return SCDnoAckERROR; + + _i2c.read(SCD30_I2C_ADDR | 1, i2cBuff, 18, false); + + uint16_t stat = (i2cBuff[0] << 8) | i2cBuff[1]; + sSTR.co2m = stat; + uint8_t dat = scd30::checkCrc2b(stat, i2cBuff[2]); + if(dat == SCDcrcERROR) return SCDcrcERRORv1; + + stat = (i2cBuff[3] << 8) | i2cBuff[4]; + sSTR.co2l = stat; + dat = scd30::checkCrc2b(stat, i2cBuff[5]); + if(dat == SCDcrcERROR) return SCDcrcERRORv2; + + stat = (i2cBuff[6] << 8) | i2cBuff[7]; + sSTR.tempm = stat; + dat = scd30::checkCrc2b(stat, i2cBuff[8]); + if(dat == SCDcrcERROR) return SCDcrcERRORv3; + + stat = (i2cBuff[9] << 8) | i2cBuff[10]; + sSTR.templ = stat; + dat = scd30::checkCrc2b(stat, i2cBuff[11]); + if(dat == SCDcrcERROR) return SCDcrcERRORv4; + + stat = (i2cBuff[12] << 8) | i2cBuff[13]; + sSTR.humm = stat; + dat = scd30::checkCrc2b(stat, i2cBuff[14]); + if(dat == SCDcrcERROR) return SCDcrcERRORv5; + + stat = (i2cBuff[15] << 8) | i2cBuff[16]; + sSTR.huml = stat; + dat = scd30::checkCrc2b(stat, i2cBuff[17]); + if(dat == SCDcrcERROR) return SCDcrcERRORv6; + + sSTR.co2i = (sSTR.co2m << 16) | sSTR.co2l ; + sSTR.tempi = (sSTR.tempm << 16) | sSTR.templ ; + sSTR.humi = (sSTR.humm << 16) | sSTR.huml ; + + sSTR.co2f = *(float*)&sSTR.co2i; + sSTR.tempf = *(float*)&sSTR.tempi; + sSTR.humf = *(float*)&sSTR.humi; + + return SCDnoERROR; +} + +//----------------------------------------------------------------------------- +// Change the ambient temperature (in 0.01C), +// i.e. 0x1F4 = 500 = 5.00C, CRC = 0x33 + +uint8_t scd30::setTemperatureOffs(uint16_t temp) +{ + i2cBuff[0] = SCD30_CMMD_SET_TEMP_OFFS >> 8; + i2cBuff[1] = SCD30_CMMD_SET_TEMP_OFFS & 255; + i2cBuff[2] = temp >> 8; + i2cBuff[3] = temp & 255; + i2cBuff[4] = scd30::calcCrc2b(temp); + int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 5, false); + if(res) return SCDnoAckERROR; + return SCDnoERROR; +} + +//----------------------------------------------------------------------------- +// Change the altitude reading (in meters above seal level) + +uint8_t scd30::setAltitudeComp(uint16_t alt) +{ + i2cBuff[0] = SCD30_CMMD_SET_ALT_COMP >> 8; + i2cBuff[1] = SCD30_CMMD_SET_ALT_COMP & 255; + i2cBuff[2] = alt >> 8; + i2cBuff[3] = alt & 255; + i2cBuff[4] = scd30::calcCrc2b(alt); + int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 5, false); + if(res) return SCDnoAckERROR; + return SCDnoERROR; +} + +//----------------------------------------------------------------------------- +// Perform a soft reset on the SCD30 + +uint8_t scd30::softReset() +{ + i2cBuff[0] = SCD30_CMMD_SOFT_RESET >> 8; + i2cBuff[1] = SCD30_CMMD_SOFT_RESET & 255; + int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 2, false); + if(res) return SCDnoAckERROR; + return SCDnoERROR; +} + +//----------------------------------------------------------------------------- +// Calculate the CRC of a 2 byte value using the SCD30 CRC polynomial + +uint8_t scd30::calcCrc2b(uint16_t seed) +{ + uint8_t bit; // bit mask + uint8_t crc = SCD30_CRC_INIT; // calculated checksum + + // calculates 8-Bit checksum with given polynomial + + crc ^= (seed >> 8) & 255; + for(bit = 8; bit > 0; --bit) + { + if(crc & 0x80) crc = (crc << 1) ^ SCD30_POLYNOMIAL; + else crc = (crc << 1); + } + + crc ^= seed & 255; + for(bit = 8; bit > 0; --bit) + { + if(crc & 0x80) crc = (crc << 1) ^ SCD30_POLYNOMIAL; + else crc = (crc << 1); + } + + return crc; +} + +//----------------------------------------------------------------------------- +// Compare the CRC values + +uint8_t scd30::checkCrc2b(uint16_t seed, uint8_t crcIn) +{ + uint8_t crcCalc = scd30::calcCrc2b(seed); + if(crcCalc != crcIn) return SCDcrcERROR; + return SCDnoERROR; +} + +//----------------------------------------------------------------------------- +// start single-measurement with barometer reading (in mB) +// + +uint8_t scd30::startOneMeasurement(uint16_t baro) +{ + i2cBuff[0] = SCD30_CMMD_START_SINGLE_MEAS >> 8; + i2cBuff[1] = SCD30_CMMD_START_SINGLE_MEAS & 255; + i2cBuff[2] = baro >> 8; + i2cBuff[3] = baro & 255; + i2cBuff[4] = scd30::calcCrc2b(baro); + int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 5, false); + if(res) return SCDnoAckERROR; + return SCDnoERROR; +} + +//----------------------------------------------------------------------------- +// Get article code + +uint8_t scd30::getArticleCode(SCD30_str& sSTR) +{ + i2cBuff[0] = SCD30_CMMD_READ_ARTICLECODE >> 8; + i2cBuff[1] = SCD30_CMMD_READ_ARTICLECODE & 255; + int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 2, false); + if(res) return SCDnoAckERROR; + + _i2c.read(SCD30_I2C_ADDR | 1, i2cBuff, 3, false); + uint16_t stat = (i2cBuff[0] << 8) | i2cBuff[1]; + sSTR.acode = stat; + uint8_t dat = scd30::checkCrc2b(stat, i2cBuff[2]); + + if(dat == SCDcrcERROR) return SCDcrcERRORv1; + return SCDnoERROR; +} + +//----------------------------------------------------------------------------- +// Get scd30 serial number + +uint8_t scd30::getSerialNumber(SCD30_str& sSTR) +{ + i2cBuff[0] = SCD30_CMMD_READ_SERIALNBR >> 8; + i2cBuff[1] = SCD30_CMMD_READ_SERIALNBR & 255; + int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 2, false); + if(res) return SCDnoAckERROR; + + int i = 0; + for(i = 0; i < sizeof(sSTR.sn); i++) sSTR.sn[i] = 0; + for(i = 0; i < sizeof(i2cBuff); i++) i2cBuff[i] = 0; + + _i2c.read(SCD30_I2C_ADDR | 1, i2cBuff, SCD30_SN_SIZE, false); + int t = 0; + for(i = 0; i < SCD30_SN_SIZE; i +=3) { + uint16_t stat = (i2cBuff[i] << 8) | i2cBuff[i + 1]; + sSTR.sn[i - t] = stat >> 8; + sSTR.sn[i - t + 1] = stat & 255; + uint8_t dat = scd30::checkCrc2b(stat, i2cBuff[i + 2]); + t++; + if(dat == SCDcrcERROR) return SCDcrcERRORv1; + if(stat == 0) break; + } + + return SCDnoERROR; +}