Driver for Sensirion SCD30, CO2 sensor module using I2C. The device also senses Temperature and Humidity.

Dependents:   scd30_HelloWorld scd30_HelloWorld

scd30.cpp

Committer:
loopsva
Date:
2018-09-13
Revision:
1:24bb922e179c
Parent:
0:2a16832400d0

File content as of revision 1:24bb922e179c:

#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()
{
    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];
    scdSTR.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()
{
    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];
    scdSTR.co2m = stat;
    uint8_t dat = scd30::checkCrc2b(stat, i2cBuff[2]);
    if(dat == SCDcrcERROR) return SCDcrcERRORv1;
    
    stat = (i2cBuff[3] << 8) | i2cBuff[4];
    scdSTR.co2l = stat;
    dat = scd30::checkCrc2b(stat, i2cBuff[5]);
    if(dat == SCDcrcERROR) return SCDcrcERRORv2;
    
    stat = (i2cBuff[6] << 8) | i2cBuff[7];
    scdSTR.tempm = stat;
    dat = scd30::checkCrc2b(stat, i2cBuff[8]);
    if(dat == SCDcrcERROR) return SCDcrcERRORv3;
    
    stat = (i2cBuff[9] << 8) | i2cBuff[10];
    scdSTR.templ = stat;
    dat = scd30::checkCrc2b(stat, i2cBuff[11]);
    if(dat == SCDcrcERROR) return SCDcrcERRORv4;
    
    stat = (i2cBuff[12] << 8) | i2cBuff[13];
    scdSTR.humm = stat;
    dat = scd30::checkCrc2b(stat, i2cBuff[14]);
    if(dat == SCDcrcERROR) return SCDcrcERRORv5;
    
    stat = (i2cBuff[15] << 8) | i2cBuff[16];
    scdSTR.huml = stat;
    dat = scd30::checkCrc2b(stat, i2cBuff[17]);
    if(dat == SCDcrcERROR) return SCDcrcERRORv6;
    
    scdSTR.co2i = (scdSTR.co2m << 16) | scdSTR.co2l ;
    scdSTR.tempi = (scdSTR.tempm << 16) | scdSTR.templ ;
    scdSTR.humi = (scdSTR.humm << 16) | scdSTR.huml ;
    
    scdSTR.co2f = *(float*)&scdSTR.co2i;
    scdSTR.tempf = *(float*)&scdSTR.tempi;
    scdSTR.humf = *(float*)&scdSTR.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()
{
    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];
    scdSTR.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()
{
    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(scdSTR.sn); i++) scdSTR.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];
        scdSTR.sn[i - t] = stat >> 8;
        scdSTR.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;
}