Extracting PM2.5 data from new Honeywell HPM Particle Sensor via serial UART

Testing the new Honeywell Particulate sensor for a project with local schools. Has advantage over some other sensors of returning a pre-calculated PM2.5 reading, has an on board fan, and only 15ug variance so perfectly good enough for doing a low/med/high/v high assessment.

Full datasheet is here.

Connected it to Arduino Uno via SoftwareSerial.

The datasheet customer use protocol has codes I'm sending to the sensor to request a reading, and I am getting a stream of readings back through the serial monitor. (code is at bottom of post). The protocol also says the format I receive back should be in format:
HEAD (0x40)
LEN (0x05)
CMD (0x04)
DF1
DF2
DF3
DF4
CS (MOD((65536-(HEAD+LEN+CMD+DATA)),256)

With the PM2.5 calculated as DF1*256 + DF2 (and PM10 using DF3 and DF4 via the same)

Data in the monitor looks like this when I run it at present;

66, 77, 0, 28, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255,
0, 0, 81, 0, 0, 255, 66, 77, 0, 28, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 81, 0, 0, 255, 66, 77, 0, 28, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255,
66, 77, 0, 28, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 0, 0, 255,

There's an obvious sequence, and by wafting a candle, I know that the numbers highlighted in red and blue are DF2 and DF4 reads. I don't know which fields have the DF1 and DF3 data in. However, as you can see, the stream doesn't reliably start at the same point, and there are lots of stray zeros. Also, I can't see the HEAD/LEN/CMD in the stream, and am worried by this.

Would be grateful for help with helping me align the data I'm receiving with what I should be getting, so I can reliably extract DF1 and DF2 to put into the output I subsequently have planned.

I am new to extracting data from the serial flow like this (my // notes show my attempts to understand), so apologies that I suspect I am making some rookie serial/byte type errors. I know this sensor will pick up a library in due course, but can't find anything at present, and I can't quite work out the string listening etc. to make it work.

Thanks in advance for help.

Chris


#include "SoftwareSerial.h"
SoftwareSerial mySerial(12,13);  //Using pin 12 for Rx and pin 13 for Tx
 
byte readpart[] = {0x68, 0x01, 0x04, 0x93};  //Listed command to read particle data
byte readstart[] = {0x68, 0x01, 0x04, 0x96}; //Listed command to start particle measurement
byte response[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};  //32 byte array to store returned data
 
void setup()
{
  // put your setup code here, to run once:
  Serial.begin(9600);                                          //Opens the main serial port to communicate with the computer
  mySerial.begin(9600);                                        //Opens the virtual serial port with a baud of 9600
  delay(2000);                                                 //Wait for serial to have started
  Serial.println("Sending particle start reading code");       //Update user that autosend code sent
  mySerial.write(readstart,8);                                 //Request sensor starts particle reading
  delay(6000);                                                 //Allow 6 seconds for sensor response time
  Serial.println("Sensor started");                            //Confirm end of setup code
}
 
void loop()
{ sendRequest(readpart);                                       //Send the read code request
  delay(1000);                                                      
  Serial.println(" ");                                         //New line on serial monitor
  for(int i = 0; i < 32; i++)                                  //Print out the data obtained
              { Serial.print(response[i]);
                Serial.print(", ");
                }}
 
//Sub command that runs inside the main loop
void sendRequest(byte packet[])
{
  while(!mySerial.available())                                  //keep sending request until we start to get a response
{ //mySerial.write(readpart,4);                                 //send command for a reading
    delay(50);
}
  int timeout=0;                                                //set a timeout counter
  while(mySerial.available() < 32)                              //Hold in this loop until we get a 32 bit response
   { timeout++; 
   if(timeout > 10)                                             //wait 10ms to check if we're happy, else reset and wait again
     { while(mySerial.available())                                             
        mySerial.read();                                        //read everything out the buffer to empty it                   
        break;                                                  //exit and try again
     }
    delay(50);
   }
  for (int i=0; i < 32; i++)
{ response[i] = mySerial.read();                                //Read data out the buffer into into the response array
}}

honeywell-sensing-particle-sensors-hpm-series-datasheet-32322550-b-en.pdf (393 KB)

You should not have those extra zeros, so something is wrong with your connection. Furthermore you should be reading a given number of bytes that will end in a correct checksum.

Note that the RX and TX lines are 3.3V ONLY. You should be using this with a 3.3V Arduino.

If you are using a 5V Arduino, do you have a level shifter? You need one at least in the line connecting TX from Arduino to RX on the sensor. A 1k/2K resistive voltage divider should work.

Thanks. I am using a standard 5v arduino (assumed this was needed given vin for sensor).
**update
Have wired in the level shifter as described but still getting exactly the same readouts.

Does that mean that do I have a code problem as well?

You may have damaged the sensor by exposing a 3.3V pin to 5V.

Are the sensor and Arduino grounds connected?

You are not using the sensor correctly. The "start particle measurement" command sends a response, either ACK or NACK, which you should be reading and verifying.

I would debug this using a 3.3V USB-serial TTL adapter and a good terminal program on a PC.

Thanks - will try and get hold of one from family. I've continued tinkering in the meantime, and radically simplified my code (see bottom) to start always reading at the 66..77.. point, as this consistently comes up in the code. Tried with second sensor not exposed to 5v Rx/Tx and getting exactly same output, and also set up smoke chamber to get particles high enough to trip past the 256 figure.

Output is pasted below. My working hypothesis is PM2.5 = (line 105256) + line 106, as this aligns with the experiments I'm doing. PM10 would be 107256 and 108

Having been looking at other sketches pinging these kind of sensors, there seems to be some interesting mixing between uint8_t and uint16_t in the libraries.

Am I missing something silly here around how I'm reading the data into the array? Tried changing the 8_t to 16_t, but that breaks the readbytes function.

100: 77
101: 0
102: 28
103: 0
104: 0
105: 1
106: 192
107: 1
108: 199
109: 0
110: 0
111: 0
112: 0
113: 0
114: 0
115: 0
116: 0
117: 0
118: 0
119: 0
120: 0
121: 0
122: 0
123: 0
124: 0
125: 0
126: 0
127: 81
128: 0
129: 2
130: 133
131: 66 (the first reading, but confirming I've closed the loop)

Wondering if chunking anything into 16 not 8 would get it to look like more like the datasheet, as at the moment, I feel I'm getting a reliable output, but that it doesn't resemble the datasheet one bit (or byte).

Thanks

#include "SoftwareSerial.h"
SoftwareSerial mySerial(12,13);  //Using pin 12 for Rx and pin 13 for Tx
byte autosend[] = {0x68, 0x01, 0x40, 0x57};
 
#define LENG 32   
uint8_t buf[LENG];
int PM2_5Value=0;         //define PM2.5 value

void setup()
{ Serial.begin(9600);       
  mySerial.begin(9600);   
  mySerial.setTimeout(6000);
  delay(2000);
  mySerial.write(autosend,4);  
}

void loop()
{ Serial.println(" ");
 // mySerial.write(readpart,4);
  if(mySerial.find(66)){   
  if(buf[1] = 77){mySerial.readBytes(buf,LENG);
  PM2_5Value=((buf[5]*256) + buf[6]);}
} delay(2000);

  Serial.print("PM2.5 = ");
  Serial.println(PM2_5Value);
for(int i = 0; i < 32; i++)
{ Serial.print(i+100);
  Serial.print(":  ");
  Serial.println(buf[i],DEC);
}}

Hi Chris!

I've just picked up one of these too - did you get any further with your experiments?

Hi,

I did, but my skill level isn't really sufficient to get it reliably reporting for my project. Hope someone else takes up the challenge.

I was successfully extracting the string on just the base board, but then when I added an additional software serial for a GPS sensor, and through an ethernet shield, the numbers shot up into the 100s at the same point, even though nothing else changed. I guess I was reading the bytes wrong, but didn't know where to start fixing it.

My cobbling together wasn't reliable, so have put the sensor on the shelf until someone with more skill at understanding the serial inputs and outputs can come up with a proper library/sketch.

Fingers crossed they do, as it has an on board fan, small variability on the PM2.5 reading, and is commonly available in the US/UK rather than needing an import of one of the Plantower sensors.

Let me know if you have more luck!

Chris

I think Chris' did nothing wrong in programming. I have been playing with Pantower PMS5003 (bought at eBay, shipped from China) and Honeywell HPMA115 (mouser.com) and found out that HPMA115 is actually a reduced function of PMS5003. They probably come from the same designer and even manufacturer. They both output exact 32 bytes of data for each frame (auto output mode, default), with the exact same header identifier 0x42 0x4d, then length, then data, etc, all in exact same byte location. HPMA115 has only PM2.5 and PM10 data, but PMS5003 has much more, including PM1.0, PM10.0, etc. filling all 32 bytes. HPMA115's PM2.5 and PM10 data bytes are in the exact location as in PMS5003, while all other data bytes that PMS5003 would have are zeroed in HPMS115. Good luck in your programming.

If you only need low/med/high indication I'd suggest a IR LED based solution like the SM-PWM-01C we make, which is mainly used in the Chinese Air Purifier market, we've even put some code on Github for it outputting the US EPA Colour Codes for PM2.5.

Later this year we will have our own laser based solution (like the Plantower and Honeywell solutions mentioned), and I've already got the Arduino code so will publish when we launch. SM-UART-01L will be part number, it'll be in Mouser, Farnell et al when launched.

Thanks both - really helpful, and reassuring! Just out of curiosity, do you know if that mean the libraries and sketches available for the PMS5003 work with the Honeywell - would be great to just be able to use it off the shelf? The Honeywell is great just for being able to have a UK supplier, and therefore we can procure it for our project through standard routes.

math1289:
I think Chris' did nothing wrong in programming. I have been playing with Pantower PMS5003 (bought at eBay, shipped from China) and Honeywell HPMA115 (mouser.com) and found out that HPMA115 is actually a reduced function of PMS5003. They probably come from the same designer and even manufacturer. They both output exact 32 bytes of data for each frame (auto output mode, default), with the exact same header identifier 0x42 0x4d, then length, then data, etc, all in exact same byte location. HPMA115 has only PM2.5 and PM10 data, but PMS5003 has much more, including PM1.0, PM10.0, etc. filling all 32 bytes. HPMA115's PM2.5 and PM10 data bytes are in the exact location as in PMS5003, while all other data bytes that PMS5003 would have are zeroed in HPMS115. Good luck in your programming.

We're favouring the fan assisted laser versions at the moment, partly because there's less error in setup and positioning, and because some of the online 'hack' trials seem to suggest they perform better in non standard (e.g. outdoor shielded) environments.

We're launching a schools project in September, so will keep an eye out for the other sensor though, particularly as we may have opportunity to be running a calibration study alongside a true outdoor sensor.

AmphenolSensors:
If you only need low/med/high indication I'd suggest a IR LED based solution like the SM-PWM-01C we make, which is mainly used in the Chinese Air Purifier market, we've even put some code on Github for it outputting the US EPA Colour Codes for PM2.5.

Later this year we will have our own laser based solution (like the Plantower and Honeywell solutions mentioned), and I've already got the Arduino code so will publish when we launch. SM-UART-01L will be part number, it'll be in Mouser, Farnell et al when launched.

Appreciate the help.

I just got one of these sensors also. I found this info at dfrobots on a similiar model. Maybe this could be adapted to the honeywell one?

I am also trying to extract data from a HPM dust sensor but have encountered some other problems than the ones discussed above. Since I am new to Arduino I am not able to figure out whether it is the programming, the wiring or something else that is causing the problem.

I have an Arduino UNO and use the example and library available at Github: GitHub - felixgalindo/HPMA115S0: Arduino Library for Honeywell's Particle Sensor (HPMA115S0-XXX).

I am using a separate 5V battery as power source for the sensor to ensure correct voltage.

I have tried to connect the RX/TX from the sensor to the RX/TX on the board, and pin 10/11 on the board. I have also tried to connect/disconnect the 3.3 V.

When I am using 9600 baud in the serial monitor I get only gibberish, but when I am turning it to 57600 I get readable text saying:

PS- Reading Particle Measurements...
PS- Sending cmd 68 1 4 93
PS- Waiting for cmd resp

And then repeats this every second endlessly and does not change when I change the wiring.

Anyone have a suggestion for what to do overcome this problem?

Interesting. Would your sensor also come with code for Particle Photon?

AmphenolSensors:
If you only need low/med/high indication I'd suggest a IR LED based solution like the SM-PWM-01C we make, which is mainly used in the Chinese Air Purifier market, we've even put some code on Github for it outputting the US EPA Colour Codes for PM2.5.

Later this year we will have our own laser based solution (like the Plantower and Honeywell solutions mentioned), and I've already got the Arduino code so will publish when we launch. SM-UART-01L will be part number, it'll be in Mouser, Farnell et al when launched.

Hi chris
originally i had same problem,,
so I try to stop Auto Send(default seems enabled) 0x68 0x01 0x20 0x77.
then try again start measurement,
he return 165 165 (0xA5 0xA5)

seems when Auto Send is enabled,
you start particle measurement,
he will send "auto send" to you,

The updated the datasheet to version C, see page 6.

chris_chiswell:
Thanks - will try and get hold of one from family. I've continued tinkering in the meantime, and radically simplified my code (see bottom) to start always reading at the 66..77.. point, as this consistently comes up in the code. Tried with second sensor not exposed to 5v Rx/Tx and getting exactly same output, and also set up smoke chamber to get particles high enough to trip past the 256 figure.

Output is pasted below. My working hypothesis is PM2.5 = (line 105256) + line 106, as this aligns with the experiments I'm doing. PM10 would be 107256 and 108

Having been looking at other sketches pinging these kind of sensors, there seems to be some interesting mixing between uint8_t and uint16_t in the libraries.

Am I missing something silly here around how I'm reading the data into the array? Tried changing the 8_t to 16_t, but that breaks the readbytes function.

100: 77
101: 0
102: 28
103: 0
104: 0
105: 1
106: 192
107: 1
108: 199
109: 0
110: 0
111: 0
112: 0
113: 0
114: 0
115: 0
116: 0
117: 0
118: 0
119: 0
120: 0
121: 0
122: 0
123: 0
124: 0
125: 0
126: 0
127: 81
128: 0
129: 2
130: 133
131: 66 (the first reading, but confirming I've closed the loop)

Wondering if chunking anything into 16 not 8 would get it to look like more like the datasheet, as at the moment, I feel I'm getting a reliable output, but that it doesn't resemble the datasheet one bit (or byte).

Thanks

#include "SoftwareSerial.h"

SoftwareSerial mySerial(12,13);  //Using pin 12 for Rx and pin 13 for Tx
byte autosend[] = {0x68, 0x01, 0x40, 0x57};

#define LENG 32 
uint8_t buf[LENG];
int PM2_5Value=0;        //define PM2.5 value

void setup()
{ Serial.begin(9600);     
  mySerial.begin(9600); 
  mySerial.setTimeout(6000);
  delay(2000);
  mySerial.write(autosend,4); 
}

void loop()
{ Serial.println(" ");
// mySerial.write(readpart,4);
  if(mySerial.find(66)){ 
  if(buf[1] = 77){mySerial.readBytes(buf,LENG);
  PM2_5Value=((buf[5]*256) + buf[6]);}
} delay(2000);

Serial.print("PM2.5 = ");
  Serial.println(PM2_5Value);
for(int i = 0; i < 32; i++)
{ Serial.print(i+100);
  Serial.print(":  ");
  Serial.println(buf[i],DEC);
}}

Your code looks okay, 66 is the first byte followed by 77 for the header, 101 and 102 is the MSB and LSB for the length (always 28 at the moment).

You have to take the sum off al the bytes, header + length + 26 data bytes what should correspond with byte 129 (MSB) and 130 (LSB) as an uint16_t.

Has anyone gotten this working yet? I'm having trouble with mine as well. The datasheet on Honeywell's website says it will work with either 5V or 3.3V, I'm not sure why people are saying this isn't the case...

The way I read the datasheet, it has its own on board regulator with 5v being the input. It also quite clearly states that the uart levels are 3.3v. If 5v is applied here I would imagine it would destroy the pin driver.
john

john_m0ers:
The way I read the datasheet, it has its own on board regulator with 5v being the input. It also quite clearly states that the uart levels are 3.3v. If 5v is applied here I would imagine it would destroy the pin driver.
john

I just got another HPMA115S0 and a level shifter to try out. I will keep everyone updated if I get things working!

The level shifter is here.

I am encountering exactly the same problem as described below. Did you already find a solution now, Latsbben?

Latsbben:
I am also trying to extract data from a HPM dust sensor but have encountered some other problems than the ones discussed above. Since I am new to Arduino I am not able to figure out whether it is the programming, the wiring or something else that is causing the problem.

I have an Arduino UNO and use the example and library available at Github: GitHub - felixgalindo/HPMA115S0: Arduino Library for Honeywell's Particle Sensor (HPMA115S0-XXX).

I am using a separate 5V battery as power source for the sensor to ensure correct voltage.

I have tried to connect the RX/TX from the sensor to the RX/TX on the board, and pin 10/11 on the board. I have also tried to connect/disconnect the 3.3 V.

When I am using 9600 baud in the serial monitor I get only gibberish, but when I am turning it to 57600 I get readable text saying:

PS- Reading Particle Measurements...
PS- Sending cmd 68 1 4 93
PS- Waiting for cmd resp

And then repeats this every second endlessly and does not change when I change the wiring.

Anyone have a suggestion for what to do overcome this problem?

I would also like to try the solution as quoted below, but I can't seem to figure out how to disable this 'Auto Send'. Can anyone explain this to me?

blackfreer:
Hi chris
originally i had same problem,,
so I try to stop Auto Send(default seems enabled) 0x68 0x01 0x20 0x77.
then try again start measurement,
he return 165 165 (0xA5 0xA5)

seems when Auto Send is enabled,
you start particle measurement,
he will send "auto send" to you,