From facb89e5296f5b1620f8d0c874913dae48d92aa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xose=20P=C3=A9rez?= Date: Tue, 9 Oct 2018 11:13:47 +0200 Subject: [PATCH] Initial version of the PulseMeter sensor --- README.md | 4 +- code/espurna/config/arduino.h | 1 + code/espurna/config/hardware.h | 2 + code/espurna/config/progmem.h | 3 + code/espurna/config/sensors.h | 103 +++++++---- code/espurna/config/types.h | 59 +++---- code/espurna/sensor.ino | 69 ++++++-- code/espurna/sensors/PulseMeterSensor.h | 220 ++++++++++++++++++++++++ code/html/index.html | 14 +- 9 files changed, 385 insertions(+), 90 deletions(-) create mode 100644 code/espurna/sensors/PulseMeterSensor.h diff --git a/README.md b/README.md index 394351d5..6d16150a 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ ESPurna ("spark" in Catalan) is a custom firmware for ESP8285/ESP8266 based smar It uses the Arduino Core for ESP8266 framework and a number of 3rd party libraries. [![version](https://img.shields.io/badge/version-1.13.3-brightgreen.svg)](CHANGELOG.md) -[![branch](https://img.shields.io/badge/branch-dev-orange.svg)](https://github.com/xoseperez/espurna/tree/dev/) -[![travis](https://travis-ci.org/xoseperez/espurna.svg?branch=dev)](https://travis-ci.org/xoseperez/espurna) +[![branch](https://img.shields.io/badge/branch-sensors-orange.svg)](https://github.com/xoseperez/espurna/tree/sensors/) +[![travis](https://travis-ci.org/xoseperez/espurna.svg?branch=sensors)](https://travis-ci.org/xoseperez/espurna) [![codacy](https://api.codacy.com/project/badge/Grade/c9496e25cf07434cba786b462cb15f49)](https://www.codacy.com/app/xoseperez/espurna/dashboard) [![license](https://img.shields.io/github/license/xoseperez/espurna.svg)](LICENSE)
diff --git a/code/espurna/config/arduino.h b/code/espurna/config/arduino.h index fe4b081a..04693e94 100644 --- a/code/espurna/config/arduino.h +++ b/code/espurna/config/arduino.h @@ -164,6 +164,7 @@ //#define MICS5525_SUPPORT 1 //#define NTC_SUPPORT 1 //#define PMSX003_SUPPORT 1 +//#define PULSEMETER_SUPPORT 1 //#define PZEM004T_SUPPORT 1 //#define SDS011_SUPPORT 1 //#define SENSEAIR_SUPPORT 1 diff --git a/code/espurna/config/hardware.h b/code/espurna/config/hardware.h index 6068a274..ad95c402 100644 --- a/code/espurna/config/hardware.h +++ b/code/espurna/config/hardware.h @@ -3032,6 +3032,8 @@ #define EMON_ANALOG_SUPPORT 1 #endif + #define PULSEMETER_SUPPORT 1 + // Test non-default modules #define LLMNR_SUPPORT 1 #define NETBIOS_SUPPORT 1 diff --git a/code/espurna/config/progmem.h b/code/espurna/config/progmem.h index 11811783..f686fbe9 100644 --- a/code/espurna/config/progmem.h +++ b/code/espurna/config/progmem.h @@ -199,6 +199,9 @@ PROGMEM const char espurna_sensors[] = #if PMSX003_SUPPORT "PMSX003 " #endif + #if PULSEMETER_SUPPORT + "PULSEMETER " + #endif #if PZEM004T_SUPPORT "PZEM004T " #endif diff --git a/code/espurna/config/sensors.h b/code/espurna/config/sensors.h index b0ea4dcc..084debd6 100644 --- a/code/espurna/config/sensors.h +++ b/code/espurna/config/sensors.h @@ -506,40 +506,6 @@ #define NTC_BETA 3977 // Beta coeficient #endif -//------------------------------------------------------------------------------ -// SDS011 particulates sensor -// Enable support by passing SDS011_SUPPORT=1 build flag -//------------------------------------------------------------------------------ - -#ifndef SDS011_SUPPORT -#define SDS011_SUPPORT 0 -#endif - -#ifndef SDS011_RX_PIN -#define SDS011_RX_PIN 14 -#endif - -#ifndef SDS011_TX_PIN -#define SDS011_TX_PIN 12 -#endif - -//------------------------------------------------------------------------------ -// SenseAir CO2 sensor -// Enable support by passing SENSEAIR_SUPPORT=1 build flag -//------------------------------------------------------------------------------ - -#ifndef SENSEAIR_SUPPORT -#define SENSEAIR_SUPPORT 0 -#endif - -#ifndef SENSEAIR_RX_PIN -#define SENSEAIR_RX_PIN 0 -#endif - -#ifndef SENSEAIR_TX_PIN -#define SENSEAIR_TX_PIN 2 -#endif - //------------------------------------------------------------------------------ // Particle Monitor based on Plantower PMS // Enable support by passing PMSX003_SUPPORT=1 build flag @@ -561,20 +527,42 @@ #endif #ifndef PMS_USE_SOFT -#define PMS_USE_SOFT 0 // If PMS_USE_SOFT == 1, DEBUG_SERIAL_SUPPORT must be 0 +#define PMS_USE_SOFT 0 // If PMS_USE_SOFT == 1, DEBUG_SERIAL_SUPPORT must be 0 #endif #ifndef PMS_RX_PIN -#define PMS_RX_PIN 13 // Software serial RX GPIO (if PMS_USE_SOFT == 1) +#define PMS_RX_PIN 13 // Software serial RX GPIO (if PMS_USE_SOFT == 1) #endif #ifndef PMS_TX_PIN -#define PMS_TX_PIN 15 // Software serial TX GPIO (if PMS_USE_SOFT == 1) +#define PMS_TX_PIN 15 // Software serial TX GPIO (if PMS_USE_SOFT == 1) #endif #ifndef PMS_HW_PORT -#define PMS_HW_PORT Serial // Hardware serial port (if PMS_USE_SOFT == 0) +#define PMS_HW_PORT Serial // Hardware serial port (if PMS_USE_SOFT == 0) +#endif + +//------------------------------------------------------------------------------ +// Pulse Meter Energy monitor +// Enable support by passing PULSEMETER_SUPPORT=1 build flag +//------------------------------------------------------------------------------ + +#ifndef PULSEMETER_SUPPORT +#define PULSEMETER_SUPPORT 0 +#endif + +#ifndef PULSEMETER_PIN +#define PULSEMETERL_PIN 5 #endif + +#ifndef PULSEMETER_ENERGY_RATIO +#define PULSEMETER_ENERGY_RATIO 4000 // In pulses/kWh +#endif + +#ifndef PULSEMETER_INTERRUPT_ON +#define PULSEMETER_INTERRUPT_ON FALLING +#endif + //------------------------------------------------------------------------------ // PZEM004T based power monitor // Enable support by passing PZEM004T_SUPPORT=1 build flag @@ -600,6 +588,40 @@ #define PZEM004T_HW_PORT Serial // Hardware serial port (if PZEM004T_USE_SOFT == 0) #endif +//------------------------------------------------------------------------------ +// SDS011 particulates sensor +// Enable support by passing SDS011_SUPPORT=1 build flag +//------------------------------------------------------------------------------ + +#ifndef SDS011_SUPPORT +#define SDS011_SUPPORT 0 +#endif + +#ifndef SDS011_RX_PIN +#define SDS011_RX_PIN 14 +#endif + +#ifndef SDS011_TX_PIN +#define SDS011_TX_PIN 12 +#endif + +//------------------------------------------------------------------------------ +// SenseAir CO2 sensor +// Enable support by passing SENSEAIR_SUPPORT=1 build flag +//------------------------------------------------------------------------------ + +#ifndef SENSEAIR_SUPPORT +#define SENSEAIR_SUPPORT 0 +#endif + +#ifndef SENSEAIR_RX_PIN +#define SENSEAIR_RX_PIN 0 +#endif + +#ifndef SENSEAIR_TX_PIN +#define SENSEAIR_TX_PIN 2 +#endif + //------------------------------------------------------------------------------ // SHT3X I2C (Wemos) temperature & humidity sensor // Enable support by passing SHT3X_I2C_SUPPORT=1 build flag @@ -720,6 +742,7 @@ SENSEAIR_SUPPORT || \ PMSX003_SUPPORT || \ PZEM004T_SUPPORT || \ + PULSEMETER_SUPPORT || \ SHT3X_I2C_SUPPORT || \ SI7021_SUPPORT || \ SONAR_SUPPORT || \ @@ -858,6 +881,10 @@ #include "../sensors/PMSX003Sensor.h" #endif +#if PULSEMETER_SUPPORT + #include "../sensors/PulseMeterSensor.h" +#endif + #if PZEM004T_SUPPORT #include "../sensors/PZEM004TSensor.h" #endif diff --git a/code/espurna/config/types.h b/code/espurna/config/types.h index a984bb7e..be615fee 100644 --- a/code/espurna/config/types.h +++ b/code/espurna/config/types.h @@ -250,35 +250,36 @@ // These should remain over time, do not modify them, only add new ones at the end //-------------------------------------------------------------------------------- -#define SENSOR_DHTXX_ID 0x01 -#define SENSOR_DALLAS_ID 0x02 -#define SENSOR_EMON_ANALOG_ID 0x03 -#define SENSOR_EMON_ADC121_ID 0x04 -#define SENSOR_EMON_ADS1X15_ID 0x05 -#define SENSOR_HLW8012_ID 0x06 -#define SENSOR_V9261F_ID 0x07 -#define SENSOR_ECH1560_ID 0x08 -#define SENSOR_ANALOG_ID 0x09 -#define SENSOR_DIGITAL_ID 0x10 -#define SENSOR_EVENTS_ID 0x11 -#define SENSOR_PMSX003_ID 0x12 -#define SENSOR_BMX280_ID 0x13 -#define SENSOR_MHZ19_ID 0x14 -#define SENSOR_SI7021_ID 0x15 -#define SENSOR_SHT3X_I2C_ID 0x16 -#define SENSOR_BH1750_ID 0x17 -#define SENSOR_PZEM004T_ID 0x18 -#define SENSOR_AM2320_ID 0x19 -#define SENSOR_GUVAS12SD_ID 0x20 -#define SENSOR_CSE7766_ID 0x21 -#define SENSOR_TMP3X_ID 0x22 -#define SENSOR_SONAR_ID 0x23 -#define SENSOR_SENSEAIR_ID 0x24 -#define SENSOR_GEIGER_ID 0x25 -#define SENSOR_NTC_ID 0x26 -#define SENSOR_SDS011_ID 0x27 -#define SENSOR_MICS2710_ID 0x28 -#define SENSOR_MICS5525_ID 0x29 +#define SENSOR_DHTXX_ID 01 +#define SENSOR_DALLAS_ID 02 +#define SENSOR_EMON_ANALOG_ID 03 +#define SENSOR_EMON_ADC121_ID 04 +#define SENSOR_EMON_ADS1X15_ID 05 +#define SENSOR_HLW8012_ID 06 +#define SENSOR_V9261F_ID 07 +#define SENSOR_ECH1560_ID 08 +#define SENSOR_ANALOG_ID 09 +#define SENSOR_DIGITAL_ID 10 +#define SENSOR_EVENTS_ID 11 +#define SENSOR_PMSX003_ID 12 +#define SENSOR_BMX280_ID 13 +#define SENSOR_MHZ19_ID 14 +#define SENSOR_SI7021_ID 15 +#define SENSOR_SHT3X_I2C_ID 16 +#define SENSOR_BH1750_ID 17 +#define SENSOR_PZEM004T_ID 18 +#define SENSOR_AM2320_ID 19 +#define SENSOR_GUVAS12SD_ID 20 +#define SENSOR_CSE7766_ID 21 +#define SENSOR_TMP3X_ID 22 +#define SENSOR_SONAR_ID 23 +#define SENSOR_SENSEAIR_ID 24 +#define SENSOR_GEIGER_ID 25 +#define SENSOR_NTC_ID 26 +#define SENSOR_SDS011_ID 27 +#define SENSOR_MICS2710_ID 28 +#define SENSOR_MICS5525_ID 29 +#define SENSOR_PULSEMETER_ID 30 //-------------------------------------------------------------------------------- // Magnitudes diff --git a/code/espurna/sensor.ino b/code/espurna/sensor.ino index 48fef408..75e38ae2 100644 --- a/code/espurna/sensor.ino +++ b/code/espurna/sensor.ino @@ -196,6 +196,12 @@ void _sensorWebSocketStart(JsonObject& root) { } #endif + #if PULSEMETER_SUPPORT + if (sensor->getID() == SENSOR_PULSEMETER_ID) { + root["pmVisible"] = 1; + } + #endif + } if (_magnitudes.size() > 0) { @@ -559,24 +565,6 @@ void _sensorLoad() { } #endif - #if SENSEAIR_SUPPORT - { - SenseAirSensor * sensor = new SenseAirSensor(); - sensor->setRX(SENSEAIR_RX_PIN); - sensor->setTX(SENSEAIR_TX_PIN); - _sensors.push_back(sensor); - } - #endif - - #if SDS011_SUPPORT - { - SDS011Sensor * sensor = new SDS011Sensor(); - sensor->setRX(SDS011_RX_PIN); - sensor->setTX(SDS011_TX_PIN); - _sensors.push_back(sensor); - } - #endif - #if PMSX003_SUPPORT { PMSX003Sensor * sensor = new PMSX003Sensor(); @@ -591,6 +579,15 @@ void _sensorLoad() { } #endif + #if PULSEMETER_SUPPORT + { + PulseMeterSensor * sensor = new PulseMeterSensor(); + sensor->setGPIO(PULSEMETER_PIN); + sensor->setEnergyRatio(PULSEMETER_ENERGY_RATIO); + _sensors.push_back(sensor); + } + #endif + #if PZEM004T_SUPPORT { PZEM004TSensor * sensor = new PZEM004TSensor(); @@ -604,6 +601,24 @@ void _sensorLoad() { } #endif + #if SENSEAIR_SUPPORT + { + SenseAirSensor * sensor = new SenseAirSensor(); + sensor->setRX(SENSEAIR_RX_PIN); + sensor->setTX(SENSEAIR_TX_PIN); + _sensors.push_back(sensor); + } + #endif + + #if SDS011_SUPPORT + { + SDS011Sensor * sensor = new SDS011Sensor(); + sensor->setRX(SDS011_RX_PIN); + sensor->setTX(SDS011_TX_PIN); + _sensors.push_back(sensor); + } + #endif + #if SHT3X_I2C_SUPPORT { SHT3XI2CSensor * sensor = new SHT3XI2CSensor(); @@ -795,6 +810,13 @@ void _sensorInit() { #endif // CSE7766_SUPPORT + #if PULSEMETER_SUPPORT + if (_sensors[i]->getID() == SENSOR_PULSEMETER_ID) { + PulseMeterSensor * sensor = (PulseMeterSensor *) _sensors[i]; + sensor->setEnergyRatio(getSetting("pwrRatioE", PULSEMETER_ENERGY_RATIO).toInt()); + } + #endif // PULSEMETER_SUPPORT + } } @@ -970,6 +992,17 @@ void _sensorConfigure() { #endif // CSE7766_SUPPORT + #if PULSEMETER_SUPPORT + if (_sensors[i]->getID() == SENSOR_PULSEMETER_ID) { + PulseMeterSensor * sensor = (PulseMeterSensor *) _sensors[i]; + if (getSetting("pwrResetE", 0).toInt() == 1) { + sensor->resetEnergy(); + delSetting("eneTotal"); + _sensorResetTS(); + } + } + #endif // PULSEMETER_SUPPORT + } // Update filter sizes diff --git a/code/espurna/sensors/PulseMeterSensor.h b/code/espurna/sensors/PulseMeterSensor.h new file mode 100644 index 00000000..dc293880 --- /dev/null +++ b/code/espurna/sensors/PulseMeterSensor.h @@ -0,0 +1,220 @@ +// ----------------------------------------------------------------------------- +// Pulse Meter Power Monitor Sensor +// Copyright (C) 2018 by Xose PĂ©rez +// ----------------------------------------------------------------------------- + +#if SENSOR_SUPPORT && PULSEMETER_SUPPORT + +#pragma once + +#include "Arduino.h" +#include "BaseSensor.h" + +class PulseMeterSensor : public BaseSensor { + + public: + + // --------------------------------------------------------------------- + // Public + // --------------------------------------------------------------------- + + PulseMeterSensor(): BaseSensor() { + _count = 2; + _sensor_id = SENSOR_PULSEMETER_ID; + } + + ~PulseMeterSensor() { + _enableInterrupts(false); + } + + void resetEnergy(double value = 0) { + _energy = value; + } + + // --------------------------------------------------------------------- + + void setGPIO(unsigned char gpio) { + if (_gpio == gpio) return; + _gpio = gpio; + _dirty = true; + } + + void setEnergyRatio(unsigned long ratio) { + _ratio = ratio; + } + + // --------------------------------------------------------------------- + + unsigned char getGPIO() { + return _gpio; + } + + unsigned char getEnergyRatio() { + return _ratio; + } + + // --------------------------------------------------------------------- + // Sensors API + // --------------------------------------------------------------------- + + // Initialization method, must be idempotent + // Defined outside the class body + void begin() { + + _enableInterrupts(true); + _ready = true; + + } + + // Descriptive name of the sensor + String description() { + char buffer[20]; + snprintf(buffer, sizeof(buffer), "PulseMeter @ GPIO(%u)", _gpio); + return String(buffer); + } + + // Descriptive name of the slot # index + String slot(unsigned char index) { + return description(); + }; + + // Address of the sensor (it could be the GPIO or I2C address) + String address(unsigned char index) { + return String(gpio); + } + + // Pre-read hook (usually to populate registers with up-to-date data) + void pre() { + + static unsigned long _previous_pulses = 0; + static unsigned long _previous_time = millis(); + + unsigned long lapse = millis() - _previous_time; + _previous_time = millis(); + unsigned long pulses = _pulses - _previous_pulses; + _previous_pulses = _pulses; + + unsigned long _energy_delta = 1000 * 3600 * pulses / _ratio; + _energy += _energy_delta; + _active = 1000 * _energy_delta / lapse; + + } + + // Type for slot # index + unsigned char type(unsigned char index) { + if (index == 0) return MAGNITUDE_POWER_ACTIVE; + if (index == 1) return MAGNITUDE_ENERGY; + return MAGNITUDE_NONE; + } + + // Current value for slot # index + double value(unsigned char index) { + if (index == 0) return _active; + if (index == 1) return _energy; + return 0; + } + + // Handle interrupt calls + void ICACHE_RAM_ATTR handleInterrupt(unsigned char gpio) { + if (gpio == _gpio) { + _pulses++; + } + } + + protected: + + // --------------------------------------------------------------------- + // Interrupt management + // --------------------------------------------------------------------- + + void _attach(PulseMeterSensor * instance, unsigned char gpio, unsigned char mode); + void _detach(unsigned char gpio); + + void _enableInterrupts(bool value) { + + static unsigned char _previous = GPIO_NONE; + + if (value) { + + if (_gpio != _previous) { + if (_previous != GPIO_NONE) _detach(_previous); + _attach(this, _gpio, PULSEMETER_INTERRUPT_ON); + _previous = _gpio; + } + + } else { + + _detach(_previous); + _previous = GPIO_NONE; + + } + + } + + // --------------------------------------------------------------------- + + unsigned char _gpio = GPIO_NONE; + unsigned long _ratio = 4000; + + double _active = 0; + double _energy = 0; + + volatile unsigned long _pulses = 0; + +}; + +// ----------------------------------------------------------------------------- +// Interrupt helpers +// ----------------------------------------------------------------------------- + +PulseMeterSensor * _pulsemeter_sensor_instance[10] = {NULL}; + +void ICACHE_RAM_ATTR _pulseMeter_sensor_isr(unsigned char gpio) { + unsigned char index = gpio > 5 ? gpio-6 : gpio; + if (_pulsemeter_sensor_instance[index]) { + _pulsemeter_sensor_instance[index]->handleInterrupt(gpio); + } +} + +void ICACHE_RAM_ATTR _pulsemeter_sensor_isr_0() { _pulsemeter_sensor_isr(0); } +void ICACHE_RAM_ATTR _pulsemeter_sensor_isr_1() { _pulsemeter_sensor_isr(1); } +void ICACHE_RAM_ATTR _pulsemeter_sensor_isr_2() { _pulsemeter_sensor_isr(2); } +void ICACHE_RAM_ATTR _pulsemeter_sensor_isr_3() { _pulsemeter_sensor_isr(3); } +void ICACHE_RAM_ATTR _pulsemeter_sensor_isr_4() { _pulsemeter_sensor_isr(4); } +void ICACHE_RAM_ATTR _pulsemeter_sensor_isr_5() { _pulsemeter_sensor_isr(5); } +void ICACHE_RAM_ATTR _pulsemeter_sensor_isr_12() { _pulsemeter_sensor_isr(12); } +void ICACHE_RAM_ATTR _pulsemeter_sensor_isr_13() { _pulsemeter_sensor_isr(13); } +void ICACHE_RAM_ATTR _pulsemeter_sensor_isr_14() { _pulsemeter_sensor_isr(14); } +void ICACHE_RAM_ATTR _pulsemeter_sensor_isr_15() { _pulsemeter_sensor_isr(15); } + +static void (*_pulsemeter_sensor_isr_list[10])() = { + _pulsemeter_sensor_isr_0, _pulsemeter_sensor_isr_1, _pulsemeter_sensor_isr_2, + _pulsemeter_sensor_isr_3, _pulsemeter_sensor_isr_4, _pulsemeter_sensor_isr_5, + _pulsemeter_sensor_isr_12, _pulsemeter_sensor_isr_13, _pulsemeter_sensor_isr_14, + _pulsemeter_sensor_isr_15 +}; + +void PulseMeterSensor::_attach(PulseMeterSensor * instance, unsigned char gpio, unsigned char mode) { + if (!gpioValid(gpio)) return; + _detach(gpio); + unsigned char index = gpio > 5 ? gpio-6 : gpio; + _pulsemeter_sensor_instance[index] = instance; + attachInterrupt(gpio, _pulsemeter_sensor_isr_list[index], mode); + #if SENSOR_DEBUG + DEBUG_MSG_P(PSTR("[SENSOR] GPIO%u interrupt attached to %s\n"), gpio, instance->description().c_str()); + #endif +} + +void PulseMeterSensor::_detach(unsigned char gpio) { + if (!gpioValid(gpio)) return; + unsigned char index = gpio > 5 ? gpio-6 : gpio; + if (_pulsemeter_sensor_instance[index]) { + detachInterrupt(gpio); + #if SENSOR_DEBUG + DEBUG_MSG_P(PSTR("[SENSOR] GPIO%u interrupt detached from %s\n"), gpio, _pulsemeter_sensor_instance[index]->description().c_str()); + #endif + _pulsemeter_sensor_instance[index] = NULL; + } +} + +#endif // SENSOR_SUPPORT && PULSEMETER_SUPPORT diff --git a/code/html/index.html b/code/html/index.html index c94743b4..1ef31ba6 100644 --- a/code/html/index.html +++ b/code/html/index.html @@ -1332,17 +1332,25 @@
In Watts (W). Calibrate your sensor connecting a pure resistive load (like a bulb) and enter here the its nominal power or use a multimeter.
+
+ +
+
+
+
Energy ratio in pulses/kWh.
+
+
-
+
Move this switch to ON and press "Save" to revert to factory calibration values.
-
+
-
+
Move this switch to ON and press "Save" to set energy count to 0.