# Question: How can we connect a PMS5003 dust sensor to an Arduino?

warren asked on October 26, 2018 16:50
151 | 3 answers | #17406

Are there some code and wiring diagrams folks have found to connect one of these Plantower sensors (that is inside the Purple Air) to an Arduino? And how to read the data off of it?

* $30 shipped, laser particle sensor with fan - https://www.amazon.com/Beaster-particle-Digital-Purifier-Precision/dp/B07F2X6RVF Thanks! ### 9 Comments @guolivar @BrandonFeenstra @GreenFrogg @rockets @nanocastro do you have some information on this? Some folks are looking to build some sensor/dataloggers in Providence and I know some of you have worked with this one. Is this a question? Click here to post it to the Questions page. Reply to this comment... This sensor comes with a serial output. Adafruit has an excellent tutorial on how to interface to an Arduino:https://learn.adafruit.com/pm25-air-quality-sensor/arduino-code Reply to this comment... Wow, this got a reply on my posted question real fast. Thank you!!!! On Friday, October 26, 2018 at 12:45:33 PM UTC-4, notifications wrote: -- Post to this group at plots-airquality@googlegroups.com ## Public Lab mailing lists (http://publiclab.org/lists) are great for discussion, but to get attribution, open source your work, and make it easy for others to find and cite your contributions, please publish your work at http://publiclab.org You received this message because you are subscribed to the Google Groups "plots-airquality" group. To unsubscribe from this group and stop receiving emails from it, send an email to plots-airquality+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout. > How can we connect a PMS5003 dust sensor to an Arduino? was tagged with #air-quality by warren Read and respond to the post here: https://publiclab.org/notes/warren/10-26-2018/how-can-we-connect-a-pms5003-dust-sensor-to-an-arduino You received this email because it was tagged with: air-quality. To change your preferences, please visit https://publiclab.org/subscriptions. Report spam and abuse to: moderators@publiclab.org Check out the blog at https://publiclab.org/blog | Love our work? Become a Public Lab Sustaining Member today at https://publiclab.org/donate Is this a question? Click here to post it to the Questions page. Hi... This is Viking Karlsson and I am new here and I need support from you peoples. I was surfing the net and at random land over here and find the stuff useful. Reply to this comment... So, great timing, I was just working on doing this with an Arduino mega today (here are some pictures). I was going to do a more extensive write up on this, but I'm not quite ready since I haven't put it in a case and sent it out for testing yet. Just FYI I'm using the AirCasting app and a cheap android phone for data logging, though I assume you can do it just fine with an SD card slot or other method, this was just the lowest effort way. Here's the breakout I printed up (definitely looking for some feedback): https://oshpark.com/shared_projects/afBXgzqa Here's the socket for the cables: https://www.digikey.com/product-detail/en/molex-llc/0530480810/WM1748-ND/242870 Here's the code I modified and cobbled together from Adafruit and a couple of other sources, I added in functionality for two PMS5003s and a bme280 as well as support for the AirCasting app using a HC-06 bluetooth module: // On Leonardo/Micro or others with hardware serial, use those! // uncomment this line: [#define](/tag/define) pmsSerialA Serial1 [#define](/tag/define) pmsSerialB Serial2 // For UNO and others without hardware serial, we must use software serial... // pin [#2](/n/2) is IN from sensor (TX pin on sensor), leave pin [#3](/n/3) disconnected // comment these two lines if using hardware serial //#include <SoftwareSerial.h> //#include <AltSoftSerial.h> //AltSoftSerial pmsSerial; [#define](/tag/define) mySerial Serial3 // for bluetooth module [#include](/tag/include) <Wire.h> [#define](/tag/define) BME280_ADDRESS 0x76 unsigned long int hum_raw,temp_raw,pres_raw; signed long int t_fine; uint16_t dig_T1; int16_t dig_T2; int16_t dig_T3; uint16_t dig_P1; int16_t dig_P2; int16_t dig_P3; int16_t dig_P4; int16_t dig_P5; int16_t dig_P6; int16_t dig_P7; int16_t dig_P8; int16_t dig_P9; int8_t dig_H1; int16_t dig_H2; int8_t dig_H3; int16_t dig_H4; int16_t dig_H5; int8_t dig_H6; void setup() { // our debugging output Serial.begin(4800); mySerial.begin(115200); // sensor baud rate is 9600 pmsSerialA.begin(9600); pmsSerialB.begin(9600); // its BME280 things uint8_t osrs_t = 1; //Temperature oversampling x 1 uint8_t osrs_p = 1; //Pressure oversampling x 1 uint8_t osrs_h = 1; //Humidity oversampling x 1 uint8_t mode = 3; //Normal mode uint8_t t_sb = 5; //Tstandby 1000ms uint8_t filter = 0; //Filter off uint8_t spi3w_en = 0; //3-wire SPI Disable uint8_t ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode; uint8_t config_reg = (t_sb << 5) | (filter << 2) | spi3w_en; uint8_t ctrl_hum_reg = osrs_h; Wire.begin(); writeReg(0xF2,ctrl_hum_reg); writeReg(0xF4,ctrl_meas_reg); writeReg(0xF5,config_reg); readTrim(); } struct pms5003adata { uint16_t framelen; uint16_t pm10_standard, pm25_standard, pm100_standard; uint16_t pm10_env, pm25_env, pm100_env; uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um; uint16_t unused; uint16_t checksum; }; struct pms5003bdata { uint16_t framelen; uint16_t pm10_standard, pm25_standard, pm100_standard; uint16_t pm10_env, pm25_env, pm100_env; uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um; uint16_t unused; uint16_t checksum; }; struct pms5003adata dataA; struct pms5003bdata dataB; void loop() { if (readPMSdataA(&pmsSerialA)) { // reading data was successful! Serial.println(); Serial.println("A-------------------------------------A"); Serial.println("Concentration Units (standard)"); Serial.print("PM 1.0: "); Serial.print(dataA.pm10_standard); Serial.print("\t\tPM 2.5: "); Serial.print(dataA.pm25_standard); Serial.print("\t\tPM 10: "); Serial.println(dataA.pm100_standard); Serial.println("---------------------------------------"); Serial.println("Concentration Units (environmental)"); Serial.print("PM 1.0: "); Serial.print(dataA.pm10_env); Serial.print("\t\tPM 2.5: "); Serial.print(dataA.pm25_env); Serial.print("\t\tPM 10: "); Serial.println(dataA.pm100_env); Serial.println("---------------------------------------"); Serial.print("Particles > 0.3um / 0.1L air:"); Serial.println(dataA.particles_03um); Serial.print("Particles > 0.5um / 0.1L air:"); Serial.println(dataA.particles_05um); Serial.print("Particles > 1.0um / 0.1L air:"); Serial.println(dataA.particles_10um); Serial.print("Particles > 2.5um / 0.1L air:"); Serial.println(dataA.particles_25um); Serial.print("Particles > 5.0um / 0.1L air:"); Serial.println(dataA.particles_50um); Serial.print("Particles > 50 um / 0.1L air:"); Serial.println(dataA.particles_100um); Serial.println("A-------------------------------------A"); double temp_act = 0.0, press_act = 0.0,hum_act=0.0, fah_act= 0.0; signed long int temp_cal; unsigned long int press_cal,hum_cal; readData(); temp_cal = calibration_T(temp_raw); press_cal = calibration_P(pres_raw); hum_cal = calibration_H(hum_raw); temp_act = (double)temp_cal / 100.0; press_act = (double)press_cal / 100.0; hum_act = (double)hum_cal / 1024.0; fah_act = temp_act * 9/5 +32; Serial.print("TEMP : "); Serial.print(temp_act); Serial.print(" Deg C, "); Serial.print(fah_act); Serial.print("Deg F PRESS : "); Serial.print(press_act); Serial.print(" hPa HUM : "); Serial.print(hum_act); Serial.println(" %"); delay(1000); //AirCasting Output (the AB2 is a PMS 7003 sensor as well, though the PA II is a 5003) mySerial.print(dataA.pm25_standard); mySerial.print(";PuprpleAirII;PMS5003-2.5A;Particulate Matter;PM 2.5;Micrograms Per Meter Cubed;ug/m^3;0;12;35;55;150"); mySerial.print("\n"); /* Taking out the 10 output for now mySerial.print(dataA.pm100_standard); mySerial.print(";PuprpleAirII;PMS5003-10A;Particulate Matter;PM 10;Micrograms Per Meter Cubed;ug/m^3;0;12;35;55;150"); mySerial.print("\n"); */ mySerial.print(fah_act); mySerial.print(";BME280T;BME280T;Temperature;Temp;deg F; deg F;0;32;50;70;120;"); mySerial.print("\n"); mySerial.print(hum_act); mySerial.print(";BME280H;BME280H;Humidity;Hum;%;%;0;25;50;75;100;"); mySerial.print("\n"); } if (readPMSdataB(&pmsSerialB)) { // reading data was successful! Serial.println(); Serial.println("B-------------------------------------B"); Serial.println("Concentration Units (standard)"); Serial.print("PM 1.0: "); Serial.print(dataB.pm10_standard); Serial.print("\t\tPM 2.5: "); Serial.print(dataB.pm25_standard); Serial.print("\t\tPM 10: "); Serial.println(dataB.pm100_standard); Serial.println("---------------------------------------"); Serial.println("Concentration Units (environmental)"); Serial.print("PM 1.0: "); Serial.print(dataB.pm10_env); Serial.print("\t\tPM 2.5: "); Serial.print(dataB.pm25_env); Serial.print("\t\tPM 10: "); Serial.println(dataB.pm100_env); Serial.println("---------------------------------------"); Serial.print("Particles > 0.3um / 0.1L air:"); Serial.println(dataB.particles_03um); Serial.print("Particles > 0.5um / 0.1L air:"); Serial.println(dataB.particles_05um); Serial.print("Particles > 1.0um / 0.1L air:"); Serial.println(dataB.particles_10um); Serial.print("Particles > 2.5um / 0.1L air:"); Serial.println(dataB.particles_25um); Serial.print("Particles > 5.0um / 0.1L air:"); Serial.println(dataB.particles_50um); Serial.print("Particles > 50 um / 0.1L air:"); Serial.println(dataB.particles_100um); Serial.println("B-------------------------------------B"); //AirCasting Output (the AB2 is a PMS 7003 sensor as well, though the PA II is a 5003) mySerial.print(dataB.pm25_standard); mySerial.print(";PuprpleAirII;PMS5003-2.5B;Particulate Matter;PM 2.5;Micrograms Per Meter Cubed;ug/m^3;0;12;35;55;150"); mySerial.print("\n"); /* Taking out the 10 output for now mySerial.print(dataB.pm100_standard); mySerial.print(";PuprpleAirII;PMS5003-10B;Particulate Matter;PM 10;Micrograms Per Meter Cubed;ug/m^3;0;12;35;55;150"); mySerial.print("\n"); */ } } boolean readPMSdataA(Stream *s) { if (! s->available()) { return false; } // Read a byte at a time until we get to the special '0x42' start-byte if (s->peek() != 0x42) { s->read(); return false; } // Now read all 32 bytes if (s->available() < 32) { return false; } uint8_t buffer[32]; uint16_t sum = 0; s->readBytes(buffer, 32); // get checksum ready for (uint8_t i=0; i<30; i++) { sum += buffer[i]; } /* debugging for (uint8_t i=2; i<32; i++) { Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", "); } Serial.println(); */ // The data comes in endian'd, this solves it so it works on all platforms uint16_t buffer_u16[15]; for (uint8_t i=0; i<15; i++) { buffer_u16[i] = buffer[2 + i*2 + 1]; buffer_u16[i] += (buffer[2 + i*2] << 8); } // put it into a nice struct :) memcpy((void *)&dataA, (void *)buffer_u16, 30); if (sum != dataA.checksum) { Serial.println("Checksum failure"); return false; } // success! return true; } boolean readPMSdataB(Stream *s) { if (! s->available()) { return false; } // Read a byte at a time until we get to the special '0x42' start-byte if (s->peek() != 0x42) { s->read(); return false; } // Now read all 32 bytes if (s->available() < 32) { return false; } uint8_t buffer[32]; uint16_t sum = 0; s->readBytes(buffer, 32); // get checksum ready for (uint8_t i=0; i<30; i++) { sum += buffer[i]; } /* debugging for (uint8_t i=2; i<32; i++) { Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", "); } Serial.println(); */ // The data comes in endian'd, this solves it so it works on all platforms uint16_t buffer_u16[15]; for (uint8_t i=0; i<15; i++) { buffer_u16[i] = buffer[2 + i*2 + 1]; buffer_u16[i] += (buffer[2 + i*2] << 8); } // put it into a nice struct :) memcpy((void *)&dataB, (void *)buffer_u16, 30); if (sum != dataB.checksum) { Serial.println("Checksum failure"); return false; } // success! return true; } //BME280 functions void readTrim() { uint8_t data[32],i=0; Wire.beginTransmission(BME280_ADDRESS); Wire.write(0x88); Wire.endTransmission(); Wire.requestFrom(BME280_ADDRESS,24); while(Wire.available()){ data[i] = Wire.read(); i++; } Wire.beginTransmission(BME280_ADDRESS); Wire.write(0xA1); Wire.endTransmission(); Wire.requestFrom(BME280_ADDRESS,1); data[i] = Wire.read(); i++; Wire.beginTransmission(BME280_ADDRESS); Wire.write(0xE1); Wire.endTransmission(); Wire.requestFrom(BME280_ADDRESS,7); while(Wire.available()){ data[i] = Wire.read(); i++; } dig_T1 = (data[1] << 8) | data[0]; dig_T2 = (data[3] << 8) | data[2]; dig_T3 = (data[5] << 8) | data[4]; dig_P1 = (data[7] << 8) | data[6]; dig_P2 = (data[9] << 8) | data[8]; dig_P3 = (data[11]<< 8) | data[10]; dig_P4 = (data[13]<< 8) | data[12]; dig_P5 = (data[15]<< 8) | data[14]; dig_P6 = (data[17]<< 8) | data[16]; dig_P7 = (data[19]<< 8) | data[18]; dig_P8 = (data[21]<< 8) | data[20]; dig_P9 = (data[23]<< 8) | data[22]; dig_H1 = data[24]; dig_H2 = (data[26]<< 8) | data[25]; dig_H3 = data[27]; dig_H4 = (data[28]<< 4) | (0x0F & data[29]); dig_H5 = (data[30] << 4) | ((data[29] >> 4) & 0x0F); dig_H6 = data[31]; } void writeReg(uint8_t reg_address, uint8_t data) { Wire.beginTransmission(BME280_ADDRESS); Wire.write(reg_address); Wire.write(data); Wire.endTransmission(); } void readData() { int i = 0; uint32_t data[8]; Wire.beginTransmission(BME280_ADDRESS); Wire.write(0xF7); Wire.endTransmission(); Wire.requestFrom(BME280_ADDRESS,8); while(Wire.available()){ data[i] = Wire.read(); i++; } pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4); temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4); hum_raw = (data[6] << 8) | data[7]; } signed long int calibration_T(signed long int adc_T) { signed long int var1, var2, T; var1 = ((((adc_T >> 3) - ((signed long int)dig_T1<<1))) * ((signed long int)dig_T2)) >> 11; var2 = (((((adc_T >> 4) - ((signed long int)dig_T1)) * ((adc_T>>4) - ((signed long int)dig_T1))) >> 12) * ((signed long int)dig_T3)) >> 14; t_fine = var1 + var2; T = (t_fine * 5 + 128) >> 8; return T; } unsigned long int calibration_P(signed long int adc_P) { signed long int var1, var2; unsigned long int P; var1 = (((signed long int)t_fine)>>1) - (signed long int)64000; var2 = (((var1>>2) * (var1>>2)) >> 11) * ((signed long int)dig_P6); var2 = var2 + ((var1*((signed long int)dig_P5))<<1); var2 = (var2>>2)+(((signed long int)dig_P4)<<16); var1 = (((dig_P3 * (((var1>>2)*(var1>>2)) >> 13)) >>3) + ((((signed long int)dig_P2) * var1)>>1))>>18; var1 = ((((32768+var1))*((signed long int)dig_P1))>>15); if (var1 == 0) { return 0; } P = (((unsigned long int)(((signed long int)1048576)-adc_P)-(var2>>12)))*3125; if(P<0x80000000) { P = (P << 1) / ((unsigned long int) var1); } else { P = (P / (unsigned long int)var1) * 2; } var1 = (((signed long int)dig_P9) * ((signed long int)(((P>>3) * (P>>3))>>13)))>>12; var2 = (((signed long int)(P>>2)) * ((signed long int)dig_P8))>>13; P = (unsigned long int)((signed long int)P + ((var1 + var2 + dig_P7) >> 4)); return P; } unsigned long int calibration_H(signed long int adc_H) { signed long int v_x1; v_x1 = (t_fine - ((signed long int)76800)); v_x1 = (((((adc_H << 14) -(((signed long int)dig_H4) << 20) - (((signed long int)dig_H5) * v_x1)) + ((signed long int)16384)) >> 15) * (((((((v_x1 * ((signed long int)dig_H6)) >> 10) * (((v_x1 * ((signed long int)dig_H3)) >> 11) + ((signed long int) 32768))) >> 10) + (( signed long int)2097152)) * ((signed long int) dig_H2) + 8192) >> 14)); v_x1 = (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * ((signed long int)dig_H1)) >> 4)); v_x1 = (v_x1 < 0 ? 0 : v_x1); v_x1 = (v_x1 > 419430400 ? 419430400 : v_x1); return (unsigned long int)(v_x1 >> 12); }  Is this a question? Click here to post it to the Questions page. Reply to this comment... Thanks, everyone! Reply to this comment... There is another version of this sensor with the same specifications and the same price but it is about half the size. Does anyone know any reason not to use this one instead? It is the PMS7003 instead of PMS5003. Both are available on eBay for$20.

https://www.amazon.com/Lychee-PLANTOWER-PMS7003-Digital-Concentration/dp/B075WR5YKL/

Chris

Is this a question? Click here to post it to the Questions page.

Here is a page about the PMS5003 and 7003 ... https://aqicn.org/sensor/pms5003-7003/

The difference seems to be in their internal layout. In that link they show a 5003 opened after several months outdoor and the light sensor seems to be "dust free" which the 1003 suffered from ... don't know the internal layout of the 7003 but unless you're "size constrained" I'd go with the ones that have been shown to keep themselves clean inside.