Zi Qi Yap / SPS30-sensor

Dependents:   SPS30_TEST

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers sps30.cpp Source File

sps30.cpp

00001 #include "mbed.h"
00002 #include "sps30.h"
00003 
00004 //-----------------------------------------------------------------------------
00005 // Constructor 
00006 
00007 Sps30::Sps30(PinName sda, PinName scl, int i2c_frequency)  : _i2c(sda, scl) {
00008         _i2c.frequency(i2c_frequency);
00009         
00010         sensor_data_name.push_back("PM1.0 Mass");
00011         sensor_data_name.push_back("PM2.5 Mass");
00012         sensor_data_name.push_back("PM4.0 Mass");
00013         sensor_data_name.push_back("PM10.0 Mass");
00014         sensor_data_name.push_back("PM0.5 Num");
00015         sensor_data_name.push_back("PM1.0 Num");
00016         sensor_data_name.push_back("PM2.5 Num");
00017         sensor_data_name.push_back("PM4.0 Num");
00018         sensor_data_name.push_back("PM10.0 Num");
00019         sensor_data_name.push_back("Typical Particle Size");
00020 }
00021 
00022 //-----------------------------------------------------------------------------
00023 // Destructor
00024 
00025 Sps30::~Sps30() {
00026 }
00027 
00028 //-----------------------------------------------------------------------------
00029 // start auto-measurement 
00030 //
00031 
00032 uint8_t Sps30::StartMeasurement() 
00033 {
00034     i2cbuff[0] = SPS30_CMMD_STRT_MEAS >> 8;
00035     i2cbuff[1] = SPS30_CMMD_STRT_MEAS & 255;
00036     i2cbuff[2] = SPS30_STRT_MEAS_WRITE_DATA >> 8;
00037     i2cbuff[3] = SPS30_STRT_MEAS_WRITE_DATA & 255;
00038     i2cbuff[4] = Sps30::CalcCrc2b(SPS30_STRT_MEAS_WRITE_DATA);
00039     int res = _i2c.write(SPS30_I2C_ADDR, i2cbuff, 5, false);
00040     if(res) return SPSNOACKERROR;
00041     return SPSNOERROR;
00042 }
00043 
00044 //-----------------------------------------------------------------------------
00045 // Stop auto-measurement
00046 
00047 uint8_t Sps30::StopMeasurement()
00048 {
00049     i2cbuff[0] = SPS30_CMMD_STOP_MEAS >> 8;
00050     i2cbuff[1] = SPS30_CMMD_STOP_MEAS & 255;
00051     int res = _i2c.write(SPS30_I2C_ADDR, i2cbuff, 2, false);
00052     if(res) return SPSNOACKERROR;
00053     return SPSNOERROR;
00054 }
00055 
00056 //-----------------------------------------------------------------------------
00057 // Get Sps30 serial number
00058 
00059 int Sps30::GetSerialNumber()
00060 {
00061     i2cbuff[0] = SPS30_CMMD_READ_SERIALNBR >> 8;
00062     i2cbuff[1] = SPS30_CMMD_READ_SERIALNBR & 255;
00063     int res = _i2c.write(SPS30_I2C_ADDR, i2cbuff, 2, false);
00064     if(res) return SPSNOACKERROR;
00065     
00066     int i = 0;
00067     for(i = 0; i < sizeof(sn); i++) sn[i] = 0;
00068     for(i = 0; i < sizeof(i2cbuff); i++) i2cbuff[i] = 0;
00069     
00070     _i2c.read(SPS30_I2C_ADDR | 1, i2cbuff, SPS30_SN_SIZE, false);
00071     int t = 0;
00072     for(i = 0; i < SPS30_SN_SIZE; i +=3) 
00073     {
00074         uint16_t stat = (i2cbuff[i] << 8) | i2cbuff[i + 1];
00075         sn[i - t] = stat >> 8;
00076         sn[i - t + 1] = stat & 255;
00077         uint8_t dat = Sps30::CheckCrc2b(stat, i2cbuff[i + 2]);
00078         t++;
00079         if(dat == SPSCRCERROR) return SPSCRCERROR;
00080         if(stat == 0) break;
00081     }
00082     
00083     return SPSNOERROR;
00084 }
00085 
00086 //-----------------------------------------------------------------------------
00087 // Get ready status value
00088 
00089 uint8_t Sps30::GetReadyStatus()
00090 {
00091     i2cbuff[0] = SPS30_CMMD_GET_READY_STAT >> 8;
00092     i2cbuff[1] = SPS30_CMMD_GET_READY_STAT & 255;
00093     int res = _i2c.write(SPS30_I2C_ADDR, i2cbuff, 2, false);
00094     if(res) return SPSNOACKERROR;
00095     
00096     _i2c.read(SPS30_I2C_ADDR | 1, i2cbuff, 3, false);
00097     uint16_t stat = (i2cbuff[0] << 8) | i2cbuff[1];
00098     sps_ready = stat;
00099     uint8_t dat = Sps30::CheckCrc2b(stat, i2cbuff[2]);
00100     
00101     if(dat == SPSCRCERROR) return SPSCRCERROR;
00102     return SPSNOERROR;
00103 }
00104 
00105 //-----------------------------------------------------------------------------
00106 // Get all the measurement values, stick them into the array
00107 
00108 uint8_t Sps30::ReadMeasurement()   
00109 {
00110     i2cbuff[0] = SPS30_CMMD_READ_MEAS >> 8;
00111     i2cbuff[1] = SPS30_CMMD_READ_MEAS & 255;
00112     int res = _i2c.write(SPS30_I2C_ADDR, i2cbuff, 2, false);
00113     if(res) return SPSNOACKERROR;
00114     
00115     _i2c.read(SPS30_I2C_ADDR | 1, i2cbuff, 60, false);
00116     
00117     uint16_t stat = (i2cbuff[0] << 8) | i2cbuff[1];
00118     mass_1p0_m = stat;
00119     uint8_t dat = Sps30::CheckCrc2b(stat, i2cbuff[2]);
00120     if(dat == SPSCRCERROR) return SPSCRCERROR;
00121     
00122     stat = (i2cbuff[3] << 8) | i2cbuff[4];
00123     mass_1p0_l = stat;
00124     dat = Sps30::CheckCrc2b(stat, i2cbuff[5]);
00125     if(dat == SPSCRCERROR) return SPSCRCERROR;
00126 
00127 
00128     
00129     stat = (i2cbuff[6] << 8) | i2cbuff[7];
00130     mass_2p5_m = stat;
00131     dat = Sps30::CheckCrc2b(stat, i2cbuff[8]);
00132     if(dat == SPSCRCERROR) return SPSCRCERROR;
00133     
00134     stat = (i2cbuff[9] << 8) | i2cbuff[10];
00135     mass_2p5_l = stat;
00136     dat = Sps30::CheckCrc2b(stat, i2cbuff[11]);
00137     if(dat == SPSCRCERROR) return SPSCRCERROR;
00138 
00139 
00140     
00141     stat = (i2cbuff[12] << 8) | i2cbuff[13];
00142     mass_4p0_m = stat;
00143     dat = Sps30::CheckCrc2b(stat, i2cbuff[14]);
00144     if(dat == SPSCRCERROR) return SPSCRCERROR;
00145     
00146     stat = (i2cbuff[15] << 8) | i2cbuff[16];
00147     mass_4p0_l = stat;
00148     dat = Sps30::CheckCrc2b(stat, i2cbuff[17]);
00149     if(dat == SPSCRCERROR) return SPSCRCERROR;
00150 
00151 
00152 
00153     stat = (i2cbuff[18] << 8) | i2cbuff[19];
00154     mass_10p0_m = stat;
00155     dat = Sps30::CheckCrc2b(stat, i2cbuff[20]);
00156     if(dat == SPSCRCERROR) return SPSCRCERROR;
00157     
00158     stat = (i2cbuff[21] << 8) | i2cbuff[22];
00159     mass_10p0_l = stat;
00160     dat = Sps30::CheckCrc2b(stat, i2cbuff[23]);
00161     if(dat == SPSCRCERROR) return SPSCRCERROR;
00162 
00163 
00164 
00165     stat = (i2cbuff[24] << 8) | i2cbuff[25];
00166     num_0p5_m = stat;
00167     dat = Sps30::CheckCrc2b(stat, i2cbuff[26]);
00168     if(dat == SPSCRCERROR) return SPSCRCERROR;
00169     
00170     stat = (i2cbuff[27] << 8) | i2cbuff[28];
00171     num_0p5_l = stat;
00172     dat = Sps30::CheckCrc2b(stat, i2cbuff[29]);
00173     if(dat == SPSCRCERROR) return SPSCRCERROR;
00174 
00175 
00176     stat = (i2cbuff[30] << 8) | i2cbuff[31];
00177     num_1p0_m = stat;
00178     dat = Sps30::CheckCrc2b(stat, i2cbuff[32]);
00179     if(dat == SPSCRCERROR) return SPSCRCERROR;
00180     
00181     stat = (i2cbuff[33] << 8) | i2cbuff[34];
00182     num_1p0_l = stat;
00183     dat = Sps30::CheckCrc2b(stat, i2cbuff[35]);
00184     if(dat == SPSCRCERROR) return SPSCRCERROR;
00185 
00186 
00187     
00188     stat = (i2cbuff[36] << 8) | i2cbuff[37];
00189     num_2p5_m = stat;
00190     dat = Sps30::CheckCrc2b(stat, i2cbuff[38]);
00191     if(dat == SPSCRCERROR) return SPSCRCERROR;
00192     
00193     stat = (i2cbuff[39] << 8) | i2cbuff[40];
00194     num_2p5_l = stat;
00195     dat = Sps30::CheckCrc2b(stat, i2cbuff[41]);
00196     if(dat == SPSCRCERROR) return SPSCRCERROR;
00197 
00198 
00199     
00200     stat = (i2cbuff[42] << 8) | i2cbuff[43];
00201     num_4p0_m = stat;
00202     dat = Sps30::CheckCrc2b(stat, i2cbuff[44]);
00203     if(dat == SPSCRCERROR) return SPSCRCERROR;
00204     
00205     stat = (i2cbuff[45] << 8) | i2cbuff[46];
00206     num_4p0_l = stat;
00207     dat = Sps30::CheckCrc2b(stat, i2cbuff[47]);
00208     if(dat == SPSCRCERROR) return SPSCRCERROR;
00209 
00210 
00211     stat = (i2cbuff[48] << 8) | i2cbuff[49];
00212     num_10p0_m = stat;
00213     dat = Sps30::CheckCrc2b(stat, i2cbuff[50]);
00214     if(dat == SPSCRCERROR) return SPSCRCERROR;
00215     
00216     stat = (i2cbuff[51] << 8) | i2cbuff[52];
00217     num_10p0_l = stat;
00218     dat = Sps30::CheckCrc2b(stat, i2cbuff[53]);
00219     if(dat == SPSCRCERROR) return SPSCRCERROR;
00220 
00221 
00222     stat = (i2cbuff[54] << 8) | i2cbuff[55];
00223     typ_pm_size_m = stat;
00224     dat = Sps30::CheckCrc2b(stat, i2cbuff[56]);
00225     if(dat == SPSCRCERROR) return SPSCRCERROR;
00226     
00227     stat = (i2cbuff[57] << 8) | i2cbuff[58];
00228     typ_pm_size_l = stat;
00229     dat = Sps30::CheckCrc2b(stat, i2cbuff[59]);
00230     if(dat == SPSCRCERROR) return SPSCRCERROR;
00231     
00232     mass_1p0_i = (mass_1p0_m << 16) | mass_1p0_l;
00233     mass_2p5_i = (mass_2p5_m << 16) | mass_2p5_l;
00234     mass_4p0_i = (mass_4p0_m << 16) | mass_4p0_l;
00235     mass_10p0_i = (mass_10p0_m << 16) | mass_10p0_l;
00236 
00237     num_0p5_i = (num_0p5_m << 16) | num_0p5_l;
00238     num_1p0_i = (num_1p0_m << 16) | num_1p0_l;
00239     num_2p5_i = (num_2p5_m << 16) | num_2p5_l;
00240     num_4p0_i = (num_4p0_m << 16) | num_4p0_l;
00241     num_10p0_i = (num_10p0_m << 16) | num_10p0_l;          
00242 
00243     typ_pm_size_i = (typ_pm_size_m << 16) | typ_pm_size_l;          
00244 
00245     mass_1p0_f = *(float*)&mass_1p0_i;
00246     mass_2p5_f = *(float*)&mass_2p5_i;
00247     mass_4p0_f = *(float*)&mass_4p0_i;
00248     mass_10p0_f = *(float*)&mass_10p0_i;
00249 
00250     num_0p5_f = *(float*)&num_0p5_i;
00251     num_1p0_f = *(float*)&num_1p0_i;
00252     num_2p5_f = *(float*)&num_2p5_i;
00253     num_4p0_f = *(float*)&num_4p0_i;
00254     num_10p0_f = *(float*)&num_10p0_i;          
00255 
00256     typ_pm_size_f = *(float*)&typ_pm_size_i;
00257     
00258     return SPSNOERROR;
00259 }
00260     
00261 //-----------------------------------------------------------------------------
00262 // Calculate the CRC of a 2 byte value using the SPS30 CRC polynomial
00263 
00264 uint8_t Sps30::CalcCrc2b(uint16_t seed)
00265 {
00266   uint8_t bit;                  // bit mask
00267   uint8_t crc = SPS30_CRC_INIT; // calculated checksum
00268   
00269   // calculates 8-Bit checksum with given polynomial
00270 
00271     crc ^= (seed >> 8) & 255;
00272     for(bit = 8; bit > 0; --bit)
00273     {
00274       if(crc & 0x80) crc = (crc << 1) ^ SPS30_POLYNOMIAL;
00275       else           crc = (crc << 1);
00276     }
00277 
00278     crc ^= seed & 255;
00279     for(bit = 8; bit > 0; --bit)
00280     {
00281       if(crc & 0x80) crc = (crc << 1) ^ SPS30_POLYNOMIAL;
00282       else           crc = (crc << 1);
00283     }
00284     
00285   return crc;
00286 }
00287 
00288 //-----------------------------------------------------------------------------
00289 // Compare the CRC values
00290 
00291 uint8_t Sps30::CheckCrc2b(uint16_t seed, uint8_t crc_in)
00292 {
00293     uint8_t crc_calc = Sps30::CalcCrc2b(seed);
00294     if(crc_calc != crc_in) return SPSCRCERROR;
00295     return SPSNOERROR;
00296 }
00297 
00298 //-----------------------------------------------------------------------------
00299 // Read Auto Cleaning Interval on the SPS30
00300 
00301 uint8_t Sps30::ReadAutoCleanInterval()
00302 {   
00303     i2cbuff[0] = SPS30_CMMD_AUTO_CLEAN_INTV >> 8;
00304     i2cbuff[1] = SPS30_CMMD_AUTO_CLEAN_INTV & 255;
00305     
00306     int res = _i2c.write(SPS30_I2C_ADDR, i2cbuff, 2, false);
00307     if(res) return SPSNOACKERROR;
00308     
00309     _i2c.read(SPS30_I2C_ADDR | 1, i2cbuff, 6, false);
00310     
00311     uint16_t stat = (i2cbuff[0] << 8) | i2cbuff[1];
00312     clean_interval_m = stat;
00313     uint8_t dat = Sps30::CheckCrc2b(stat, i2cbuff[2]);
00314     if(dat == SPSCRCERROR) return SPSCRCERROR;
00315     
00316     stat = (i2cbuff[3] << 8) | i2cbuff[4];
00317     clean_interval_l = stat;
00318     dat = Sps30::CheckCrc2b(stat, i2cbuff[5]);
00319     if(dat == SPSCRCERROR) return SPSCRCERROR;
00320     
00321     clean_interval_i = (clean_interval_m << 16) | clean_interval_l;
00322     
00323     return SPSNOERROR;
00324 }
00325 
00326 //-----------------------------------------------------------------------------
00327 // Set Auto Cleaning Interval on the SPS30
00328 
00329 uint8_t Sps30::SetAutoCleanInterval(uint32_t set_interval)
00330 {
00331     uint16_t set_interval_m = set_interval >> 16;
00332     uint16_t set_interval_l = set_interval & 65535;
00333     
00334     i2cbuff[0] = SPS30_CMMD_AUTO_CLEAN_INTV >> 8;
00335     i2cbuff[1] = SPS30_CMMD_AUTO_CLEAN_INTV & 255;
00336     
00337     i2cbuff[2] = set_interval_m >> 8;
00338     i2cbuff[3] = set_interval_m & 255;
00339     i2cbuff[4] = Sps30::CalcCrc2b(set_interval_m);
00340     
00341     i2cbuff[5] = set_interval_l >> 8;
00342     i2cbuff[6] = set_interval_l & 255;
00343     i2cbuff[7] = Sps30::CalcCrc2b(set_interval_l);
00344     
00345     int res = _i2c.write(SPS30_I2C_ADDR, i2cbuff, 8, false);
00346     if(res) return SPSNOACKERROR;
00347     
00348     _i2c.read(SPS30_I2C_ADDR | 1, i2cbuff, 6, false);
00349     
00350     uint16_t stat = (i2cbuff[0] << 8) | i2cbuff[1];
00351     clean_interval_m = stat;
00352     uint8_t dat = Sps30::CheckCrc2b(stat, i2cbuff[2]);
00353     if(dat == SPSCRCERROR) return SPSCRCERROR;
00354     
00355     stat = (i2cbuff[3] << 8) | i2cbuff[4];
00356     clean_interval_l = stat;
00357     dat = Sps30::CheckCrc2b(stat, i2cbuff[5]);
00358     if(dat == SPSCRCERROR) return SPSCRCERROR;
00359     
00360     clean_interval_i = (clean_interval_m << 16) | clean_interval_l;
00361     
00362     return SPSNOERROR;
00363 }
00364 
00365 //-----------------------------------------------------------------------------
00366 // Perform manual fan cleaning
00367 
00368 uint8_t Sps30::StartFanClean()
00369 {
00370     i2cbuff[0] = SPS30_CMMD_START_FAN_CLEAN >> 8;
00371     i2cbuff[1] = SPS30_CMMD_START_FAN_CLEAN & 255;
00372     int res = _i2c.write(SPS30_I2C_ADDR, i2cbuff, 2, false);
00373     if(res) return SPSNOACKERROR;
00374     return SPSNOERROR;
00375 }
00376 
00377 //-----------------------------------------------------------------------------
00378 // Perform a soft reset on the SPS30
00379 
00380 uint8_t Sps30::SoftReset()
00381 {
00382     i2cbuff[0] = SPS30_CMMD_SOFT_RESET >> 8;
00383     i2cbuff[1] = SPS30_CMMD_SOFT_RESET & 255;
00384     int res = _i2c.write(SPS30_I2C_ADDR, i2cbuff, 2, false);
00385     if(res) return SPSNOACKERROR;
00386     return SPSNOERROR;
00387 }
00388 
00389 //-----------------------------------------------------------------------------
00390 // Fill up the sensor data vector to be read in Sensor Thread 
00391 
00392 void Sps30::GetDataArray()
00393 {   
00394     sensor_float_data.clear();
00395 
00396     sensor_float_data.push_back(mass_1p0_f); 
00397     sensor_float_data.push_back(mass_2p5_f); 
00398     sensor_float_data.push_back(mass_4p0_f); 
00399     sensor_float_data.push_back(mass_10p0_f); 
00400     
00401     sensor_float_data.push_back(num_0p5_f); 
00402     sensor_float_data.push_back(num_1p0_f); 
00403     sensor_float_data.push_back(num_2p5_f); 
00404     sensor_float_data.push_back(num_4p0_f); 
00405     sensor_float_data.push_back(num_10p0_f); 
00406     
00407     sensor_float_data.push_back(typ_pm_size_f); 
00408     
00409     int i = 0;
00410     for (i = 0; i < sensor_float_data.size(); i++)
00411     {
00412         std::string sensor_string_data = ConvertDataToString(sensor_float_data[i]);
00413         sensor_data.push_back( make_pair(sensor_data_name[i],sensor_string_data) );
00414 
00415         int val_data;
00416         if (i < 4) 
00417         {
00418 //            sensor_float_data[i] = 99999.00;        // test use case 3c; expected output: EmTrace for sensor data out-of-range with data value
00419             val_data = ValidateData(sensor_float_data[i], MASS_MIN, MASS_MAX);
00420         }
00421         else if (i < 9) 
00422         {
00423 //            sensor_float_data[i] = 99999.00;        // test use case 3c; expected output: EmTrace for sensor data out-of-range with data value
00424             val_data = ValidateData(sensor_float_data[i], NUM_MIN, NUM_MAX);
00425         }
00426 
00427         if (val_data == SENSOR_DATAOOR) 
00428         {
00429             std::string oor_string = sensor_data_name[i] + " is out-of-range: " + sensor_string_data;
00430             SendSensorEmTrace(SENSOR_DATAOOR, oor_string);
00431         }
00432     }
00433     return;
00434 }
00435 
00436 /**************************   PUBLIC METHODS   **********************************/
00437 
00438 //-----------------------------------------------------------------------------
00439 // Initialise SPS30
00440 
00441 void Sps30::InitSensor()
00442 {   
00443     uint8_t dat = Sps30::GetSerialNumber();
00444     if (dat == SPSNOACKERROR) 
00445     {
00446         SendSensorEmTrace(SENSOR_DCN);
00447         sps_status = 0;
00448     }
00449     
00450     dat = Sps30::StartMeasurement();
00451     if (dat == SPSNOACKERROR) 
00452     {
00453         SendSensorEmTrace(SENSOR_DCN);
00454         sps_status = 0;
00455     }
00456     
00457     SendSensorEmTrace(SENSOR_CN);
00458     sensor_serial = ConvertSerialNumber(sn);
00459     sps_status = 1;
00460     t_count = 0;
00461 
00462     return;
00463 }
00464 
00465 //-----------------------------------------------------------------------------
00466 // Poll SPS30
00467 
00468 int Sps30::PollSensor()
00469 {
00470     sensor_data.clear();
00471     uint8_t dat = Sps30::GetReadyStatus();
00472     if (dat == SPSNOACKERROR)
00473     {
00474         if (sps_status == 1) SendSensorEmTrace(SENSOR_DCN);
00475         sps_status = 0;
00476         return SENSOR_DCN;
00477     }
00478     
00479     if (sps_status == 0) 
00480     {
00481         Sps30::InitSensor();   // If was disconnected, reinitialise
00482         Sps30::GetReadyStatus();
00483     }
00484 //    sps_ready = 0;               // test use case 3b; expeced output: EMTrace for sensor data not ready
00485     if (sps_ready == SPSISREADY)
00486     {
00487         uint8_t crcc = Sps30::ReadMeasurement();
00488 //        crcc = SPSCRCERROR;      // test use case 3b; expeced output: EMTrace for sensor data not ready
00489         if (crcc == SPSNOERROR)
00490         {
00491             Sps30::GetDataArray();
00492             return SENSOR_DATAOK;
00493         }
00494         else if (crcc == SPSNOACKERROR)
00495         {
00496             SendSensorEmTrace(SENSOR_DCN);
00497             sps_status = 0;
00498             return SENSOR_DCN;
00499         }
00500         else
00501         {
00502             SendSensorEmTrace(SENSOR_DATAERR);
00503             return SENSOR_DATAERR;
00504         }
00505     }
00506     else
00507     {        
00508         if (t_count == TIMEOUT_COUNT)     // timeout counter for data not ready
00509         {
00510             SendSensorEmTrace(SENSOR_DATAERR);
00511             Sps30::SoftReset();
00512             t_count = 0;
00513         }
00514         t_count++;
00515 
00516         return SENSOR_DATAERR;
00517     }
00518 }