A comparative test of two particle sensors

Teodor Costachioiu
Mar 22, 2019 · 6 min read
Image for post
Image for post

Just a few days go another PM2.5 and PM10 particle sensor has landed on my desk. This time is an SDS011, a sensor manufactured by Nova Fitness, a spin-off from the University of Jinan (in Shandong). It’s quite a popular sensor, being used in many citizen science projects, of which I mention the global air quality map maintained by luftdaten.info.

I bought it with the intent to use it to make a luftdaten.info monitoring station (and blog about it, of course). But first I will play with it for a few days, to see what it can do.

Image for post
Image for post
SDS011 particle sensor

And I also already have an HPMA115S0 sensor from Honeywell, it makes sense to put the two sensors together and compare their performance.

Image for post
Image for post

As a quick comparison between the two sensors we find a lot of things in common:

  • both sensors use Laser-based light scattering particle sensing
  • both sensors measure PM2.5 and PM10
  • both sensors have a range of 0 μg/m3to 1,000 μg/m3
  • both sensors use serial communication to send particle data
  • both sensors require 5V power
  • the current draw is 100mA for SDS011 and 80mA for HPMA115S0
  • both sensors use 3.3V logic levels

There are also some differences:

  • each sensor comes with its communication protocol over UART
  • SDS011 has a relative error of 10%
  • HPMA115S0 has an accuracy of ±15%
  • SDS011 returns dust data with two decimals
  • HPMA115S0 returns data as an integer, with no decimals
  • SDS011 sends dust data every second
  • HPMA115S0 can perform measurements on demand
  • Average live of SDS011 is 8000 hours in continuous mode
  • HPMA115S0 will last for 20,000 hr (continuous mode)
  • SDS011 has a hose connector for the air intake

To make a comparison of the two sensors I will connect both sensors to an Arduino Due board, and I will log the dust data on SD card.

The board of my choice is a Flip & Click from MikroElektronika, which I variant of an Arduino Due, plus four mikroBUS sockets. The reason for choosing the Arduino Due platform are:

  • the board provides 5V power and has native 3.3V logic levels
  • I already have a microSD click board in my inventory
  • for the HPMA115S0 I have an adapter made from a PROTO click, which I have used in my first HPMA115S0 project.

Of course, one can use an Arduino Due, a breadboard and a generic SD card adapter with the same results. Just respect the wiring in my project, as follows:

  • SDS011 uses Serial1
  • HPMA115S0 uses Serial2
  • The CS line for the SD card is digital pin 52
  • The project used one diagnostics LED connected to digital pin 40. You can skip it if you wish.

As for the software, I used the following libraries:

The library for SDS011 appears as being incompatible with the Arduino Due. Trust me; it works fine for this blog post.

For the HPMA11sS0 I used my own library, described here.

Arduino code for sensors comparison

The code I propose in this blog post reads the output of the two sensors and writes the data on an SD card in CSV format. On each line in the log file we will write:

  • the current value of millis()
  • PM2.5 data returned by SDS011
  • PM2.5 data returned by HPMA115S0
  • PM10 data returned by SDS011
  • PM10 data returned by HPMA115S0

The code is written in such way that the log file is opened before every log entry, then closed. Thus, the risk of corrupting the logged data if greatly reduced.

A new line in the log file is added at an interval specified by the log_delay variable. In my code I set log_delay = 15000; thus a new line is added every 15 seconds.

However, this approach comes with a quirk: the SDS011 sends data every second. Turning off the sensor between measurements is not a good option; the sensor needs to suck fresh air to provide meaningful results. This is especially important if a hose is used on the air intake.

So, in the code, for a time interval equal to log_delay I will just read the data from the SDS011 and throw it away, I will keep only the readings that occur at log_delay intervals.

For the HPMA115So things are simpler. I only have to disable auto send mode, configure the sensor in measurement mode (active, the fan is on), and then trigger measurements on demand.

// SDS011 dust sensor example
// for use with additional Serial ports
// like Arduino Mega or senseBox MCU
// -----------------------------

#include <SPI.h>
#include <SD.h>
#include <SDS011-select-serial.h>
#include <hpma115s0.h>

Sd2Card card;
SdVolume volume;
SdFile root;
// Flip & Click socket C
const int SD_CS = 52;
String logString;

// Debugging
#define LED_PIN 40

// SDS011
float p10,p25;
int error;
unsigned long log_delay;
unsigned long last_time;

// HPMA115S0
bool my_status;
float p25_2;
float p10_2;

SDS011 my_sds(Serial1);
HPMA115S0 my_hpm(Serial2);

void setup() {
// initialize normal Serial port
// initalize SDS Serial Port
// initalize HPMA1125S0 Serial Port

// Used by the debugging LED

// SD card
// SPI: first begin, then set parameters

Serial.print("Initializing SD card...");
// see if the card is present and can be initialized:
if (!SD.begin(SD_CS)) {
Serial.println("Card failed, or not present");
// don't do anything more:

// Flash the led if card is initialized
Serial.println("card initialized.");

// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
File dataFile = SD.open("datalog.csv", FILE_WRITE);

// Disable HPMA115S0 autosend mode
my_status = my_hpm.stop_autosend();
if (my_status == 1){
Serial.println("Autosend disabled");
// HPMA115S0 start fan (and measurement mode)
my_status = my_hpm.start_measurement();
if (my_status == 1){
Serial.println("Start Particle Measurement");
// We are ready to go
last_time = millis();
// log data every 15 seconds
log_delay = 15000;

void loop() {
while( millis() < (last_time + log_delay)){
// throw away anything that comes from SDS011
error = my_sds.read(&p25,&p10);
last_time = millis();
// Read SDS 011
error = my_sds.read(&p25,&p10);
Serial.println("Error is "+String(error));
if (! error) {
Serial.println("P2.5-SDS: "+String(p25));
Serial.println("P10-SDS: "+String(p10));
// Read HPMA115S0
my_status = my_hpm.read(&p25_2,&p10_2);
if (my_status == 1){
Serial.println("P2.5-HPM: "+String(p25_2));
Serial.println("P10-HPM: "+String(p10_2));
Serial.println(" ");
// String format: millis, PM2.5-SDS , PM2.5-HPM , PM10-SDS, PM10-HPM
logString = "";
logString += String(millis());
logString += ",";
logString += String(p25);
logString += ",";
logString += String(p25_2);
logString += ",";
logString += String(p10);
logString += ",";
logString += String(p10_2);

// write data to sd card
File dataFile3 = SD.open("datalog.csv", FILE_WRITE);
if (dataFile3) {
// print to the serial port too:
// Serial.println(logString);


This how the log file should look:

Image for post
Image for post

Now all I have to do is wait for a day or so for enough data to accumulate, and then to perform the comparison of the two sensors. A follow-up is coming soon…

Originally published at https://electronza.com on March 22, 2019. Moved to MEdium on May 6, 2020.


DIY electronics projects and more

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store