Browse Source

Added support for MHZ19 CO2 sensor

fastled
Xose Pérez 7 years ago
parent
commit
77b05086d9
9 changed files with 2078 additions and 1900 deletions
  1. +0
    -45
      code/espurna/config/general.h
  2. +3
    -0
      code/espurna/config/prototypes.h
  3. +60
    -13
      code/espurna/config/sensors.h
  4. BIN
      code/espurna/data/index.html.gz
  5. +8
    -0
      code/espurna/sensor.ino
  6. +6
    -3
      code/espurna/sensors/BaseSensor.h
  7. +162
    -0
      code/espurna/sensors/MHZ19Sensor.h
  8. +1838
    -1838
      code/espurna/static/index.html.gz.h
  9. +1
    -1
      code/html/custom.js

+ 0
- 45
code/espurna/config/general.h View File

@ -755,51 +755,6 @@ PROGMEM const char* const custom_reset_string[] = {
#define RF_SEND_DELAY 250 // Interval between sendings in ms #define RF_SEND_DELAY 250 // Interval between sendings in ms
#define RF_RECEIVE_DELAY 500 // Interval between recieving in ms (avoid debouncing) #define RF_RECEIVE_DELAY 500 // Interval between recieving in ms (avoid debouncing)
// -----------------------------------------------------------------------------
// SENSORS
// -----------------------------------------------------------------------------
#define SENSOR_READ_INTERVAL 6000 // Read data from sensors every 6 seconds
#define SENSOR_REPORT_EVERY 10 // Report every this many readings
#define SENSOR_USE_INDEX 0 // Use the index in topic (i.e. temperature/0)
// even if just one sensor (0 for backwards compatibility)
#define SENSOR_TEMPERATURE_DECIMALS 1
#define SENSOR_HUMIDITY_DECIMALS 0
#define SENSOR_PRESSURE_DECIMALS 2
#define SENSOR_ANALOG_DECIMALS 0
#define SENSOR_EVENTS_DECIMALS 0
#define SENSOR_CURRENT_DECIMALS 3
#define SENSOR_VOLTAGE_DECIMALS 0
#define SENSOR_POWER_DECIMALS 0
#define SENSOR_POWER_FACTOR_DECIMALS 0
#define SENSOR_ENERGY_DECIMALS 0
#define SENSOR_PM1dot0_DECIMALS 0
#define SENSOR_PM2dot5_DECIMALS 0
#define SENSOR_PM10_DECIMALS 0
#define SENSOR_UNKNOWN_TOPIC "unknown"
#define SENSOR_TEMPERATURE_TOPIC "temperature"
#define SENSOR_HUMIDITY_TOPIC "humidity"
#define SENSOR_PRESSURE_TOPIC "pressure"
#define SENSOR_CURRENT_TOPIC "current"
#define SENSOR_VOLTAGE_TOPIC "voltage"
#define SENSOR_ACTIVE_POWER_TOPIC "power"
#define SENSOR_APPARENT_POWER_TOPIC "apparent"
#define SENSOR_REACTIVE_POWER_TOPIC "reactive"
#define SENSOR_POWER_FACTOR_TOPIC "factor"
#define SENSOR_ENERGY_TOPIC "energy"
#define SENSOR_ENERGY_DELTA_TOPIC "energy_delta"
#define SENSOR_PM1dot0_TOPIC "pm1dot0"
#define SENSOR_PM2dot5_TOPIC "pm2dot5"
#define SENSOR_PM10_TOPIC "pm10"
#define SENSOR_ANALOG_TOPIC "analog"
#define SENSOR_DIGITAL_TOPIC "digital"
#define SENSOR_EVENTS_TOPIC "events"
#define SENSOR_TEMPERATURE_UNITS TMP_CELSIUS // Temperature units (TMP_CELSIUS | TMP_FAHRENHEIT)
#define SENSOR_TEMPERATURE_CORRECTION 0.0 // Offset correction
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// IR // IR
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------


+ 3
- 0
code/espurna/config/prototypes.h View File

@ -82,6 +82,9 @@ template<typename T> bool idbSend(const char * topic, unsigned char id, T payloa
#include <SoftwareSerial.h> #include <SoftwareSerial.h>
#include <PMS.h> #include <PMS.h>
#endif #endif
#if MHZ19_SUPPORT
#include <SoftwareSerial.h>
#endif
#if BME280_SUPPORT #if BME280_SUPPORT
#include <SparkFunBME280.h> #include <SparkFunBME280.h>
#endif #endif


+ 60
- 13
code/espurna/config/sensors.h View File

@ -1,16 +1,51 @@
//--------------------------------------------------------------------------------
// General
//--------------------------------------------------------------------------------
#ifndef TEMPERATURE_MIN_CHANGE
#define TEMPERATURE_MIN_CHANGE 0.0 // Minimum temperature change to report
#endif
#ifndef HUMIDITY_MIN_CHANGE
#define HUMIDITY_MIN_CHANGE 0 // Minimum humidity change to report
#endif
#define TEMPERATURE_DECIMALS 1 // Decimals for temperature values
// -----------------------------------------------------------------------------
// SENSORS
// -----------------------------------------------------------------------------
#define SENSOR_READ_INTERVAL 6000 // Read data from sensors every 6 seconds
#define SENSOR_REPORT_EVERY 10 // Report every this many readings
#define SENSOR_USE_INDEX 0 // Use the index in topic (i.e. temperature/0)
// even if just one sensor (0 for backwards compatibility)
#define SENSOR_TEMPERATURE_UNITS TMP_CELSIUS // Temperature units (TMP_CELSIUS | TMP_FAHRENHEIT)
#define SENSOR_TEMPERATURE_CORRECTION 0.0 // Offset correction
#define TEMPERATURE_MIN_CHANGE 0.0 // Minimum temperature change to report
#define HUMIDITY_MIN_CHANGE 0 // Minimum humidity change to report
#define SENSOR_TEMPERATURE_DECIMALS 1
#define SENSOR_HUMIDITY_DECIMALS 0
#define SENSOR_PRESSURE_DECIMALS 2
#define SENSOR_ANALOG_DECIMALS 0
#define SENSOR_EVENTS_DECIMALS 0
#define SENSOR_CURRENT_DECIMALS 3
#define SENSOR_VOLTAGE_DECIMALS 0
#define SENSOR_POWER_DECIMALS 0
#define SENSOR_POWER_FACTOR_DECIMALS 0
#define SENSOR_ENERGY_DECIMALS 0
#define SENSOR_PM1dot0_DECIMALS 0
#define SENSOR_PM2dot5_DECIMALS 0
#define SENSOR_PM10_DECIMALS 0
#define SENSOR_CO2_DECIMALS 0
#define SENSOR_UNKNOWN_TOPIC "unknown"
#define SENSOR_TEMPERATURE_TOPIC "temperature"
#define SENSOR_HUMIDITY_TOPIC "humidity"
#define SENSOR_PRESSURE_TOPIC "pressure"
#define SENSOR_CURRENT_TOPIC "current"
#define SENSOR_VOLTAGE_TOPIC "voltage"
#define SENSOR_ACTIVE_POWER_TOPIC "power"
#define SENSOR_APPARENT_POWER_TOPIC "apparent"
#define SENSOR_REACTIVE_POWER_TOPIC "reactive"
#define SENSOR_POWER_FACTOR_TOPIC "factor"
#define SENSOR_ENERGY_TOPIC "energy"
#define SENSOR_ENERGY_DELTA_TOPIC "energy_delta"
#define SENSOR_PM1dot0_TOPIC "pm1dot0"
#define SENSOR_PM2dot5_TOPIC "pm2dot5"
#define SENSOR_PM10_TOPIC "pm10"
#define SENSOR_ANALOG_TOPIC "analog"
#define SENSOR_DIGITAL_TOPIC "digital"
#define SENSOR_EVENTS_TOPIC "events"
#define SENSOR_CO2_TOPIC "co2"
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// DHTXX temperature/humidity sensor // DHTXX temperature/humidity sensor
@ -244,6 +279,18 @@
#define PMS_RX_PIN 13 #define PMS_RX_PIN 13
#define PMS_TX_PIN 15 #define PMS_TX_PIN 15
//--------------------------------------------------------------------------------
// MHZ19 CO2 sensor
// Enable support by passing MHZ19_SUPPORT=1 build flag
//--------------------------------------------------------------------------------
#ifndef MHZ19_SUPPORT
#define MHZ19_SUPPORT 1
#endif
#define MHZ19_RX_PIN 13
#define MHZ19_TX_PIN 15
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// Internal power montior // Internal power montior
// Enable support by passing ADC_VCC_ENABLED=1 build flag // Enable support by passing ADC_VCC_ENABLED=1 build flag


BIN
code/espurna/data/index.html.gz View File


+ 8
- 0
code/espurna/sensor.ino View File

@ -55,6 +55,7 @@ String _sensorTopic(magnitude_t type) {
if (type == MAGNITUDE_PM1dot0) return String(SENSOR_PM1dot0_TOPIC); if (type == MAGNITUDE_PM1dot0) return String(SENSOR_PM1dot0_TOPIC);
if (type == MAGNITUDE_PM2dot5) return String(SENSOR_PM2dot5_TOPIC); if (type == MAGNITUDE_PM2dot5) return String(SENSOR_PM2dot5_TOPIC);
if (type == MAGNITUDE_PM10) return String(SENSOR_PM10_TOPIC); if (type == MAGNITUDE_PM10) return String(SENSOR_PM10_TOPIC);
if (type == MAGNITUDE_CO2) return String(SENSOR_CO2_TOPIC);
return String(SENSOR_UNKNOWN_TOPIC); return String(SENSOR_UNKNOWN_TOPIC);
} }
@ -75,6 +76,7 @@ unsigned char _sensorDecimals(magnitude_t type) {
if (type == MAGNITUDE_PM1dot0) return SENSOR_PM1dot0_DECIMALS; if (type == MAGNITUDE_PM1dot0) return SENSOR_PM1dot0_DECIMALS;
if (type == MAGNITUDE_PM2dot5) return SENSOR_PM2dot5_DECIMALS; if (type == MAGNITUDE_PM2dot5) return SENSOR_PM2dot5_DECIMALS;
if (type == MAGNITUDE_PM10) return SENSOR_PM10_DECIMALS; if (type == MAGNITUDE_PM10) return SENSOR_PM10_DECIMALS;
if (type == MAGNITUDE_CO2) return SENSOR_CO2_DECIMALS;
return 0; return 0;
} }
@ -94,6 +96,7 @@ String _sensorUnits(magnitude_t type) {
if (type == MAGNITUDE_PM1dot0) return String("µg/m3"); if (type == MAGNITUDE_PM1dot0) return String("µg/m3");
if (type == MAGNITUDE_PM2dot5) return String("µg/m3"); if (type == MAGNITUDE_PM2dot5) return String("µg/m3");
if (type == MAGNITUDE_PM10) return String("µg/m3"); if (type == MAGNITUDE_PM10) return String("µg/m3");
if (type == MAGNITUDE_CO2) return String("ppm");
return String(); return String();
} }
@ -282,6 +285,11 @@ void sensorInit() {
sensorRegister(new PMSX003Sensor(PMS_RX_PIN, PMS_TX_PIN)); sensorRegister(new PMSX003Sensor(PMS_RX_PIN, PMS_TX_PIN));
#endif #endif
#if MHZ19_SUPPORT
#include "sensors/MHZ19Sensor.h"
sensorRegister(new MHZ19Sensor(MHZ19_RX_PIN, MHZ19_TX_PIN));
#endif
#if COUNTER_SUPPORT #if COUNTER_SUPPORT
#include "sensors/EventSensor.h" #include "sensors/EventSensor.h"
sensorRegister(new EventSensor(COUNTER_PIN, COUNTER_PIN_MODE, COUNTER_DEBOUNCE)); sensorRegister(new EventSensor(COUNTER_PIN, COUNTER_PIN_MODE, COUNTER_DEBOUNCE));


+ 6
- 3
code/espurna/sensors/BaseSensor.h View File

@ -31,13 +31,16 @@ typedef enum magnitude_t {
MAGNITUDE_PM2dot5, MAGNITUDE_PM2dot5,
MAGNITUDE_PM10, MAGNITUDE_PM10,
MAGNITUDE_CO2,
MAGNITUDE_MAX, MAGNITUDE_MAX,
} magnitude_t; } magnitude_t;
#define SENSOR_ERROR_OK 0
#define SENSOR_ERROR_OUT_OF_RANGE 1
#define SENSOR_ERROR_WARM_UP 2
#define SENSOR_ERROR_OK 0 // No error
#define SENSOR_ERROR_OUT_OF_RANGE 1 // Result out of sensor range
#define SENSOR_ERROR_WARM_UP 2 // Sensor is warming-up
#define SENSOR_ERROR_TIMEOUT 3 // Response from sensor timed out
class BaseSensor { class BaseSensor {


+ 162
- 0
code/espurna/sensors/MHZ19Sensor.h View File

@ -0,0 +1,162 @@
// -----------------------------------------------------------------------------
// MHZ19 CO2 sensor
// Based on: https://github.com/nara256/mhz19_uart
// http://www.winsen-sensor.com/d/files/infrared-gas-sensor/mh-z19b-co2-ver1_0.pdf
// -----------------------------------------------------------------------------
#pragma once
#include "Arduino.h"
#include "BaseSensor.h"
#include <SoftwareSerial.h>
#define MHZ19_REQUEST_LEN 8
#define MHZ19_RESPONSE_LEN 9
#define MHZ19_TIMEOUT 1000
#define MHZ19_GETPPM 0x8600
#define MHZ19_ZEROCALIB 0x8700
#define MHZ19_SPANCALIB 0x8800
#define MHZ19_AUTOCALIB_ON 0x79A0
#define MHZ19_AUTOCALIB_OFF 0x7900
class MHZ19Sensor : public BaseSensor {
public:
MHZ19Sensor(int pin_rx = MHZ19_RX_PIN, int pin_tx = MHZ19_TX_PIN): BaseSensor() {
// Cache
_pin_rx = pin_rx;
_pin_tx = pin_tx;
_count = 1;
// Init
_serial = new SoftwareSerial(pin_rx, pin_tx, false, 256);
_serial->begin(9600);
calibrateAuto(false);
}
// Descriptive name of the sensor
String name() {
char buffer[28];
snprintf(buffer, sizeof(buffer), "MHZ19 @ SwSerial(%i,%i)", _pin_rx, _pin_tx);
return String(buffer);
}
// Descriptive name of the slot # index
String slot(unsigned char index) {
return name();
}
// Type for slot # index
magnitude_t type(unsigned char index) {
_error = SENSOR_ERROR_OK;
if (index == 0) return MAGNITUDE_CO2;
_error = SENSOR_ERROR_OUT_OF_RANGE;
return MAGNITUDE_NONE;
}
void pre() {
_read();
}
// Current value for slot # index
double value(unsigned char index) {
_error = SENSOR_ERROR_OK;
if (index == 0) return _co2;
_error = SENSOR_ERROR_OUT_OF_RANGE;
return 0;
}
void calibrateAuto(boolean state){
_write(state ? MHZ19_AUTOCALIB_ON : MHZ19_AUTOCALIB_OFF);
}
void calibrateZero() {
_write(MHZ19_ZEROCALIB);
}
void calibrateSpan(unsigned int ppm) {
if( ppm < 1000 ) return;
unsigned char buffer[MHZ19_REQUEST_LEN] = {0};
buffer[0] = 0xFF;
buffer[1] = 0x01;
buffer[2] = MHZ19_SPANCALIB >> 8;
buffer[3] = ppm >> 8;
buffer[4] = ppm & 0xFF;
_write(buffer);
}
protected:
void _write(unsigned char * command) {
_serial->write(command, MHZ19_REQUEST_LEN);
_serial->write(_checksum(command));
_serial->flush();
}
void _write(unsigned int command, unsigned char * response) {
unsigned char buffer[MHZ19_REQUEST_LEN] = {0};
buffer[0] = 0xFF;
buffer[1] = 0x01;
buffer[2] = command >> 8;
buffer[3] = command & 0xFF;
_write(buffer);
if (response != NULL) {
unsigned long start = millis();
while (_serial->available() == 0) {
if (millis() - start > MHZ19_TIMEOUT) {
_error = SENSOR_ERROR_TIMEOUT;
return;
}
yield();
}
_serial->readBytes(response, MHZ19_RESPONSE_LEN);
}
}
void _write(unsigned int command) {
_write(command, NULL);
}
void _read() {
unsigned char buffer[MHZ19_RESPONSE_LEN] = {0};
_write(MHZ19_GETPPM, buffer);
// Check response
if ((buffer[0] == 0xFF)
&& (buffer[1] == 0x86)
&& (_checksum(buffer) == buffer[MHZ19_RESPONSE_LEN-1])) {
unsigned int value = buffer[2] * 256 + buffer[3];
if (0 <= value && value <= 5000) {
_co2 = value;
_error = SENSOR_ERROR_OK;
} else {
_error = SENSOR_ERROR_OUT_OF_RANGE;
}
}
}
uint8_t _checksum(uint8_t * command) {
uint8_t sum = 0x00;
for (unsigned char i = 1; i < MHZ19_REQUEST_LEN-1; i++) {
sum += command[i];
}
sum = 0xFF - sum + 0x01;
return sum;
}
double _co2 = 0;
unsigned int _pin_rx;
unsigned int _pin_tx;
SoftwareSerial * _serial;
};

+ 1838
- 1838
code/espurna/static/index.html.gz.h
File diff suppressed because it is too large
View File


+ 1
- 1
code/html/custom.js View File

@ -33,7 +33,7 @@ function sensorType(type) {
"Current", "Voltage", "Active Power", "Apparent Power", "Current", "Voltage", "Active Power", "Apparent Power",
"Reactive Power", "Energy", "Energy (delta)", "Power Factor", "Reactive Power", "Energy", "Energy (delta)", "Power Factor",
"Analog", "Digital", "Events", "Analog", "Digital", "Events",
"PM1.0", "PM2.5", "PM10"
"PM1.0", "PM2.5", "PM10", "CO2"
]; ];
if (1 <= type && type <= types.length) return types[type-1]; if (1 <= type && type <= types.length) return types[type-1];
return null; return null;


Loading…
Cancel
Save