From e5bf17d0190a9fe7f280a857e4611fed191add26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xose=20P=C3=A9rez?= Date: Thu, 21 Dec 2017 18:31:19 +0100 Subject: [PATCH] Ported V9261F power sensor to new sensors --- code/espurna/config/hardware.h | 2 +- code/espurna/config/sensors.h | 57 +++++-- code/espurna/espurna.ino | 6 + code/espurna/sensor.ino | 11 +- code/espurna/sensors/MHZ19Sensor.h | 2 +- code/espurna/sensors/PMSX003Sensor.h | 6 +- code/espurna/sensors/V9261FSensor.h | 242 +++++++++++++++++++++++++++ 7 files changed, 307 insertions(+), 19 deletions(-) create mode 100644 code/espurna/sensors/V9261FSensor.h diff --git a/code/espurna/config/hardware.h b/code/espurna/config/hardware.h index 0cb2880f..3e81321d 100644 --- a/code/espurna/config/hardware.h +++ b/code/espurna/config/hardware.h @@ -1026,7 +1026,7 @@ #define ALEXA_SUPPORT 0 // V9261F - #define POWER_PROVIDER POWER_PROVIDER_V9261F + #define V9261F_SUPPORT 1 #define V9261F_PIN 2 #define V9261F_PIN_INVERSE 1 diff --git a/code/espurna/config/sensors.h b/code/espurna/config/sensors.h index 6c8a1cde..a7fc6d8d 100644 --- a/code/espurna/config/sensors.h +++ b/code/espurna/config/sensors.h @@ -2,33 +2,33 @@ // SENSORS - General data // ============================================================================= -#define SENSOR_DEBUG 0 // Debug sensors +#define SENSOR_DEBUG 0 // Debug 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) +#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) #ifndef SENSOR_TEMPERATURE_UNITS -#define SENSOR_TEMPERATURE_UNITS TMP_CELSIUS // Temperature units (TMP_CELSIUS | TMP_FAHRENHEIT) +#define SENSOR_TEMPERATURE_UNITS TMP_CELSIUS // Temperature units (TMP_CELSIUS | TMP_FAHRENHEIT) #endif #ifndef SENSOR_TEMPERATURE_CORRECTION -#define SENSOR_TEMPERATURE_CORRECTION 0.0 // Offset correction +#define SENSOR_TEMPERATURE_CORRECTION 0.0 // Offset correction #endif #ifndef TEMPERATURE_MIN_CHANGE -#define TEMPERATURE_MIN_CHANGE 0.0 // Minimum temperature change to report +#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 +#define HUMIDITY_MIN_CHANGE 0 // Minimum humidity change to report #endif -#define HUMIDITY_NORMAL 0 -#define HUMIDITY_COMFORTABLE 1 -#define HUMIDITY_DRY 2 -#define HUMIDITY_WET 3 +#define HUMIDITY_NORMAL 0 +#define HUMIDITY_COMFORTABLE 1 +#define HUMIDITY_DRY 2 +#define HUMIDITY_WET 3 //-------------------------------------------------------------------------------- // Magnitudes @@ -240,7 +240,7 @@ //------------------------------------------------------------------------------ #ifndef EMON_ANALOG_SUPPORT -#define EMON_ANALOG_SUPPORT 1 // Do not build support by default +#define EMON_ANALOG_SUPPORT 0 // Do not build support by default #endif #if EMON_ANALOG_SUPPORT @@ -331,6 +331,32 @@ #define I2C_SUPPORT 1 #endif +//------------------------------------------------------------------------------ +// V9261F based power sensor +// Enable support by passing SI7021_SUPPORT=1 build flag +//------------------------------------------------------------------------------ + +#ifndef V9261F_SUPPORT +#define V9261F_SUPPORT 0 +#endif + +#ifndef V9261F_PIN +#define V9261F_PIN 2 // TX pin from the V9261F +#endif + +#ifndef V9261F_PIN_INVERSE +#define V9261F_PIN_INVERSE 1 // Signal is inverted +#endif + +#define V9261F_SYNC_INTERVAL 600 // Sync signal length (ms) +#define V9261F_BAUDRATE 4800 // UART baudrate + +// Default ratios +#define V9261F_CURRENT_FACTOR 79371434.0 +#define V9261F_VOLTAGE_FACTOR 4160651.0 +#define V9261F_POWER_FACTOR 153699.0 +#define V9261F_RPOWER_FACTOR V9261F_CURRENT_FACTOR + // ============================================================================= // Sensor helpers configuration // ============================================================================= @@ -433,3 +459,8 @@ #if SHT3X_I2C_SUPPORT #include "sensors/SHT3XI2CSensor.h" #endif + +#if V9261F_SUPPORT + #include + #include "sensors/V9261FSensor.h" +#endif diff --git a/code/espurna/espurna.ino b/code/espurna/espurna.ino index 7419a550..6a288bac 100644 --- a/code/espurna/espurna.ino +++ b/code/espurna/espurna.ino @@ -233,9 +233,15 @@ void welcome() { #if PMSX003_SUPPORT DEBUG_MSG_P(PSTR(" PMSX003")); #endif + #if SHT3X_I2C_SUPPORT + DEBUG_MSG_P(PSTR(" SHT3X_I2C")); + #endif #if SI7021_SUPPORT DEBUG_MSG_P(PSTR(" SI7021")); #endif + #if V9261F_SUPPORT + DEBUG_MSG_P(PSTR(" V9261F")); + #endif DEBUG_MSG_P(PSTR("\n\n")); diff --git a/code/espurna/sensor.ino b/code/espurna/sensor.ino index d230f817..5e16176f 100644 --- a/code/espurna/sensor.ino +++ b/code/espurna/sensor.ino @@ -353,6 +353,15 @@ void _sensorInit() { } #endif + #if V9261F_SUPPORT + { + V9261FSensor * sensor = new V9261FSensor(); + sensor->setRX(V9261F_PIN); + sensor->setInverted(V9261F_PIN_INVERSE); + _sensors.push_back(sensor); + } + #endif + } void _sensorConfigure() { @@ -424,7 +433,7 @@ void _magnitudesInit() { if (type == MAGNITUDE_DIGITAL) { new_magnitude.filter = new MaxFilter(); } else if (type == MAGNITUDE_EVENTS) { - new_magnitude.filter = new MovingAverageFilter(SENSOR_REPORT_EVERY); + new_magnitude.filter = new MovingAverageFilter(); } else { new_magnitude.filter = new MedianFilter(); } diff --git a/code/espurna/sensors/MHZ19Sensor.h b/code/espurna/sensors/MHZ19Sensor.h index 947b8f70..d4769511 100644 --- a/code/espurna/sensors/MHZ19Sensor.h +++ b/code/espurna/sensors/MHZ19Sensor.h @@ -193,6 +193,6 @@ class MHZ19Sensor : public BaseSensor { double _co2 = 0; unsigned int _pin_rx; unsigned int _pin_tx; - SoftwareSerial * _serial; + SoftwareSerial * _serial = NULL; }; diff --git a/code/espurna/sensors/PMSX003Sensor.h b/code/espurna/sensors/PMSX003Sensor.h index 955de28e..266a0a3c 100644 --- a/code/espurna/sensors/PMSX003Sensor.h +++ b/code/espurna/sensors/PMSX003Sensor.h @@ -118,7 +118,7 @@ class PMSX003Sensor : public BaseSensor { } _pms->requestRead(); - + } // Current value for slot # index @@ -140,8 +140,8 @@ class PMSX003Sensor : public BaseSensor { unsigned int _pin_rx; unsigned int _pin_tx; unsigned long _startTime; - SoftwareSerial * _serial; - PMS * _pms; + SoftwareSerial * _serial = NULL; + PMS * _pms = NULL; PMS::DATA _data; }; diff --git a/code/espurna/sensors/V9261FSensor.h b/code/espurna/sensors/V9261FSensor.h new file mode 100644 index 00000000..75c63cc7 --- /dev/null +++ b/code/espurna/sensors/V9261FSensor.h @@ -0,0 +1,242 @@ +// ----------------------------------------------------------------------------- +// V9261F based power monitor +// Copyright (C) 2017 by Xose PĂ©rez +// ----------------------------------------------------------------------------- + +#pragma once + +#include "Arduino.h" +#include "BaseSensor.h" + +#include + +class V9261FSensor : public BaseSensor { + + public: + + // --------------------------------------------------------------------- + // Public + // --------------------------------------------------------------------- + + V9261FSensor(): BaseSensor() { + _count = 6; + _sensor_id = SENSOR_V9261F_ID; + } + + // --------------------------------------------------------------------- + + void setRX(unsigned char pin_rx) { + if (_pin_rx == pin_rx) return; + _pin_rx = pin_rx; + _dirty = true; + } + + void setInverted(bool inverted) { + if (_inverted == inverted) return; + _inverted = inverted; + _dirty = true; + } + + // --------------------------------------------------------------------- + + unsigned char getRX() { + return _pin_rx; + } + + bool getInverted() { + return _inverted; + } + + // --------------------------------------------------------------------- + // Sensor API + // --------------------------------------------------------------------- + + // Initialization method, must be idempotent + void begin() { + + if (!_dirty) return; + _dirty = false; + + if (_serial) delete _serial; + + _serial = new SoftwareSerial(_pin_rx, SW_SERIAL_UNUSED_PIN, _inverted, 256); + _serial->begin(V9261F_BAUDRATE); + + } + + // Descriptive name of the sensor + String description() { + char buffer[28]; + snprintf(buffer, sizeof(buffer), "V9261F @ SwSerial(%i,NULL)", _pin_rx); + return String(buffer); + } + + // Loop-like method, call it in your main loop + void tick() { + _read(); + } + + // Type for slot # index + magnitude_t type(unsigned char index) { + _error = SENSOR_ERROR_OK; + if (index == 0) return MAGNITUDE_CURRENT; + if (index == 1) return MAGNITUDE_VOLTAGE; + if (index == 2) return MAGNITUDE_POWER_ACTIVE; + if (index == 3) return MAGNITUDE_POWER_REACTIVE; + if (index == 4) return MAGNITUDE_POWER_APPARENT; + if (index == 5) return MAGNITUDE_POWER_FACTOR; + _error = SENSOR_ERROR_OUT_OF_RANGE; + return MAGNITUDE_NONE; + } + + // Current value for slot # index + double value(unsigned char index) { + _error = SENSOR_ERROR_OK; + if (index == 0) return _current; + if (index == 1) return _voltage; + if (index == 2) return _active; + if (index == 3) return _reactive; + if (index == 4) return _apparent; + if (index == 5) return _apparent > 0 ? 100 * _active / _apparent : 100; + _error = SENSOR_ERROR_OUT_OF_RANGE; + return 0; + } + + protected: + + // --------------------------------------------------------------------- + // Protected + // --------------------------------------------------------------------- + + void _read() { + + static unsigned char state = 0; + static unsigned long last = 0; + static bool found = false; + static unsigned char index = 0; + + if (state == 0) { + + while (_serial->available()) { + _serial->flush(); + found = true; + last = millis(); + } + + if (found && (millis() - last > V9261F_SYNC_INTERVAL)) { + _serial->flush(); + index = 0; + state = 1; + } + + } else if (state == 1) { + + while (_serial->available()) { + _serial->read(); + if (index++ >= 7) { + _serial->flush(); + index = 0; + state = 2; + } + } + + } else if (state == 2) { + + while (_serial->available()) { + _data[index] = _serial->read(); + if (index++ >= 19) { + _serial->flush(); + last = millis(); + state = 3; + } + } + + } else if (state == 3) { + + if (_checksum()) { + + _active = (double) ( + (_data[3]) + + (_data[4] << 8) + + (_data[5] << 16) + + (_data[6] << 24) + ) / _ratioP; + + _reactive = (double) ( + (_data[7]) + + (_data[8] << 8) + + (_data[9] << 16) + + (_data[10] << 24) + ) / _ratioR; + + _voltage = (double) ( + (_data[11]) + + (_data[12] << 8) + + (_data[13] << 16) + + (_data[14] << 24) + ) / _ratioV; + + _current = (double) ( + (_data[15]) + + (_data[16] << 8) + + (_data[17] << 16) + + (_data[18] << 24) + ) / _ratioC; + + if (_active < 0) _active = 0; + if (_reactive < 0) _reactive = 0; + if (_voltage < 0) _voltage = 0; + if (_current < 0) _current = 0; + + _apparent = sqrt(_reactive * _reactive + _active * _active); + + } + + last = millis(); + index = 0; + state = 4; + + } else if (state == 4) { + + while (_serial->available()) { + _serial->flush(); + last = millis(); + } + + if (millis() - last > V9261F_SYNC_INTERVAL) { + state = 1; + } + + } + + } + + bool _checksum() { + unsigned char checksum = 0; + for (unsigned char i = 0; i < 19; i++) { + checksum = checksum + _data[i]; + } + checksum = ~checksum + 0x33; + return checksum == _data[19]; + } + + // --------------------------------------------------------------------- + + unsigned int _pin_rx = V9261F_PIN; + bool _inverted = V9261F_PIN_INVERSE; + SoftwareSerial * _serial = NULL; + + double _active = 0; + double _reactive = 0; + double _voltage = 0; + double _current = 0; + double _apparent = 0; + + double _ratioP = V9261F_POWER_FACTOR; + double _ratioC = V9261F_CURRENT_FACTOR; + double _ratioV = V9261F_VOLTAGE_FACTOR; + double _ratioR = V9261F_RPOWER_FACTOR; + + unsigned char _data[24]; + +};