// ----------------------------------------------------------------------------- // MICS-2710 (and MICS-4514) NO2 Analog Sensor // Copyright (C) 2019 by Xose PĂ©rez // ----------------------------------------------------------------------------- #if SENSOR_SUPPORT && MICS2710_SUPPORT #pragma once #include "BaseAnalogSensor.h" extern "C" { #include "../libs/fs_math.h" } class MICS2710Sensor : public BaseAnalogSensor { public: // --------------------------------------------------------------------- // Public // --------------------------------------------------------------------- MICS2710Sensor() { _count = 2; _sensor_id = SENSOR_MICS2710_ID; } void calibrate() { setR0(_getResistance()); } // --------------------------------------------------------------------- void setAnalogGPIO(unsigned char gpio) { _noxGPIO = gpio; } unsigned char getAnalogGPIO() { return _noxGPIO; } void setPreHeatGPIO(unsigned char gpio) { _preGPIO = gpio; } unsigned char getPreHeatGPIO() { return _preGPIO; } // --------------------------------------------------------------------- // Sensor API // --------------------------------------------------------------------- // Initialization method, must be idempotent void begin() { // Set NOX as analog input pinMode(_noxGPIO, INPUT); // Start pre-heating pinMode(_preGPIO, OUTPUT); digitalWrite(_preGPIO, HIGH); _heating = true; _start = millis(); _ready = true; } // Pre-read hook (usually to populate registers with up-to-date data) void pre() { // Check pre-heat time if (_heating && (millis() - _start > MICS2710_PREHEAT_TIME)) { digitalWrite(_preGPIO, LOW); _heating = false; } if (_ready) { _Rs = _getResistance(); } } // Descriptive name of the sensor String description() { return String("MICS-2710 @ TOUT"); } // 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) { return String("0"); } // Type for slot # index unsigned char type(unsigned char index) { if (0 == index) return MAGNITUDE_RESISTANCE; if (1 == index) return MAGNITUDE_NO2; return MAGNITUDE_NONE; } // Current value for slot # index double value(unsigned char index) { if (0 == index) return _Rs; if (1 == index) return _getPPM(); return 0; } private: unsigned long _getReading() { return analogRead(_noxGPIO); } double _getResistance() { // get voltage (1 == reference) from analog pin double voltage = (float) _getReading() / 1024.0; // schematic: 3v3 - Rs - P - Rl - GND // V(P) = 3v3 * Rl / (Rs + Rl) // Rs = 3v3 * Rl / V(P) - Rl = Rl * ( 3v3 / V(P) - 1) // 3V3 voltage is cancelled double resistance = (voltage > 0) ? _Rl * ( 1 / voltage - 1 ) : 0; return resistance; } double _getPPM() { // According to the datasheet (https://www.cdiweb.com/datasheets/e2v/mics-2710.pdf) // there is an almost linear relation between log(Rs/R0) and log(ppm). // Regression parameters have been calculated based on the graph // in the datasheet with these readings: // // Rs/R0 NO2(ppm) // 23 0.20 // 42 0.30 // 90 0.40 // 120 0.50 // 200 0.60 // 410 0.90 // 500 1.00 // 1000 1.30 // 10000 5.00 return fs_pow(10, 0.5170 * fs_log10(_Rs / _R0) - 1.3954); } bool _heating = false; unsigned long _start = 0; // monitors the pre-heating time unsigned char _noxGPIO = MICS2710_PRE_PIN; unsigned char _preGPIO = MICS2710_NOX_PIN; }; #endif // SENSOR_SUPPORT && MICS2710_SUPPORT