diff --git a/Adafruit_PM25AQI.cpp b/Adafruit_PM25AQI.cpp index a6aee3b..010c7d0 100644 --- a/Adafruit_PM25AQI.cpp +++ b/Adafruit_PM25AQI.cpp @@ -9,6 +9,8 @@ * Arduino platform. It is designed specifically to work with the * Adafruit PM2.5 Air quality sensors: http://www.adafruit.com/products/4632 * + * This library also works with the Cubic PM1006 UART Air Quality Sensor. + * * These sensors use I2C or UART to communicate. * * Adafruit invests time and resources providing this open source code, @@ -18,6 +20,8 @@ * * @section author Author * Written by Ladyada for Adafruit Industries. + * Modified by Brent Rubell for Adafruit Industries for use with Cubic PM1006 + * Air Quality Sensor. * * @section license License * BSD license, all text here must be included in any redistribution. @@ -69,7 +73,10 @@ bool Adafruit_PM25AQI::begin_UART(Stream *theSerial) { */ bool Adafruit_PM25AQI::read(PM25_AQI_Data *data) { uint8_t buffer[32]; + size_t bufLen = sizeof(buffer); uint16_t sum = 0; + uint8_t csum = 0; + bool is_pm1006 = false; if (!data) { return false; @@ -83,48 +90,81 @@ bool Adafruit_PM25AQI::read(PM25_AQI_Data *data) { if (!serial_dev->available()) { return false; } + int skipped = 0; - while ((skipped < 32) && (serial_dev->peek() != 0x42)) { + while ((skipped < 32) && (serial_dev->peek() != 0x42) && + (serial_dev->peek() != 0x16)) { serial_dev->read(); skipped++; if (!serial_dev->available()) { return false; } } - if (serial_dev->peek() != 0x42) { + + // Check for the start character in the stream for both sensors + if ((serial_dev->peek() != 0x42) && (serial_dev->peek() != 0x16)) { serial_dev->read(); return false; } - // Now read all 32 bytes - if (serial_dev->available() < 32) { + + // Are we using the Cubic PM1006 sensor? + if (serial_dev->peek() == 0x16) { + is_pm1006 = true; // Set flag to indicate we are using the PM1006 + bufLen = + 20; // Reduce buffer read length to 20 bytes. Last 12 bytes ignored. + } + + // Are there enough bytes to read from? + if (serial_dev->available() < bufLen) { return false; } - serial_dev->readBytes(buffer, 32); + + // Read all available bytes from the serial stream + serial_dev->readBytes(buffer, bufLen); } else { return false; } - // Check that start byte is correct! - if (buffer[0] != 0x42) { + // Validate start byte is correct if using Adafruit PM sensors + if ((!is_pm1006 && buffer[0] != 0x42)) { return false; } - // get checksum ready - for (uint8_t i = 0; i < 30; i++) { - sum += buffer[i]; + // Validate start header is correct if using Cubic PM1006 sensor + if (is_pm1006 && + (buffer[0] != 0x16 || buffer[1] != 0x11 || buffer[2] != 0x0B)) { + return false; } - // 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); + // Calculate checksum + if (!is_pm1006) { + for (uint8_t i = 0; i < 30; i++) { + sum += buffer[i]; + } + } else { + for (uint8_t i = 0; i < bufLen; i++) { + csum += buffer[i]; + } } - // put it into a nice struct :) - memcpy((void *)data, (void *)buffer_u16, 30); + // Since header and checksum are OK, parse data from the buffer + if (!is_pm1006) { + // 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); + } else { + // Cubic PM1006 sensor only produces a pm25_env reading + data->pm25_env = (buffer[5] << 8) | buffer[6]; + data->checksum = sum; + } - if (sum != data->checksum) { + // Validate checksum + if ((is_pm1006 && csum != 0) || (!is_pm1006 && sum != data->checksum)) { return false; } diff --git a/README.md b/README.md index 527954a..3a50b4a 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ Tested and works great with the Adafruit PM2.5 Air Quality Sensor and Breadboard [](https://www.adafruit.com/products/3686) +This library also works with the Cubic PM1006 air quality sensor present on the [IKEA VINDSTYRKA](https://www.ikea.com/us/en/p/vindriktning-air-quality-sensor-60515911/). Please use the `pm1006_test.ino` sketch to use this sensor. + Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! # Installation diff --git a/examples/PM1006_test/.uno.test.skip b/examples/PM1006_test/.uno.test.skip new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/PM1006_test/.uno.test.skip @@ -0,0 +1 @@ + diff --git a/examples/PM1006_test/PM1006_test.ino b/examples/PM1006_test/PM1006_test.ino new file mode 100644 index 0000000..26faa11 --- /dev/null +++ b/examples/PM1006_test/PM1006_test.ino @@ -0,0 +1,71 @@ +/**************************************************************************************** + Test sketch for using the Adafruit_PM25AQI library with + the Cubic PM1006 UART Air Quality Sensor. + + This sensor is present on the VINDRIKTNING Air Quality sensor, sold by IKEA. + + Check out the guide below for the wiring diagram: + https://learn.adafruit.com/ikea-vindriktning-hack-with-qt-py-esp32-s3-and-adafruit-io + + + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + Modified by Brent Rubell for Adafruit Industries + MIT license, all text above must be included in any redistribution + *****************************************************************************************/ +#include "Adafruit_PM25AQI.h" + +// For UNO and others (without hardware serial) we must use software serial... +// pin #2 is IN from sensor (TX pin on sensor), leave pin #3 disconnected +// comment these two lines if using hardware serial +//#include +//SoftwareSerial pmSerial(2, 3); + +Adafruit_PM25AQI aqi = Adafruit_PM25AQI(); + +void setup() { + // Wait for serial monitor to open + Serial.begin(115200); + while (!Serial) delay(10); + + Serial.println("Cubic PM1006 Air Quality Sensor"); + + // Wait one second for sensor to boot up! + delay(1000); + + // If using serial, initialize it and set baudrate before starting! + Serial1.begin(9600); + // Uncomment the following for use with software serial + //pmSerial.begin(9600); + + if (! aqi.begin_UART(&Serial1)) { // connect to the sensor over hardware serial + //if (! aqi.begin_UART(&pmSerial)) { // connect to the sensor over software serial + Serial.println("Could not find Cubic PM1006 sensor!"); + while (1) delay(10); + } + + Serial.println("Cubic PM1006 found!"); +} + +void loop() { + PM25_AQI_Data data; + + if (! aqi.read(&data)) { + Serial.println("Could not read from PM1006 sensor"); + delay(5000); // Sample every 5 seconds + return; + } + Serial.println("AQI reading success"); + + Serial.println(); + Serial.println(F("---------------------------------------")); + Serial.println(F("Concentration Units (environmental)")); + Serial.println(F("---------------------------------------")); + Serial.print(F("PM 1.0: ")); Serial.print(data.pm25_env); + Serial.println(F("---------------------------------------")); + + delay(20000); // Wait 20 seconds and get another reading from the IKEA Vindriktning +} diff --git a/library.properties b/library.properties index e80ce02..402e5a3 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Adafruit PM25 AQI Sensor -version=1.0.8 +version=1.1.0 author=Adafruit maintainer=Adafruit sentence=This is an Arduino library for the Adafruit PM2.5 Air Quality Sensor