--> Arduino Uno: PM2.5 sensing revisited | Electronza

Arduino Uno: PM2.5 sensing revisited

Today it’s finally the time to revisit the blog post on using the Honeywell HPMA115S0 particle sensor with an Arduino Uno board. The basics of the operation of the HPMA115S0 and a preliminary version of this code that works with Arduino Due are published in this blog post, so please take a few minutes and read it.

After publishing that blog post, so many of you have asked me to come with a way to make this sensor work with an Arduino Uno. It’s relatively easy to do it, but one must overcome two minor issues:

Albeit the HPMA115S0 is powered from a 5V supply, it uses 3.3V logic. To make it work with an Arduino Uno, one must use a logic level converter. In this blog post, I used one BOB-12009 from Sparkfun.

The Arduino Uno has a single serial port, which is commonly used to communicate with the PC. The only way to make the PM2.5 sensor work with the Arduino Uno is to use SoftwareSerial, with some minor changes in the code and some quirks that are explained below.

Connections and schematic


The wires used in the blog post to connect the sensor were all red colored. Getting the right way to connect things was a bit difficult for some people, so I tried to make things a bit easier this time.

In the above picture, we can see the sensor with its connector. The pin numbering is a bit odd, with PIN1 near the fan and PIN8 to the edge of the sensor. Only four pins are used in this project: Vcc, RX, TX, and GND.

The wires are still all red, but I’ve put some colored heat shrink tubing on some wires to increase visibility:

  • red is Vcc
  • yellow is TX — this goes to SoftwareSerial RX pin
  • blue is RX — this goes to SoftwareSerial TX pin
  • black is GND


The sensor is connected as follows:

Honeywell PM2.5 sensor Arduino Uno connections. Created with Fritzing.org

We can see that the logic level converter receives both 5V (red wire) and 3.3V (orange wire) from the Arduino board. GND is connected both to the PM2.5 sensor and to the logic level converter. The sensor is powered from 5V (red wire going to pin2 of the sensor).

Pin11 of the Arduino Uno is SoftwareSerial TX pin and will go to the HV4 pin of the logic level converter. The opposite LV4 pin goes to pin7 of the HPMA115S0 sensor,

Pin10 of the Arduino Uno is SoftwareSerial RX pin and will go to the HV3 pin of the logic level converter. The opposite LV3 pin goes to pin6 (TX) of the HPMA115S0 sensor.

Beloe there are some pictures of the sensor connected to an Arduino Uno:







Arduino Uno code

The code is a variation of the code in the original blog post, but now it uses Software Serial.

// The big change: we use software serial for H2S sensor

#include <SoftwareSerial.h>
// software serial #1: RX = digital pin 10, TX = digital pin 11
SoftwareSerial portOne(10, 11);

bool my_status;

// IMPORTANT!!! When working on an Arduino DUE,
// int is 32 bit (-2,147,483,648 to 2,147,483,647)
// For Arduino Uno int size is 8 bit, that is -32,768 to 32,767
// Use long or float if working with an Uno or simmilar 8-bit board
long PM25;
long PM10;

void setup() {
Serial.begin(9600);
while (!Serial);
Serial.println("Start!");
portOne.begin(9600);
//Just discard everything the sensor will send at the beginning
flush_portOne();

// Stop autosend
Serial.println("Stopping autosend...");
my_status = stop_autosend();
// Serial print is used just for debugging
// But one can design a more complex code if desired
Serial.print("Stop autosend status is ");
Serial.println(my_status, BIN);
Serial.println(" ");
delay(500);


// Start fan
Serial.println("Starting measurements...");
my_status = start_measurement();
// Serial print is used just for debugging
// But one can design a more complex code if desired
Serial.print("Start measurement status is ");
Serial.println(my_status, BIN);
Serial.println(" ");
delay(5000);
}


void loop() {
// Read the particle data every minute
my_status = read_measurement();
// Serial print is used just for debugging
// But one can design a more complex code if desired
Serial.print("Read measurement status is ");
Serial.println(my_status, BIN);
Serial.print("PM2.5 value is ");
Serial.println(PM25, DEC);
Serial.print("PM10 value is ");
Serial.println(PM10, DEC);
Serial.println(" ");
// Wait some time
delay(10000);
}

bool start_measurement(void)
{
// First, we send the command
byte start_measurement[] = {0x68, 0x01, 0x01, 0x96 };
portOne.write(start_measurement, sizeof(start_measurement));
//Then we wait for the response
while(portOne.available() < 2);
uint8_t read1 = portOne.read();
uint8_t read2 = portOne.read();
// Test the response
if ((read1 == 0xA5) && (read2 == 0xA5)){
// ACK
return 1;
}
else if ((read1 == 0x96) && (read2 == 0x96))
{
// NACK
return 0;
}
else return 0;
}

bool stop_measurement(void)
{
// First, we send the command
byte stop_measurement[] = {0x68, 0x01, 0x02, 0x95 };
portOne.write(stop_measurement, sizeof(stop_measurement));
//Then we wait for the response
while(portOne.available() < 2);
uint8_t read1 = portOne.read();
uint8_t read2 = portOne.read();
// Test the response
if ((read1 == 0xA5) && (read2 == 0xA5)){
// ACK
return 1;
}
else if ((read1 == 0x96) && (read2 == 0x96))
{
// NACK
return 0;
}
else return 0;
}

bool read_measurement (void)
{
// Send the command 0x68 0x01 0x04 0x93
byte read_particle[] = {0x68, 0x01, 0x04, 0x93 };
portOne.write(read_particle, sizeof(read_particle));
// A measurement can return 0X9696 for NACK
// Or can return eight bytes if successful
// We wait for the first two bytes
while(portOne.available() < 1);
byte HEAD = portOne.read();
while(portOne.available() < 1);
byte LEN = portOne.read();
// Test the response
if ((HEAD == 0x96) && (LEN == 0x96)){
// NACK
Serial.println("NACK");
return 0;
}
else if ((HEAD == 0x40) && (LEN == 0x05))
{
// The measuremet is valid, read the rest of the data
// wait for the next byte
while(portOne.available() < 1);
byte COMD = portOne.read();
while(portOne.available() < 1);
byte DF1 = portOne.read();
while(portOne.available() < 1);
byte DF2 = portOne.read();
while(portOne.available() < 1);
byte DF3 = portOne.read();
while(portOne.available() < 1);
byte DF4 = portOne.read();
while(portOne.available() < 1);
byte CS = portOne.read();
// Now we shall verify the checksum
if (((0x10000 - HEAD - LEN - COMD - DF1 - DF2 - DF3 - DF4) % 0XFF) != CS){
Serial.println("Checksum fail");
return 0;
}
else
{
// Checksum OK, we compute PM2.5 and PM10 values
PM25 = DF1 * 256 + DF2;
PM10 = DF3 * 256 + DF4;
return 1;
}
}
}

bool stop_autosend(void)
{
// Stop auto send
byte stop_autosend[] = {0x68, 0x01, 0x20, 0x77 };
portOne.write(stop_autosend, sizeof(stop_autosend));
//Then we wait for the response
while(portOne.available() < 2);
uint8_t read1 = portOne.read();
uint8_t read2 = portOne.read();
// Test the response
if ((read1 == 0xA5) && (read2 == 0xA5)){
// ACK
return 1;
}
else if ((read1 == 0x96) && (read2 == 0x96))
{
// NACK
return 0;
}
else return 0;
}

bool start_autosend(void)
{
// Start auto send
byte start_autosend[] = {0x68, 0x01, 0x40, 0x57 };
portOne.write(start_autosend, sizeof(start_autosend));
//Then we wait for the response
while(portOne.available() < 2);
uint8_t read1 = portOne.read();
uint8_t read2 = portOne.read();
// Test the response
if ((read1 == 0xA5) && (read2 == 0xA5)){
// ACK
return 1;
}
else if ((read1 == 0x96) && (read2 == 0x96))
{
// NACK
return 0;
}
else return 0;
}

void flush_portOne(void){
uint8_t inchar;
uint8_t count = 0;
bool is_timeout = 0;
// We set a 10 second timeout;
unsigned long timeout = millis() + 10000;
Serial.println ("Waiting for the first autosend.");
while(portOne.available() < 1){
if (millis() > timeout){
Serial.println("Timeout");
break;
}
}
// Do we have data in the serial buffer?
// If so, flush it
if (portOne.available() > 0){
Serial.println ("Flushing buffer...");
// A data frame is 32 bytes
while(count < 32){
inchar = portOne.read();
count++;
delay(10);
Serial.print(inchar, HEX);
}
Serial.println("");
}
}
One might observe the change in the flush_portOne routine, which now expects an array of 32 bytes. As the sensor doesn’t issue any string terminator. A 10-second timeout was also implemented, unlike in the original code. This routine is required as the sensor starts in auto-send mode, and we want to put in in manual mode.

Even with those changes, there are some minor quirks and unsolved things. In particular, when the reset button of the Arduino Uno is pressed, the sensor becomes unresponsive. I assume this has something to do with the way SoftwareSerial initializes, as this won’t happen with hardware serial. The only way to make the sensor work is to briefly remove 5V power from the sensor and reapply it before the timeout expires.

[Update March 19, 2019] I wrote a code library for the sensor, you can download it from https://github.com/Electronza/HPMA115S0.

Name

Air quality,1,Arduino code library,2,Arduino projects,12,Casual stuff,1,ESP8266,2,PIC projects,1,Review,14,Tutorial,11,
ltr
item
Electronza: Arduino Uno: PM2.5 sensing revisited
Arduino Uno: PM2.5 sensing revisited
https://1.bp.blogspot.com/-AHoq83Yc_e0/YFC5KX4TacI/AAAAAAAABkQ/XBIfpe8oSCI-nNgaY729aQnRhNoofXTmwCPcBGAYYCw/w640-h480/sensor-honeywell-arduino.jpg
https://1.bp.blogspot.com/-AHoq83Yc_e0/YFC5KX4TacI/AAAAAAAABkQ/XBIfpe8oSCI-nNgaY729aQnRhNoofXTmwCPcBGAYYCw/s72-w640-c-h480/sensor-honeywell-arduino.jpg
Electronza
https://www.electronza.com/2018/11/arduino-uno-pm25-sensing-revisited.html
https://www.electronza.com/
https://www.electronza.com/
https://www.electronza.com/2018/11/arduino-uno-pm25-sensing-revisited.html
true
4275662725798500575
UTF-8
Loaded All Posts Not found any posts VIEW ALL Readmore Reply Cancel reply Delete By Home PAGES POSTS View All RECOMMENDED FOR YOU LABEL ARCHIVE SEARCH ALL POSTS Not found any post match with your request Back Home Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sun Mon Tue Wed Thu Fri Sat January February March April May June July August September October November December Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec just now 1 minute ago $$1$$ minutes ago 1 hour ago $$1$$ hours ago Yesterday $$1$$ days ago $$1$$ weeks ago more than 5 weeks ago Followers Follow THIS PREMIUM CONTENT IS LOCKED STEP 1: Share to a social network STEP 2: Click the link on your social network Copy All Code Select All Code All codes were copied to your clipboard Can not copy the codes / texts, please press [CTRL]+[C] (or CMD+C with Mac) to copy Table of Content