I am working with a Shinyei PPD 71 sensor (UART output )and am unable to get the readings . I have a basic understanding of arduino and would like to integrate the sensor with my arduino.
Do you have the UART protocol of that sensor? If not, I suggest to contact the vendor or Shinyei.
Please provide more information including
- data sheet of the device
- board you use
- what you tried so far
type or paste code here#include <HardwareSerial.h>
// Define UART pins and settings
#define RX_PIN 16 // GPIO 16 for UART RX
#define TX_PIN 17 // Not used, placeholder
#define BAUD_RATE 9600 // From datasheet, Section 8
#define FRAME_LENGTH 29 // 29-byte measurement frame, Section 4 (Page 4)
// Frame delimiters and command byte from datasheet
#define STX 0x02 // Start of Text
#define ETX 0x03 // End of Text
#define EOT 0x04 // End of Transmission
#define COMMAND 0x10 // Fixed command value for measurement data
// Use UART2 on ESP32
HardwareSerial SensorSerial(2);
void setup() {
// Start USB Serial for debugging
Serial.begin(115200);
while (!Serial) delay(10);
// Start UART2 for PPD71
SensorSerial.begin(BAUD_RATE, SERIAL_8N1, RX_PIN, TX_PIN);
Serial.println("ESP32 started, waiting for PPD71 stabilization...");
// Wait for sensor stabilization (60 seconds minimum, per Section 5)
delay(60000);
Serial.println("Stabilization complete, reading data...");
}
float parseMeasurementFrame(uint8_t* frame) {
// Check frame delimiters
if (frame[0] != STX || frame[26] != ETX || frame[28] != EOT) {
Serial.println("Invalid frame delimiters");
Serial.printf("STX: 0x%02X, ETX: 0x%02X, EOT: 0x%02X\n", frame[0], frame[26], frame[28]);
return -1.0;
}
// Check command byte (D2, offset 2)
if (frame[2] != COMMAND) {
Serial.printf("Unexpected command byte: 0x%02X (expected 0x%02X)\n", frame[2], COMMAND);
return -1.0;
}
// Extract mass concentration (D7, bytes 12-13, big-endian)
uint16_t massConcentration = (frame[12] << 8) | frame[13];
// Print the hex values of D7 (bytes 12 and 13) for debugging
Serial.printf("D7 Hex: 0x%02X 0x%02X (Raw Value: %u)\n", frame[12], frame[13], massConcentration);
return (float)massConcentration;
}
void dumpFrame(uint8_t* frame, int length) {
// Debug: Print the raw frame bytes in hex
Serial.print("Raw frame: ");
for (int i = 0; i < length; i++) {
Serial.printf("0x%02X ", frame[i]);
}
Serial.println();
}
void loop() {
static uint8_t buffer[FRAME_LENGTH * 2]; // Larger buffer to handle partial frames
static int bufferIndex = 0;
// Read available bytes into buffer
while (SensorSerial.available() > 0 && bufferIndex < sizeof(buffer)) {
buffer[bufferIndex++] = SensorSerial.read();
}
// Look for a complete frame in the buffer
for (int i = 0; i <= bufferIndex - FRAME_LENGTH; i++) {
if (buffer[i] == STX) {
// Potential frame start, check if we have enough bytes
if (i + FRAME_LENGTH <= bufferIndex) {
uint8_t frame[FRAME_LENGTH];
memcpy(frame, &buffer[i], FRAME_LENGTH);
// Always dump the full frame for every successful read
dumpFrame(frame, FRAME_LENGTH);
float massConcentration = parseMeasurementFrame(frame);
if (massConcentration >= 0) {
Serial.printf("Mass Concentration: %.0f μg/m³\n", massConcentration);
} else {
Serial.println("Failed to parse frame");
}
// Shift buffer left to remove processed frame
int remaining = bufferIndex - (i + FRAME_LENGTH);
memmove(buffer, &buffer[i + FRAME_LENGTH], remaining);
bufferIndex = remaining;
break; // Process one frame at a time
}
}
}
// If buffer is full but no frame found, clear it
if (bufferIndex >= sizeof(buffer)) {
Serial.println("Buffer overflow, resetting...");
bufferIndex = 0;
}
delay(100); // Small delay to avoid overwhelming the loop
}
this is the code i am currently using
D7 Hex: 0xCD 0x17 (Raw Value: 52503)
12:31:26.450 -> Mass Concentration: 52503 μg/m³
12:31:28.411 -> Raw frame: 0x02 0x1D 0x10 0x03 0xE8 0x03 0xE8 0x03 0xE8 0x03 0xE8 0x13 0xC6 0x17 0x93 0x14 0x16 0x18 0x38 0x10 0x00 0x10 0x24 0x03 0x0F 0x11 0x03 0x42 0x04
12:31:28.411 -> D7 Hex: 0xC6 0x17 (Raw Value: 50711)
12:31:28.460 -> Mass Concentration: 50711 μg/m³
12:31:30.446 -> Raw frame: 0x02 0x1D 0x10 0x03 0xE8 0x03 0xE8 0x03 0xE8 0x03 0xE8 0x13 0xD1 0x17 0xC1 0x14 0x10 0x18 0x29 0x10 0x00 0x10 0x24 0x03 0x0F 0x11 0x03 0x66 0x04
12:31:30.446 -> D7 Hex: 0xD1 0x17 (Raw Value: 53527)
this is the output
... I am unable to add the attachments
Maybe this will help you out:
i using an ESP 32 since the uart of sensor sends signal at 3.3 V
Can you tell me what Pulse Occupany Ratio means?
I've no idea as this isn't my field of expertise, however a quick Google search brought up an AI answer:
Pulse Occupancy:
In some air quality sensors, particularly those using optical methods, a pulse is generated when a particle passes through the sensor's detection area. The pulse occupancy ratio is the percentage of time that the sensor is detecting these pulses, indicating the presence of particles.
I am also confused which hex data do i have to take is it D3-D6 or D7-D10. because the earlier data is stationary at 03E8 which is the max value
After reading this thread I spend some time to make an alternative library for the PPD71 as I think it is an interesting environmental sensor.
The library is not tested with hardware, it would be great if you could do that (I have no HW).
Main difference is it extracts all info from the data packet the device sends.
There are still some open ends like CRC testing, add examples and improve documentation.
Please give it a try, if there are problems open an issue and I will investigate.
Across which pin to take the output of the sensor




