Browse Source

Initial version of the PulseMeter sensor

alexa
Xose Pérez 6 years ago
parent
commit
facb89e529
9 changed files with 385 additions and 90 deletions
  1. +2
    -2
      README.md
  2. +1
    -0
      code/espurna/config/arduino.h
  3. +2
    -0
      code/espurna/config/hardware.h
  4. +3
    -0
      code/espurna/config/progmem.h
  5. +65
    -38
      code/espurna/config/sensors.h
  6. +30
    -29
      code/espurna/config/types.h
  7. +51
    -18
      code/espurna/sensor.ino
  8. +220
    -0
      code/espurna/sensors/PulseMeterSensor.h
  9. +11
    -3
      code/html/index.html

+ 2
- 2
README.md View File

@ -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)
<br />


+ 1
- 0
code/espurna/config/arduino.h View File

@ -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


+ 2
- 0
code/espurna/config/hardware.h View File

@ -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


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

@ -199,6 +199,9 @@ PROGMEM const char espurna_sensors[] =
#if PMSX003_SUPPORT
"PMSX003 "
#endif
#if PULSEMETER_SUPPORT
"PULSEMETER "
#endif
#if PZEM004T_SUPPORT
"PZEM004T "
#endif


+ 65
- 38
code/espurna/config/sensors.h View File

@ -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


+ 30
- 29
code/espurna/config/types.h View File

@ -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


+ 51
- 18
code/espurna/sensor.ino View File

@ -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


+ 220
- 0
code/espurna/sensors/PulseMeterSensor.h View File

@ -0,0 +1,220 @@
// -----------------------------------------------------------------------------
// Pulse Meter Power Monitor Sensor
// Copyright (C) 2018 by Xose Pérez <xose dot perez at gmail dot com>
// -----------------------------------------------------------------------------
#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

+ 11
- 3
code/html/index.html View File

@ -1332,17 +1332,25 @@
<div class="pure-u-1 pure-u-lg-3-4 hint">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.</div>
</div>
<div class="pure-g module module-pm">
<label class="pure-u-1 pure-u-lg-1-4">Energy Ratio</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="pwrRatioE" tabindex="55" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint">Energy ratio in pulses/kWh.</div>
</div>
<div class="pure-g module module-hlw module-cse module-emon">
<label class="pure-u-1 pure-u-lg-1-4">Reset calibration</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="pwrResetCalibration" tabindex="55" /></div>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="pwrResetCalibration" tabindex="56" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint">Move this switch to ON and press "Save" to revert to factory calibration values.</div>
</div>
<div class="pure-g module module-hlw module-cse module-emon">
<div class="pure-g module module-hlw module-cse module-emon module-pm">
<label class="pure-u-1 pure-u-lg-1-4">Reset energy</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="pwrResetE" tabindex="56" /></div>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="pwrResetE" tabindex="57" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint">Move this switch to ON and press "Save" to set energy count to 0.</div>


Loading…
Cancel
Save