PMS5003 Air Quality

Using a PMS5003 Sensor to detect particle sizes of 1um, 2.5um and 10um, implemented via ESP Home on Home Assistant:

PMS5003 wiring
The PMS5003 uses 5V power and 3.3V for control.

  • Connect pin one to the 5v VIN pin on the ESP8266
  • Connect Pin 2 to any ground pin.
  • Connect Pin 3 to D3
  • Connect Pin 4 to D6
  • Connect Pin 5 to D7

           

esphome:
  name: aqspms5003

esp8266:
  board: d1_mini

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:
  password: "db13249d8b5dff2bf079cbe585af974d"

wifi:
  ssid:     "your Wi-Fi SSID"
  password: "your Wi-Fi PASSWORD"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Aqs Fallback Hotspot"
    password: "uOrOJjJeYvqg"

captive_portal:
    
uart:
    rx_pin: D7
    tx_pin: D6
    baud_rate: 9600
    id: pms5003_uart

sensor:
  - platform: pmsx003
    type: PMSX003
    pm_1_0:
      name: "PMS5003 <1.0µm Concentration"
      accuracy_decimals: 1
    pm_2_5:
      name: "PMS5003 <2.5µm Concentration"
      accuracy_decimals: 1
    pm_10_0:
      name: "PMS5003 <10.0µm Concentration"
      accuracy_decimals: 1
    update_interval: 120s

Arduino Serial Interface

#include  <SoftwareSerial.h> 

SoftwareSerial pmsSerial(2, 3);

void setup() {
  // our debugging output
  Serial.begin(115200);
  // sensor baud rate is 9600
  pmsSerial.begin(9600);
}

struct pms5003data {
uint16_t framelen;
uint16_t pm10_standard, pm25_standard, pm100_standard;
uint16_t pm10_env, pm25_env, pm100_env;
uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um;
uint16_t unused;
uint16_t checksum;
};

struct pms5003data data;

void loop() {
  if (readPMSdata(&pmsSerial)) {
    // reading data was successful!
    Serial.println();
    Serial.println("---------------------------------------");
    Serial.println("Concentration Units (standard)");
    Serial.print("PM 1.0: "); Serial.print(data.pm10_standard);
    Serial.print("\t\tPM 2.5: "); Serial.print(data.pm25_standard);
    Serial.print("\t\tPM 10: "); Serial.println(data.pm100_standard);
    Serial.println("---------------------------------------");
    Serial.println("Concentration Units (environmental)");
    Serial.print("PM 1.0: "); Serial.print(data.pm10_env);
    Serial.print("\t\tPM 2.5: "); Serial.print(data.pm25_env);
    Serial.print("\t\tPM 10: "); Serial.println(data.pm100_env);
    Serial.println("---------------------------------------");
    Serial.print("Particles > 0.3um / 0.1L air:"); Serial.println(data.particles_03um);
    Serial.print("Particles > 0.5um / 0.1L air:"); Serial.println(data.particles_05um);
    Serial.print("Particles > 1.0um / 0.1L air:"); Serial.println(data.particles_10um);
    Serial.print("Particles > 2.5um / 0.1L air:"); Serial.println(data.particles_25um);
    Serial.print("Particles > 5.0um / 0.1L air:"); Serial.println(data.particles_50um);
    Serial.print("Particles > 10.0 um / 0.1L air:"); Serial.println(data.particles_100um);
    Serial.println("---------------------------------------");
  }
}

boolean readPMSdata(Stream *s) {
  if (! s->available()) {
    return false;
  }
  // Read a byte at a time until we get to the special '0x42' start-byte
  if (s->peek() != 0x42) {
    s->read();
    return false;
  }
  // Now read all 32 bytes
  if (s->available() < 32) { return false; }
  uint8_t buffer[32];
  uint16_t sum = 0; s->readBytes(buffer, 32);
  // get checksum ready
  for (uint8_t i=0; i<30; i++) {
    sum += buffer[i];
  }

  /* debugging
  for (uint8_t i=2; i<32; i++) {
    Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", ");
  }
  Serial.println();
  */

  // The data comes in endian'd, this solves it so it works on all platforms
  uint16_t buffer_u16[15];
  for (uint8_t i=0; i<15; i++) {
    buffer_u16[i] = buffer[2 + i*2 + 1];
    buffer_u16[i] += (buffer[2 + i*2] << 8);
  }
  // put it into a nice struct :)
  memcpy((void *)&data, (void *)buffer_u16, 30);

  if (sum != data.checksum) {
    Serial.println("Checksum failure");
    return false;
  }
  // success!
  return true;
  }