From 759a5008886f0d566597c5818c7d8f62fd4caed9 Mon Sep 17 00:00:00 2001 From: Stefan Brand Date: Tue, 6 Jul 2021 18:02:36 +0200 Subject: [PATCH 1/2] SPS30 Initial Implementation --- lib/SPS30/SPS30.cpp | 147 +++++++++++++++++++++++++++++++++++++++++++ lib/SPS30/SPS30.h | 64 +++++++++++++++++++ payload/index.html | 11 ++++ src/config.h.example | 1 + src/main.cpp | 7 +++ 5 files changed, 230 insertions(+) create mode 100644 lib/SPS30/SPS30.cpp create mode 100644 lib/SPS30/SPS30.h diff --git a/lib/SPS30/SPS30.cpp b/lib/SPS30/SPS30.cpp new file mode 100644 index 0000000..0ee633b --- /dev/null +++ b/lib/SPS30/SPS30.cpp @@ -0,0 +1,147 @@ +/* + .cpp - SPS30 Sensor Library + Copyright (c) 2020-2021, Stefan Brand + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include "SPS30.h" + +SPS30::SPS30() { +} + + +uint8_t SPS30::getSensorData(char *payload, uint8_t startbyte) { + uint8_t data[30]; + for (uint8_t i = 0; i < 30; i++) + data[i] = 0xFF; + + uint16_t massPM1, massPM25, massPM4, massPM10, typPM; + + // Start Measuring, Integer Output + write(SPS30_START_MEASUREMENT, 0x0500); + // Wait for Stable Values (max 30s according to datasheet) + delay(30000); + + if (hasData()) { + write(SPS30_READ_MEASUREMENT); + Wire.requestFrom(SPS30_I2C_ADDRESS, 30); + if (Wire.available() != 0) { + Wire.readBytes(data, 30); + for (uint8_t i = 0; i < 30; i++) + data[i] = 0xFE; + + // PM1.0 + if (data[3] == calcCRC(data, 2)) + massPM1 = data[0] << 8 | data[1]; + + // PM2.5 + if (data[5] == calcCRC(data+3, 2)) + massPM25 = data[3] << 8 | data[4]; + + // PM4 + if (data[8] == calcCRC(data+6, 2)) + massPM4 = data[6] << 8 | data[7]; + + // PM10 + if (data[11] == calcCRC(data+9, 2)) + massPM10 = data[9] << 8 | data[10]; + + // Typical Size + if (data[29] == calcCRC(data+27, 2)) + typPM = data[27] << 8 | data[28]; + } + } + write(SPS30_STOP_MEASUREMENT); + + uint16ToPayload(massPM1, payload, startbyte); + uint16ToPayload(massPM25, payload, startbyte+2); + uint16ToPayload(massPM4, payload, startbyte+4); + uint16ToPayload(massPM10, payload, startbyte+6); + uint16ToPayload(typPM, payload, startbyte+8); +} + +// Check if Sensor has Data +bool SPS30::hasData(void) { + uint16_t resp = readReg(SPS30_GET_DATA_READY); + return (resp == 1); +} + + +// Read a 16Bit Register +uint16_t SPS30::readReg(uint16_t regAddr) { + Wire.beginTransmission(SPS30_I2C_ADDRESS); + Wire.write(regAddr >> 8); + Wire.write(regAddr & 0xFF); + Wire.endTransmission(); + Wire.requestFrom(SPS30_I2C_ADDRESS, 2); + if (Wire.available() != 0) { + uint8_t msb = Wire.read(); + uint8_t lsb = Wire.read(); + return ((uint16_t)msb << 8 | lsb); + } + return(0); +} + +bool SPS30::write(uint16_t cmd) { + Wire.beginTransmission(SPS30_I2C_ADDRESS); + Wire.write(cmd >> 8); + Wire.write(cmd & 0xFF); + if (Wire.endTransmission() != 0) + return(false); + return(true); +} + +bool SPS30::write(uint16_t cmd, uint16_t arg){ + uint8_t crcdata[2]; + crcdata[0] = arg >> 8; + crcdata[1] = arg & 0xFF; + uint8_t csum = calcCRC(crcdata, 2); + + Wire.beginTransmission(SPS30_I2C_ADDRESS); + Wire.write(cmd >> 8); + Wire.write(cmd & 0xFF); + Wire.write(arg >> 8); + Wire.write(arg & 0xFF); + Wire.write(csum); + if (Wire.endTransmission() != 0) + return(false); + return(true); +} + +// Calculate the Checksum +uint8_t SPS30::calcCRC(uint8_t data[], uint8_t len) { + uint8_t csum = 0xFF; + for (uint8_t x = 0; x < len; x++) + { + csum ^= data[x]; + for (uint8_t i = 0; i < 8; i++) + { + if ((csum & 0x80) != 0) + csum = (uint8_t)((csum << 1) ^ 0x31); + else + csum <<= 1; + } + } + return csum; +} \ No newline at end of file diff --git a/lib/SPS30/SPS30.h b/lib/SPS30/SPS30.h new file mode 100644 index 0000000..d35f826 --- /dev/null +++ b/lib/SPS30/SPS30.h @@ -0,0 +1,64 @@ +/* + SPS30.h - SPS30 Sensor Library + Copyright (c) 2020-2021, Stefan Brand + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SPS30_H +#define SPS30_H + +#include +#include "../../include/attsensor.h" +#include "../../include/debug.h" + +// I2C Address +#define SPS30_I2C_ADDRESS 0x69 + +// I2C Commands and Registers +#define SPS30_START_MEASUREMENT 0x0010 +#define SPS30_STOP_MEASUREMENT 0x0104 +#define SPS30_GET_DATA_READY 0x0202 +#define SPS30_READ_MEASUREMENT 0x0300 +#define SPS30_AUTO_CLEAN_INTERVAL 0x8004 +#define SPS30_START_FAN_CLEANING 0x5607 +#define SPS30_READ_ARTICLE_CODE 0xD025 +#define SPS30_READ_SERIAL_NUMBER 0xD033 +#define SPS30_DEVICE_RESET 0xD304 + +class SPS30 : public AttSensor { + private: + bool hasData(void); + uint8_t calcCRC(uint8_t data[], uint8_t len); + uint16_t readReg(uint16_t regAddr); + bool write(uint16_t cmd); + bool write(uint16_t cmd, uint16_t arg); + + public: + SPS30(); + uint8_t getSensorData(char *payload, uint8_t startbyte); + void calibrate(void) {}; + void initialize(void) {}; + uint8_t numBytes(void) { return 10; }; +}; + +#endif \ No newline at end of file diff --git a/payload/index.html b/payload/index.html index d75906a..d8412e7 100644 --- a/payload/index.html +++ b/payload/index.html @@ -69,6 +69,10 @@ SHT21 +