Sharp GP2Y1010AU PM2.5 particle/dust sensor - working

I’ve done plenty of searching but haven’t found any discussion for this sensor in ESPHome, and my implementation isn’t working as well as expected.

This might be a simple question on the use of delay() in ESPHome libraries (included xxxx.h files). Does delay(x) cause a wait of x milliseconds OR x microseconds - can an expert confirm? Based on what I’ve read it appears the former - if this is the case what function can I use for a sub-millisecond delay?

The sensor operation is simple: turn on a LED via a pin, wait 0.28msec, read an analogue value, wait 0.40msec, turn the LED off. Repeat this every 10msec (max). Apply a bit of maths and you can convert the ADC value to voltage and then to an air quality figure in µg/m³.

Located library code (
which is pretty well the same as arduino code I’ve found) performs the read/write function:

    digitalWrite(PIN_LED, LOW); 
    value = analogRead(PIN_ADC);
    digitalWrite(PIN_LED, HIGH);
    voltage = value * (VOLTAGE_VREF / 1024.0);
    publish_state(170 * voltage - 0.1);

The basic sensor config in an ESPHome yaml:

  - platform: custom
    lambda: |-
      auto dust_sensor = new GP2Y1010AU0F();
      return {dust_sensor};
      name: "${upper_devicename} PM2.5 Dust Sensor"
      accuracy_decimals: 2
      unit_of_measurement: "µg/m³"
      icon: "mdi:smog"

This works…kind of. I get results, but for 20% of readings at best. I have multiple sensors all producing the same result so don’t think it’s hardware. In fact, if I turn on the LED permanently I get the same quality of readings (at best 20% non-zero and same range of numerical values return), which from the datasheet looks incorrect.

A 280msec delay instead of 280usec could be the problem. Is there a delaymillis() or usleep() etc that I should use?

Ideally I’d get the function call to perform multiple reads (say 10 readings spaced 10msec apart per Sharp datasheet), and average the non-zero results before returning a value to ESPHome.

I’ve achieved what look to be accurate / more realistic PM2.5 results by replacing delay(x) with delayMicroseconds(x) in the library file (GP2Y1010AU0F.h).

It’s a clear, sunny, & windy late autumn day in Sydney and I’m now reading an average of 24µg/m³ which seems reasonable.

        digitalWrite(PIN_LED, LOW);
        delayMicroseconds(280);         // Changed from delay(280)
        // measure voltage
        value = analogRead(PIN_ADC);
        delayMicroseconds(40);          // Changed from delay(40)...probably not needed at all
        digitalWrite(PIN_LED, HIGH);

        // Flash onboard LED for visual indicator only
        digitalWrite(D4, LOW);
        digitalWrite(D4, HIGH);

        // calculate voltage
        voltage = value * (VOLTAGE_VREF / 1024.0);

        // for calibration
        ESP_LOGCONFIG(TAG, "Measurements done, VRef: %f, ADC Value: %f, Calculated Voltage: %f", VOLTAGE_VREF, value, voltage);

        // taken from
        publish_state(170 * voltage);   // Modified from original code, removed -0.1 offset to prevent negative values

ESP board is a Wemos D1 mini. Next step should be to replace any use of delay functions with a loop to prevent impact on other services, and average over multiple readings (in library).

1 Like

Improving results from this PM2.5 particle sensor, I’ve implemented a 10-count averaging within the library file and am happy with the results.

In this image of runtime over the first 36hrs, you can see my burning toast “smoke test” from 1:00PM on 17/08 generated results as expected. The outdoor BBQ that night (with doors open) generated a similar particulate spike that took around 30 mins to clear. The additional smoothing was implemented today from 10:30AM, which dramatically cleans up the output.

Comparing with a local IQAir sensor which has a reading of 5 (US AQI) , it looks like the current code is out in the µg/m³ calculation by up to x20…will have to put my sensor outside for a few days to calibrate!

1 Like