Rediscovery & recollection

I had to dig out the soldering iron for a repair job the other day and, while I was there, rediscovered my Honeywell HPMA115S0-XXX particulate matter sensor. I got it about a year and a half ago because it was well reviewed and seemed like a good way to get a calibrated device at relatively low cost from a reputable manufacturer. However, after some hard-won initial success my enthusiasm waned. The sensor requires a 5v power supply, but the logic is 3.3v, which means you can't use a regular Arduino Uno with it without a logic voltage converter. At the time I was tinkering with it the Arduino solution to 3.3v logic seemed to be "buy an Arduino Due". The sensor also seemed to be rather temperamental (read: unreliable) even once a method to arrive at 3.3v logic was in use, with apparently random hard lock-ups during normal operation, and hit-and-miss initialisation. My own review would have been 2/5 stars: "Promises much, but reality is complicated."

IIRC I managed to get the sensor working, unreliably, with a NodeMCU device (which has 3.3v logic input/output), but I was rolling that device out for an exterior sensor setup (it now drives my SDS011) which meant that configuration was short lived.

The Honeywell HPMA115S0-xxx particulate matter sensor

All this went through my mind as I pulled the little black box out from underneath a breadboard and a partially wired up Uno rev 3. I was not super confident about the prospects of success using the sensor, but I wondered how much online open source support for the HPMA115S0-XXX had changed in the last 18 months.

Research & reinvigoration

A spot of Googling later and I was browsing the Electronza website, specifically this page talking about how to get the logic voltage converted, and, even more excitingly, with details of newly revised and updated control code for the HPMA device. They have a very clear and simple wiring diagram. Helpfully they also have a recommended part for the voltage conversion. Electronza recommend the Sparkfun BOB-12009, but Amazon.co.uk didn't have those on next-day delivery so I got these Cylewet 2 Channel Logic Level Converters instead, which seem to work fine. An encouraging start!

A Cylewet 2 channel logic voltage level converter

Roaming requirement

I already have an external PM sensor running, but the idea of running a portable detector that I can use in different locations or when travelling seemed like something very useful. Personal exposure to PM is, after all, the key thing.

I have a USB power bank, and a 16x2 LCD display, so I was hoping that once the Arduino was programmed it would be possible to run the sensor autonomously and read off data as I go.

Raw readouts

Programming the Arduino for this project was all about mashing up public domain code for the different parts into something that worked for me. The resulting code, below, is also public domain.

The HPMA code I got via the Electronza page here, although the actual code is hosted on Github here. As will be apparent from my code below, I went with the library option, rather than cutting and pasting all the HPMA control code into my little mashup.

And the LCD 16x2 code I got from the Arduino site here.

These pieces of code were cut-and-pasted and then interleaved into a beautifully choreographed algorithm. But I did have to alter the pins used for the "SoftSerial" interface for the HPMA sensor because they were used by the display. I specified 8 and 9 in my code, but if that offends your aesthetic sensibilities, or your wires don't stretch that far, then feel free to swap them to something else.

I adjusted the period between readings down to 10 seconds. I also added in a counter, prefixed with "x" on the second line of the LCD display, to incremement by one with every reading. This serves as a heartbeat, or sign of life, to reassure operators that things are still working even if the readings are not changing. The counter has space for four digits on the LCD screen, which means a maximum of 9,999 readings (i.e. just over 27 hrs and 45 mins runtime) before it breaks. The device will not stop taking readings at that point, but the counter will only show the first four digits of the counter value - the result being that the heartbeat will not be so fast!

One other change worth noting is that I convert the readings from float to int types. The reasoning being firstly that the HPMA115S0 only reports values of whole numbers, so the decimal place accuracy in float is unnecessary, and secondly if you lcd.print() a float value it prints the decimal point and two decimal places too; using lcd.print() with an int only prints the integer value. Space is at a premium on a 16x2 LCD screen, so int it is.

Without further ado, here is the code:

// Honeywell HPMA sensor reporting PM2.5 and PM10 readings
// via serial and 16x2 LCD screen for mobile use.
// https://e.brx.io 20191230
// Using public domain code from:
//     https://electronza.com/arduino-uno-pm2-5-sensing-revisited/
//     https://www.arduino.cc/en/Tutorial/HelloWorld

// ---------------------------------
// HPMA115S0 dust sensor example
// for use with devices with SoftSerial
// on boards like Arduino Uno
//
// IMPORTANT!!!
// When using Arduino Uno boards, 
// a logic level converter for 
// TX and RX lines is mandatory
// (I used BOB-12009 from Sparkfun)
// (e.brx.io used Cylewet 2 Channel Logic Level Converter from Amazon.co.uk)
// ---------------------------------

#include <hpma115s0.h>
#include <SoftwareSerial.h>
#include <LiquidCrystal.h>

bool my_status;
float fp25;
float fp10;
int   p25;
int   p10;
int   counter;

SoftwareSerial mySerial(8, 9); // RX, TX
HPMA115S0 my_hpm(mySerial);

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
  Serial.println ("HPMA115S0 software serial example");

  delay(100);
 
   // Stop autosend
  my_status = my_hpm.stop_autosend(); 
  if (my_status == 1){
    Serial.println("Autosend disabled");
  }
  else{
  Serial.print("Error");  
  }
  delay(500);
  
  // Start fan (and measurement mode)
  my_status = my_hpm.start_measurement();  
  if (my_status == 1){
    Serial.println("Start Particle Measurement");
  }
  else{
  Serial.print("Error");  
  }
  delay(5000);

  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.clear();
  lcd.print("Mobile PM Sensor");
}
 
 
void loop() {
  // Read the particle data every fifteen seconds
  my_status = my_hpm.read(&fp25,&fp10); 
  if (my_status == 1){
    p25 = int(fp25);
    p10 = int(fp10);
    Serial.print("PM2.5 value is ");
    Serial.println(fp25);
    Serial.print("PM10 value is ");
    Serial.println(fp10);
    Serial.println(" ");
    lcd.clear();
    lcd.print("PM2.5 ");
    lcd.print(p25);
    lcd.setCursor(0,1);
    lcd.print("PM10  ");
    lcd.print(p10);
  }
  else{
  Serial.println("Measurement fail");
  lcd.setCursor(0,0);
  lcd.print("Measurement fail");
  } 

  // Wait 15 seconds
  counter = counter + 1;
  lcd.setCursor(11,1);
  lcd.print("x");
  lcd.print(counter);
  delay(10000);
}

Rechargable range

I have no idea about the power draw of this particular setup, however, my 5 year-old Anker 15,000mAh USB battery pack ran for about 20 hours and took the charge levels from about 75% to 35%. Some quick basic maths says we're using about 0.3A max, which seems high. The HPMA115S0 says it draws 0.02A at 5V. That leaves 0.28A for the display, voltage converter and Arduino Uno. An Uno (rev 3) board apparently draws about 50mA. The backlight on the LCD can wolf down up to 160mA, and the LCD driver itself eats about 20mA. Subtracting those estimates leaves 50mA unaccounted for. I'm sure that error bars in these figures and inefficiencies in an old battery pack more than make up for that. So maybe 0.3A is not so high after all.

Reflection and reconstruction?

There are a lot of wires. It would be nice to have them tucked away, and for portablility having everything safely installed in a box would be great. That's a a project for another time, or another person. For now, I am happy to have relatively cheap and cheerful mobile capability. I'm still not convinced by the accuracy or consistency of the HPMA115S0-XXX. But then again I'm not sure that mobility in this way was part of its design specification, so I'm willing to cut it some slack.

For a new build I think I would be inclined to go for an SDS011-type sensor, they are more widely used and therefore the values/behaviour is more directly comparable with a lot of online public data (e.g. Luftdaten).

WiFi? This would be useful in some ways, but how do you log the location? The real killer feature set would be WiFi + GPS + log to InfluxDB. That's a bit beyond an Arduino Uno though. More like a NodeMCU, or Raspberry Pi-based solution. All of which requires more time and money. I'm sure that someone out there will have put something together along those lines already. For now I'm focussed on putting what I have to good use and working out lowering the interior PM levels at home.