diff --git a/code/espurna/board.cpp b/code/espurna/board.cpp index d1e8fbe194..790abfdbd8 100644 --- a/code/espurna/board.cpp +++ b/code/espurna/board.cpp @@ -339,6 +339,9 @@ const char* getEspurnaSensors() { #if SI7021_SUPPORT "SI7021 " #endif +#if SM300D2_SUPPORT + "SM300D2 " +#endif #if SONAR_SUPPORT "SONAR " #endif diff --git a/code/espurna/config/arduino.h b/code/espurna/config/arduino.h index 79b02b007f..f172c36941 100644 --- a/code/espurna/config/arduino.h +++ b/code/espurna/config/arduino.h @@ -261,6 +261,7 @@ //#define SENSEAIR_SUPPORT 1 //#define SHT3X_I2C_SUPPORT 1 //#define SI7021_SUPPORT 1 +//#define SM300D2_SUPPORT 1 //#define SONAR_SUPPORT 1 //#define T6613_SUPPORT 1 //#define THERMOSTAT_SUPPORT 1 diff --git a/code/espurna/config/sensors.h b/code/espurna/config/sensors.h index 8bea7ee19f..d6b88f3b6f 100644 --- a/code/espurna/config/sensors.h +++ b/code/espurna/config/sensors.h @@ -1044,6 +1044,23 @@ #define SI7021_ADDRESS 0x00 // 0x00 means auto #endif +//------------------------------------------------------------------------------ +// SM300D2 sensor +// Enable support by passing SM300D2_SUPPORT=1 build flag +//------------------------------------------------------------------------------ + +#ifndef SM300D2_SUPPORT +#define SM300D2_SUPPORT 0 +#endif + +#ifndef SM300D2_RX_PIN +#define SM300D2_RX_PIN 13 +#endif + +#ifndef SM300D2_BAUDRATE +#define SM300D2_BAUDRATE 9600 +#endif + //------------------------------------------------------------------------------ // HDC1080 / 831R temperature & humidity sensor // Enable support by passing HDC1080_SUPPORT=1 build flag @@ -1397,6 +1414,7 @@ SHT3X_I2C_SUPPORT || \ SI1145_SUPPORT || \ SI7021_SUPPORT || \ + SM300D2_SUPPORT || \ SONAR_SUPPORT || \ T6613_SUPPORT || \ THERMOSTAT_SUPPORT || \ diff --git a/code/espurna/config/types.h b/code/espurna/config/types.h index 948abd5a2a..9df2736250 100644 --- a/code/espurna/config/types.h +++ b/code/espurna/config/types.h @@ -326,6 +326,7 @@ #define SENSOR_HDC1080_ID 40 #define SENSOR_PZEM004TV30_ID 41 #define SENSOR_BME680_ID 42 +#define SENSOR_SM300D2_ID 43 //-------------------------------------------------------------------------------- // Magnitudes @@ -369,8 +370,10 @@ #define MAGNITUDE_IAQ_ACCURACY 34 #define MAGNITUDE_IAQ_STATIC 35 #define MAGNITUDE_VOC 36 +#define MAGNITUDE_TVOC 37 +#define MAGNITUDE_CH2O 38 -#define MAGNITUDE_MAX 38 +#define MAGNITUDE_MAX 39 #define SENSOR_ERROR_OK 0 // No error #define SENSOR_ERROR_OUT_OF_RANGE 1 // Result out of sensor range diff --git a/code/espurna/sensor.cpp b/code/espurna/sensor.cpp index 307120e69e..375b000a72 100644 --- a/code/espurna/sensor.cpp +++ b/code/espurna/sensor.cpp @@ -168,6 +168,10 @@ Copyright (C) 2016-2019 by Xose Pérez #include "sensors/SI7021Sensor.h" #endif +#if SM300D2_SUPPORT + #include "sensors/SM300D2Sensor.h" +#endif + #if SONAR_SUPPORT #include "sensors/SonarSensor.h" #endif @@ -753,6 +757,12 @@ String magnitudeTopic(unsigned char type) { case MAGNITUDE_FREQUENCY: result = F("frequency"); break; + case MAGNITUDE_TVOC: + result = F("tvoc"); + break; + case MAGNITUDE_CH2O: + result = F("ch2o"); + break; case MAGNITUDE_NONE: default: result = F("unknown"); @@ -1023,6 +1033,8 @@ const char * const _magnitudeSettingsPrefix(unsigned char type) { case MAGNITUDE_RESISTANCE: return "res"; case MAGNITUDE_PH: return "ph"; case MAGNITUDE_FREQUENCY: return "freq"; + case MAGNITUDE_TVOC: return "tvoc"; + case MAGNITUDE_CH2O: return "ch2o"; default: return nullptr; } } @@ -1289,6 +1301,12 @@ String magnitudeName(unsigned char type) { case MAGNITUDE_FREQUENCY: result = F("Frequency"); break; + case MAGNITUDE_TVOC: + result = F("TVOC"); + break; + case MAGNITUDE_CH2O: + result = F("CH2O"); + break; case MAGNITUDE_NONE: default: break; @@ -2120,6 +2138,14 @@ void _sensorLoad() { } #endif + #if SM300D2_SUPPORT + { + SM300D2Sensor * sensor = new SM300D2Sensor(); + sensor->setRX(SM300D2_RX_PIN); + _sensors.push_back(sensor); + } + #endif + #if T6613_SUPPORT { T6613Sensor * sensor = new T6613Sensor(); diff --git a/code/espurna/sensors/BaseSensor.h b/code/espurna/sensors/BaseSensor.h index 99dff44a4f..d364ac98be 100644 --- a/code/espurna/sensors/BaseSensor.h +++ b/code/espurna/sensors/BaseSensor.h @@ -104,6 +104,9 @@ class BaseSensor { return sensor::Unit::KilowattHour; case MAGNITUDE_PM1dot0: case MAGNITUDE_PM2dot5: + case MAGNITUDE_PM10: + case MAGNITUDE_TVOC: + case MAGNITUDE_CH2O: return sensor::Unit::MicrogrammPerCubicMeter; case MAGNITUDE_CO: case MAGNITUDE_CO2: diff --git a/code/espurna/sensors/SM300D2Sensor.h b/code/espurna/sensors/SM300D2Sensor.h new file mode 100644 index 0000000000..2ba14f0ff9 --- /dev/null +++ b/code/espurna/sensors/SM300D2Sensor.h @@ -0,0 +1,253 @@ +// ----------------------------------------------------------------------------- +// SmartMeasure SM300D2-VO2 +// https://es.aliexpress.com/item/32984571140.html +// Uses SoftwareSerial library +// Copyright (C) 2021 by Xose Pérez +// ----------------------------------------------------------------------------- + +#if SENSOR_SUPPORT && SM300D2_SUPPORT + +#pragma once + +#include + +#include "BaseSensor.h" + +class SM300D2Sensor : public BaseSensor { + + public: + + // --------------------------------------------------------------------- + // Public + // --------------------------------------------------------------------- + + SM300D2Sensor() { + _count = 7; + _sensor_id = SENSOR_SM300D2_ID; + } + + ~SM300D2Sensor() { + if (_serial) delete _serial; + } + + // --------------------------------------------------------------------- + + void setRX(unsigned char pin_rx) { + if (_pin_rx == pin_rx) return; + _pin_rx = pin_rx; + _dirty = true; + } + + // --------------------------------------------------------------------- + + unsigned char getRX() { + return _pin_rx; + } + + // --------------------------------------------------------------------- + // Sensor API + // --------------------------------------------------------------------- + + // Initialization method, must be idempotent + void begin() { + + if (!_dirty) return; + + if (_serial) delete _serial; + + if (3 == _pin_rx) { + Serial.begin(SM300D2_BAUDRATE); + } else if (13 == _pin_rx) { + Serial.begin(SM300D2_BAUDRATE); + Serial.flush(); + Serial.swap(); + } else { + _serial = new SoftwareSerial(_pin_rx, -1, false); + _serial->enableIntTx(false); + _serial->begin(SM300D2_BAUDRATE); + } + + _ready = true; + _dirty = false; + + } + + // Descriptive name of the sensor + String description() { + char buffer[28]; + if (_serial_is_hardware()) { + snprintf(buffer, sizeof(buffer), "SM300D2 @ HwSerial"); + } else { + snprintf(buffer, sizeof(buffer), "SM300D2 @ SwSerial(%u,NULL)", _pin_rx); + } + return String(buffer); + } + + // Descriptive name of the slot # index + String description(unsigned char index) { + return description(); + }; + + // Address of the sensor (it could be the GPIO or I2C address) + String address(unsigned char index) { + char buffer[6]; + snprintf(buffer, sizeof(buffer), "%u", _pin_rx); + return String(buffer); + } + + // Type for slot # index + unsigned char type(unsigned char index) { + if (index == 0) return MAGNITUDE_CO2; + if (index == 1) return MAGNITUDE_CH2O; + if (index == 2) return MAGNITUDE_TVOC; + if (index == 3) return MAGNITUDE_PM2dot5; + if (index == 4) return MAGNITUDE_PM10; + if (index == 5) return MAGNITUDE_TEMPERATURE; + if (index == 6) return MAGNITUDE_HUMIDITY; + return MAGNITUDE_NONE; + } + + // Current value for slot # index + double value(unsigned char index) { + if (index == 0) return _co2; + if (index == 1) return _ch2o; + if (index == 2) return _tvoc; + if (index == 3) return _pm25; + if (index == 4) return _pm100; + if (index == 5) return _temperature; + if (index == 6) return _humidity; + return 0; + } + + // Process sensor UART + void tick() { + _read(); + } + + protected: + + // --------------------------------------------------------------------- + // Protected + // --------------------------------------------------------------------- + + bool _serial_is_hardware() { + return (3 == _pin_rx) || (13 == _pin_rx); + } + + bool _serial_available() { + if (_serial_is_hardware()) { + return Serial.available(); + } else { + return _serial->available(); + } + } + + void _serial_flush() { + if (_serial_is_hardware()) { + return Serial.flush(); + } else { + return _serial->flush(); + } + } + + uint8_t _serial_read() { + if (_serial_is_hardware()) { + return Serial.read(); + } else { + return _serial->read(); + } + } + + // --------------------------------------------------------------------- + + void _parse() { + + #if SENSOR_DEBUG + char hex[(sizeof(_buffer)*2)+1] = {0}; + if (hexEncode(_buffer, sizeof(_buffer), hex, sizeof(hex))) { + DEBUG_MSG("[SENSOR] SM300D2: %s\n", hex); + } + #endif + + // check second header byte + if (_buffer[1] != 0x02) { + #if SENSOR_DEBUG + DEBUG_MSG("[SENSOR] SM300D2: Wrong header\n"); + #endif + return; + } + + // check crc + uint8_t crc = 0; + for (unsigned char i=0; i<16; i++) crc += _buffer[i]; + if (crc != _buffer[16]) { + #if SENSOR_DEBUG + DEBUG_MSG("[SENSOR] SM300D2: Wrong CRC\n"); + #endif + return; + } + + // CO2 + _co2 = 256 * _buffer[2] + _buffer[3]; + + // CH2O + _ch2o = 256 * _buffer[4] + _buffer[5]; + + // TVOC + _tvoc = 256 * _buffer[6] + _buffer[7]; + + // PM 2.5 + _pm25 = 256 * _buffer[8] + _buffer[9]; + + // PM 10 + _pm100 = 256 * _buffer[10] + _buffer[11]; + + // Temperature + _temperature = (_buffer[12] & 0x7F) + (float) _buffer[13] / 10.0; + if ((_buffer[12] & 0x80) == 0x80) { + _temperature = -_temperature; + } + + // Humidity + _humidity = _buffer[14] + (float) _buffer[15] / 10.0; + + } + + void _read() { + + while(_serial_available()) { + + unsigned char ch = _serial_read(); + if ((_position > 0) || (ch == 0x3C)) { + _buffer[_position] = ch; + _position++; + if (_position == 17) { + _position = 0; + _parse(); + } + } + yield(); + + } + + } + + // --------------------------------------------------------------------- + + unsigned char _buffer[17] = {0}; + unsigned char _position = 0; + + double _co2 = 0; + double _ch2o = 0; + double _tvoc = 0; + double _pm25 = 0; + double _pm100 = 0; + double _temperature = 0; + double _humidity = 0; + + unsigned int _pin_rx; + SoftwareSerial * _serial = NULL; + +}; + +#endif // SENSOR_SUPPORT && SM300D2_SUPPORT diff --git a/code/test/build/sensor.h b/code/test/build/sensor.h index 231bac9c7a..a7cbc2efaf 100644 --- a/code/test/build/sensor.h +++ b/code/test/build/sensor.h @@ -23,6 +23,7 @@ #define SHT3X_I2C_SUPPORT 1 #define SI1145_SUPPORT 1 #define SI7021_SUPPORT 1 +#define SM300D2_SUPPORT 1 #define SONAR_SUPPORT 1 #define T6613_SUPPORT 1 #define THERMOSTAT_SUPPORT 1