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

Dependents:   scd30_HelloWorld scd30_HelloWorld

Committer:
loopsva
Date:
Thu Sep 13 17:38:26 2018 +0000
Revision:
1:24bb922e179c
Parent:
0:2a16832400d0
Changed the way the data structure is handled

Who changed what in which revision?

UserRevisionLine numberNew contents of line
loopsva 0:2a16832400d0 1 #include "mbed.h"
loopsva 0:2a16832400d0 2 #include "scd30.h"
loopsva 0:2a16832400d0 3
loopsva 0:2a16832400d0 4 //-----------------------------------------------------------------------------
loopsva 0:2a16832400d0 5 // Constructor
loopsva 0:2a16832400d0 6
loopsva 0:2a16832400d0 7 scd30::scd30(PinName sda, PinName scl, int i2cFrequency) : _i2c(sda, scl) {
loopsva 0:2a16832400d0 8 _i2c.frequency(i2cFrequency);
loopsva 0:2a16832400d0 9 }
loopsva 0:2a16832400d0 10
loopsva 0:2a16832400d0 11 //-----------------------------------------------------------------------------
loopsva 0:2a16832400d0 12 // Destructor
loopsva 0:2a16832400d0 13
loopsva 0:2a16832400d0 14 scd30::~scd30() {
loopsva 0:2a16832400d0 15 }
loopsva 0:2a16832400d0 16
loopsva 0:2a16832400d0 17 //-----------------------------------------------------------------------------
loopsva 0:2a16832400d0 18 // start auto-measurement with barometer reading (in mB)
loopsva 0:2a16832400d0 19 //
loopsva 0:2a16832400d0 20
loopsva 0:2a16832400d0 21 uint8_t scd30::startMeasurement(uint16_t baro)
loopsva 0:2a16832400d0 22 {
loopsva 0:2a16832400d0 23 i2cBuff[0] = SCD30_CMMD_STRT_CONT_MEAS >> 8;
loopsva 0:2a16832400d0 24 i2cBuff[1] = SCD30_CMMD_STRT_CONT_MEAS & 255;
loopsva 0:2a16832400d0 25 i2cBuff[2] = baro >> 8;
loopsva 0:2a16832400d0 26 i2cBuff[3] = baro & 255;
loopsva 0:2a16832400d0 27 i2cBuff[4] = scd30::calcCrc2b(baro);
loopsva 0:2a16832400d0 28 int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 5, false);
loopsva 0:2a16832400d0 29 if(res) return SCDnoAckERROR;
loopsva 0:2a16832400d0 30 return SCDnoERROR;
loopsva 0:2a16832400d0 31 }
loopsva 0:2a16832400d0 32
loopsva 0:2a16832400d0 33 //-----------------------------------------------------------------------------
loopsva 0:2a16832400d0 34 // Stop auto-measurement
loopsva 0:2a16832400d0 35
loopsva 0:2a16832400d0 36 uint8_t scd30::stopMeasurement()
loopsva 0:2a16832400d0 37 {
loopsva 0:2a16832400d0 38 i2cBuff[0] = SCD30_CMMD_STOP_CONT_MEAS >> 8;
loopsva 0:2a16832400d0 39 i2cBuff[1] = SCD30_CMMD_STOP_CONT_MEAS & 255;
loopsva 0:2a16832400d0 40 int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 2, false);
loopsva 0:2a16832400d0 41 if(res) return SCDnoAckERROR;
loopsva 0:2a16832400d0 42 return SCDnoERROR;
loopsva 0:2a16832400d0 43 }
loopsva 0:2a16832400d0 44
loopsva 0:2a16832400d0 45 //-----------------------------------------------------------------------------
loopsva 0:2a16832400d0 46 // Change the measurement interval (in Secs),
loopsva 0:2a16832400d0 47
loopsva 0:2a16832400d0 48 uint8_t scd30::setMeasInterval(uint16_t mi)
loopsva 0:2a16832400d0 49 {
loopsva 0:2a16832400d0 50 i2cBuff[0] = SCD30_CMMD_SET_MEAS_INTVL >> 8;
loopsva 0:2a16832400d0 51 i2cBuff[1] = SCD30_CMMD_SET_MEAS_INTVL & 255;
loopsva 0:2a16832400d0 52 i2cBuff[2] = mi >> 8;
loopsva 0:2a16832400d0 53 i2cBuff[3] = mi & 255;
loopsva 0:2a16832400d0 54 i2cBuff[4] = scd30::calcCrc2b(mi);
loopsva 0:2a16832400d0 55 int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 5, false);
loopsva 0:2a16832400d0 56 if(res) return SCDnoAckERROR;
loopsva 0:2a16832400d0 57 return SCDnoERROR;
loopsva 0:2a16832400d0 58 }
loopsva 0:2a16832400d0 59
loopsva 0:2a16832400d0 60 //-----------------------------------------------------------------------------
loopsva 0:2a16832400d0 61 // Get ready status value
loopsva 0:2a16832400d0 62
loopsva 1:24bb922e179c 63 uint8_t scd30::getReadyStatus()
loopsva 0:2a16832400d0 64 {
loopsva 0:2a16832400d0 65 i2cBuff[0] = SCD30_CMMD_GET_READY_STAT >> 8;
loopsva 0:2a16832400d0 66 i2cBuff[1] = SCD30_CMMD_GET_READY_STAT & 255;
loopsva 0:2a16832400d0 67 int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 2, false);
loopsva 0:2a16832400d0 68 if(res) return SCDnoAckERROR;
loopsva 0:2a16832400d0 69
loopsva 0:2a16832400d0 70 _i2c.read(SCD30_I2C_ADDR | 1, i2cBuff, 3, false);
loopsva 0:2a16832400d0 71 uint16_t stat = (i2cBuff[0] << 8) | i2cBuff[1];
loopsva 1:24bb922e179c 72 scdSTR.ready = stat;
loopsva 0:2a16832400d0 73 uint8_t dat = scd30::checkCrc2b(stat, i2cBuff[2]);
loopsva 0:2a16832400d0 74
loopsva 0:2a16832400d0 75 if(dat == SCDcrcERROR) return SCDcrcERRORv1;
loopsva 0:2a16832400d0 76 if(dat == SCDisReady) return SCDisReady;
loopsva 0:2a16832400d0 77 return SCDnoERROR;
loopsva 0:2a16832400d0 78 }
loopsva 0:2a16832400d0 79
loopsva 0:2a16832400d0 80 //-----------------------------------------------------------------------------
loopsva 0:2a16832400d0 81 // Get all the measurement values, stick them into the array
loopsva 0:2a16832400d0 82
loopsva 1:24bb922e179c 83 uint8_t scd30::readMeasurement()
loopsva 0:2a16832400d0 84 {
loopsva 0:2a16832400d0 85 i2cBuff[0] = SCD30_CMMD_READ_MEAS >> 8;
loopsva 0:2a16832400d0 86 i2cBuff[1] = SCD30_CMMD_READ_MEAS & 255;
loopsva 0:2a16832400d0 87 int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 2, false);
loopsva 0:2a16832400d0 88 if(res) return SCDnoAckERROR;
loopsva 0:2a16832400d0 89
loopsva 0:2a16832400d0 90 _i2c.read(SCD30_I2C_ADDR | 1, i2cBuff, 18, false);
loopsva 0:2a16832400d0 91
loopsva 0:2a16832400d0 92 uint16_t stat = (i2cBuff[0] << 8) | i2cBuff[1];
loopsva 1:24bb922e179c 93 scdSTR.co2m = stat;
loopsva 0:2a16832400d0 94 uint8_t dat = scd30::checkCrc2b(stat, i2cBuff[2]);
loopsva 0:2a16832400d0 95 if(dat == SCDcrcERROR) return SCDcrcERRORv1;
loopsva 0:2a16832400d0 96
loopsva 0:2a16832400d0 97 stat = (i2cBuff[3] << 8) | i2cBuff[4];
loopsva 1:24bb922e179c 98 scdSTR.co2l = stat;
loopsva 0:2a16832400d0 99 dat = scd30::checkCrc2b(stat, i2cBuff[5]);
loopsva 0:2a16832400d0 100 if(dat == SCDcrcERROR) return SCDcrcERRORv2;
loopsva 0:2a16832400d0 101
loopsva 0:2a16832400d0 102 stat = (i2cBuff[6] << 8) | i2cBuff[7];
loopsva 1:24bb922e179c 103 scdSTR.tempm = stat;
loopsva 0:2a16832400d0 104 dat = scd30::checkCrc2b(stat, i2cBuff[8]);
loopsva 0:2a16832400d0 105 if(dat == SCDcrcERROR) return SCDcrcERRORv3;
loopsva 0:2a16832400d0 106
loopsva 0:2a16832400d0 107 stat = (i2cBuff[9] << 8) | i2cBuff[10];
loopsva 1:24bb922e179c 108 scdSTR.templ = stat;
loopsva 0:2a16832400d0 109 dat = scd30::checkCrc2b(stat, i2cBuff[11]);
loopsva 0:2a16832400d0 110 if(dat == SCDcrcERROR) return SCDcrcERRORv4;
loopsva 0:2a16832400d0 111
loopsva 0:2a16832400d0 112 stat = (i2cBuff[12] << 8) | i2cBuff[13];
loopsva 1:24bb922e179c 113 scdSTR.humm = stat;
loopsva 0:2a16832400d0 114 dat = scd30::checkCrc2b(stat, i2cBuff[14]);
loopsva 0:2a16832400d0 115 if(dat == SCDcrcERROR) return SCDcrcERRORv5;
loopsva 0:2a16832400d0 116
loopsva 0:2a16832400d0 117 stat = (i2cBuff[15] << 8) | i2cBuff[16];
loopsva 1:24bb922e179c 118 scdSTR.huml = stat;
loopsva 0:2a16832400d0 119 dat = scd30::checkCrc2b(stat, i2cBuff[17]);
loopsva 0:2a16832400d0 120 if(dat == SCDcrcERROR) return SCDcrcERRORv6;
loopsva 0:2a16832400d0 121
loopsva 1:24bb922e179c 122 scdSTR.co2i = (scdSTR.co2m << 16) | scdSTR.co2l ;
loopsva 1:24bb922e179c 123 scdSTR.tempi = (scdSTR.tempm << 16) | scdSTR.templ ;
loopsva 1:24bb922e179c 124 scdSTR.humi = (scdSTR.humm << 16) | scdSTR.huml ;
loopsva 0:2a16832400d0 125
loopsva 1:24bb922e179c 126 scdSTR.co2f = *(float*)&scdSTR.co2i;
loopsva 1:24bb922e179c 127 scdSTR.tempf = *(float*)&scdSTR.tempi;
loopsva 1:24bb922e179c 128 scdSTR.humf = *(float*)&scdSTR.humi;
loopsva 0:2a16832400d0 129
loopsva 0:2a16832400d0 130 return SCDnoERROR;
loopsva 0:2a16832400d0 131 }
loopsva 0:2a16832400d0 132
loopsva 0:2a16832400d0 133 //-----------------------------------------------------------------------------
loopsva 0:2a16832400d0 134 // Change the ambient temperature (in 0.01C),
loopsva 0:2a16832400d0 135 // i.e. 0x1F4 = 500 = 5.00C, CRC = 0x33
loopsva 0:2a16832400d0 136
loopsva 0:2a16832400d0 137 uint8_t scd30::setTemperatureOffs(uint16_t temp)
loopsva 0:2a16832400d0 138 {
loopsva 0:2a16832400d0 139 i2cBuff[0] = SCD30_CMMD_SET_TEMP_OFFS >> 8;
loopsva 0:2a16832400d0 140 i2cBuff[1] = SCD30_CMMD_SET_TEMP_OFFS & 255;
loopsva 0:2a16832400d0 141 i2cBuff[2] = temp >> 8;
loopsva 0:2a16832400d0 142 i2cBuff[3] = temp & 255;
loopsva 0:2a16832400d0 143 i2cBuff[4] = scd30::calcCrc2b(temp);
loopsva 0:2a16832400d0 144 int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 5, false);
loopsva 0:2a16832400d0 145 if(res) return SCDnoAckERROR;
loopsva 0:2a16832400d0 146 return SCDnoERROR;
loopsva 0:2a16832400d0 147 }
loopsva 0:2a16832400d0 148
loopsva 0:2a16832400d0 149 //-----------------------------------------------------------------------------
loopsva 0:2a16832400d0 150 // Change the altitude reading (in meters above seal level)
loopsva 0:2a16832400d0 151
loopsva 0:2a16832400d0 152 uint8_t scd30::setAltitudeComp(uint16_t alt)
loopsva 0:2a16832400d0 153 {
loopsva 0:2a16832400d0 154 i2cBuff[0] = SCD30_CMMD_SET_ALT_COMP >> 8;
loopsva 0:2a16832400d0 155 i2cBuff[1] = SCD30_CMMD_SET_ALT_COMP & 255;
loopsva 0:2a16832400d0 156 i2cBuff[2] = alt >> 8;
loopsva 0:2a16832400d0 157 i2cBuff[3] = alt & 255;
loopsva 0:2a16832400d0 158 i2cBuff[4] = scd30::calcCrc2b(alt);
loopsva 0:2a16832400d0 159 int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 5, false);
loopsva 0:2a16832400d0 160 if(res) return SCDnoAckERROR;
loopsva 0:2a16832400d0 161 return SCDnoERROR;
loopsva 0:2a16832400d0 162 }
loopsva 0:2a16832400d0 163
loopsva 0:2a16832400d0 164 //-----------------------------------------------------------------------------
loopsva 0:2a16832400d0 165 // Perform a soft reset on the SCD30
loopsva 0:2a16832400d0 166
loopsva 0:2a16832400d0 167 uint8_t scd30::softReset()
loopsva 0:2a16832400d0 168 {
loopsva 0:2a16832400d0 169 i2cBuff[0] = SCD30_CMMD_SOFT_RESET >> 8;
loopsva 0:2a16832400d0 170 i2cBuff[1] = SCD30_CMMD_SOFT_RESET & 255;
loopsva 0:2a16832400d0 171 int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 2, false);
loopsva 0:2a16832400d0 172 if(res) return SCDnoAckERROR;
loopsva 0:2a16832400d0 173 return SCDnoERROR;
loopsva 0:2a16832400d0 174 }
loopsva 0:2a16832400d0 175
loopsva 0:2a16832400d0 176 //-----------------------------------------------------------------------------
loopsva 0:2a16832400d0 177 // Calculate the CRC of a 2 byte value using the SCD30 CRC polynomial
loopsva 0:2a16832400d0 178
loopsva 0:2a16832400d0 179 uint8_t scd30::calcCrc2b(uint16_t seed)
loopsva 0:2a16832400d0 180 {
loopsva 0:2a16832400d0 181 uint8_t bit; // bit mask
loopsva 0:2a16832400d0 182 uint8_t crc = SCD30_CRC_INIT; // calculated checksum
loopsva 0:2a16832400d0 183
loopsva 0:2a16832400d0 184 // calculates 8-Bit checksum with given polynomial
loopsva 0:2a16832400d0 185
loopsva 0:2a16832400d0 186 crc ^= (seed >> 8) & 255;
loopsva 0:2a16832400d0 187 for(bit = 8; bit > 0; --bit)
loopsva 0:2a16832400d0 188 {
loopsva 0:2a16832400d0 189 if(crc & 0x80) crc = (crc << 1) ^ SCD30_POLYNOMIAL;
loopsva 0:2a16832400d0 190 else crc = (crc << 1);
loopsva 0:2a16832400d0 191 }
loopsva 0:2a16832400d0 192
loopsva 0:2a16832400d0 193 crc ^= seed & 255;
loopsva 0:2a16832400d0 194 for(bit = 8; bit > 0; --bit)
loopsva 0:2a16832400d0 195 {
loopsva 0:2a16832400d0 196 if(crc & 0x80) crc = (crc << 1) ^ SCD30_POLYNOMIAL;
loopsva 0:2a16832400d0 197 else crc = (crc << 1);
loopsva 0:2a16832400d0 198 }
loopsva 0:2a16832400d0 199
loopsva 0:2a16832400d0 200 return crc;
loopsva 0:2a16832400d0 201 }
loopsva 0:2a16832400d0 202
loopsva 0:2a16832400d0 203 //-----------------------------------------------------------------------------
loopsva 0:2a16832400d0 204 // Compare the CRC values
loopsva 0:2a16832400d0 205
loopsva 0:2a16832400d0 206 uint8_t scd30::checkCrc2b(uint16_t seed, uint8_t crcIn)
loopsva 0:2a16832400d0 207 {
loopsva 0:2a16832400d0 208 uint8_t crcCalc = scd30::calcCrc2b(seed);
loopsva 0:2a16832400d0 209 if(crcCalc != crcIn) return SCDcrcERROR;
loopsva 0:2a16832400d0 210 return SCDnoERROR;
loopsva 0:2a16832400d0 211 }
loopsva 0:2a16832400d0 212
loopsva 0:2a16832400d0 213 //-----------------------------------------------------------------------------
loopsva 0:2a16832400d0 214 // start single-measurement with barometer reading (in mB)
loopsva 0:2a16832400d0 215 //
loopsva 0:2a16832400d0 216
loopsva 0:2a16832400d0 217 uint8_t scd30::startOneMeasurement(uint16_t baro)
loopsva 0:2a16832400d0 218 {
loopsva 0:2a16832400d0 219 i2cBuff[0] = SCD30_CMMD_START_SINGLE_MEAS >> 8;
loopsva 0:2a16832400d0 220 i2cBuff[1] = SCD30_CMMD_START_SINGLE_MEAS & 255;
loopsva 0:2a16832400d0 221 i2cBuff[2] = baro >> 8;
loopsva 0:2a16832400d0 222 i2cBuff[3] = baro & 255;
loopsva 0:2a16832400d0 223 i2cBuff[4] = scd30::calcCrc2b(baro);
loopsva 0:2a16832400d0 224 int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 5, false);
loopsva 0:2a16832400d0 225 if(res) return SCDnoAckERROR;
loopsva 0:2a16832400d0 226 return SCDnoERROR;
loopsva 0:2a16832400d0 227 }
loopsva 0:2a16832400d0 228
loopsva 0:2a16832400d0 229 //-----------------------------------------------------------------------------
loopsva 0:2a16832400d0 230 // Get article code
loopsva 0:2a16832400d0 231
loopsva 1:24bb922e179c 232 uint8_t scd30::getArticleCode()
loopsva 0:2a16832400d0 233 {
loopsva 0:2a16832400d0 234 i2cBuff[0] = SCD30_CMMD_READ_ARTICLECODE >> 8;
loopsva 0:2a16832400d0 235 i2cBuff[1] = SCD30_CMMD_READ_ARTICLECODE & 255;
loopsva 0:2a16832400d0 236 int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 2, false);
loopsva 0:2a16832400d0 237 if(res) return SCDnoAckERROR;
loopsva 0:2a16832400d0 238
loopsva 0:2a16832400d0 239 _i2c.read(SCD30_I2C_ADDR | 1, i2cBuff, 3, false);
loopsva 0:2a16832400d0 240 uint16_t stat = (i2cBuff[0] << 8) | i2cBuff[1];
loopsva 1:24bb922e179c 241 scdSTR.acode = stat;
loopsva 0:2a16832400d0 242 uint8_t dat = scd30::checkCrc2b(stat, i2cBuff[2]);
loopsva 0:2a16832400d0 243
loopsva 0:2a16832400d0 244 if(dat == SCDcrcERROR) return SCDcrcERRORv1;
loopsva 0:2a16832400d0 245 return SCDnoERROR;
loopsva 0:2a16832400d0 246 }
loopsva 0:2a16832400d0 247
loopsva 0:2a16832400d0 248 //-----------------------------------------------------------------------------
loopsva 0:2a16832400d0 249 // Get scd30 serial number
loopsva 0:2a16832400d0 250
loopsva 1:24bb922e179c 251 uint8_t scd30::getSerialNumber()
loopsva 0:2a16832400d0 252 {
loopsva 0:2a16832400d0 253 i2cBuff[0] = SCD30_CMMD_READ_SERIALNBR >> 8;
loopsva 0:2a16832400d0 254 i2cBuff[1] = SCD30_CMMD_READ_SERIALNBR & 255;
loopsva 0:2a16832400d0 255 int res = _i2c.write(SCD30_I2C_ADDR, i2cBuff, 2, false);
loopsva 0:2a16832400d0 256 if(res) return SCDnoAckERROR;
loopsva 0:2a16832400d0 257
loopsva 0:2a16832400d0 258 int i = 0;
loopsva 1:24bb922e179c 259 for(i = 0; i < sizeof(scdSTR.sn); i++) scdSTR.sn[i] = 0;
loopsva 0:2a16832400d0 260 for(i = 0; i < sizeof(i2cBuff); i++) i2cBuff[i] = 0;
loopsva 0:2a16832400d0 261
loopsva 0:2a16832400d0 262 _i2c.read(SCD30_I2C_ADDR | 1, i2cBuff, SCD30_SN_SIZE, false);
loopsva 0:2a16832400d0 263 int t = 0;
loopsva 0:2a16832400d0 264 for(i = 0; i < SCD30_SN_SIZE; i +=3) {
loopsva 0:2a16832400d0 265 uint16_t stat = (i2cBuff[i] << 8) | i2cBuff[i + 1];
loopsva 1:24bb922e179c 266 scdSTR.sn[i - t] = stat >> 8;
loopsva 1:24bb922e179c 267 scdSTR.sn[i - t + 1] = stat & 255;
loopsva 0:2a16832400d0 268 uint8_t dat = scd30::checkCrc2b(stat, i2cBuff[i + 2]);
loopsva 0:2a16832400d0 269 t++;
loopsva 0:2a16832400d0 270 if(dat == SCDcrcERROR) return SCDcrcERRORv1;
loopsva 0:2a16832400d0 271 if(stat == 0) break;
loopsva 0:2a16832400d0 272 }
loopsva 0:2a16832400d0 273
loopsva 0:2a16832400d0 274 return SCDnoERROR;
loopsva 0:2a16832400d0 275 }