@ -6,20 +6,202 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
*/
*/
# include "sensor.h"
# if SENSOR_SUPPORT
# if SENSOR_SUPPORT
# include <vector>
# include <vector>
# include <float.h>
# include <float.h>
# include "api.h"
# include "broker.h"
# include "broker.h"
# include "domoticz.h"
# include "domoticz.h"
# include "i2c.h"
# include "mqtt.h"
# include "mqtt.h"
# include "ntp.h"
# include "ntp.h"
# include "relay.h"
# include "relay.h"
# include "sensor.h"
# include "terminal.h"
# include "terminal.h"
# include "thingspeak.h"
# include "rtcmem.h"
# include "ws.h"
# include "ws.h"
//--------------------------------------------------------------------------------
// TODO: namespace { ... } ? sensor ctors need to work though
# include "filters/LastFilter.h"
# include "filters/MaxFilter.h"
# include "filters/MedianFilter.h"
# include "filters/MovingAverageFilter.h"
# include "filters/SumFilter.h"
# include "sensors/BaseSensor.h"
# include "sensors/BaseEmonSensor.h"
# include "sensors/BaseAnalogSensor.h"
# if AM2320_SUPPORT
# include "sensors/AM2320Sensor.h"
# endif
# if ANALOG_SUPPORT
# include "sensors/AnalogSensor.h"
# endif
# if BH1750_SUPPORT
# include "sensors/BH1750Sensor.h"
# endif
# if BMP180_SUPPORT
# include "sensors/BMP180Sensor.h"
# endif
# if BMX280_SUPPORT
# include "sensors/BMX280Sensor.h"
# endif
# if CSE7766_SUPPORT
# include "sensors/CSE7766Sensor.h"
# endif
# if DALLAS_SUPPORT
# include "sensors/DallasSensor.h"
# endif
# if DHT_SUPPORT
# include "sensors/DHTSensor.h"
# endif
# if DIGITAL_SUPPORT
# include "sensors/DigitalSensor.h"
# endif
# if ECH1560_SUPPORT
# include "sensors/ECH1560Sensor.h"
# endif
# if EMON_ADC121_SUPPORT
# include "sensors/EmonADC121Sensor.h"
# endif
# if EMON_ADS1X15_SUPPORT
# include "sensors/EmonADS1X15Sensor.h"
# endif
# if EMON_ANALOG_SUPPORT
# include "sensors/EmonAnalogSensor.h"
# endif
# if EVENTS_SUPPORT
# include "sensors/EventSensor.h"
# endif
# if EZOPH_SUPPORT
# include "sensors/EZOPHSensor.h"
# endif
# if GEIGER_SUPPORT
# include "sensors/GeigerSensor.h"
# endif
# if GUVAS12SD_SUPPORT
# include "sensors/GUVAS12SDSensor.h"
# endif
# if HLW8012_SUPPORT
# include "sensors/HLW8012Sensor.h"
# endif
# if LDR_SUPPORT
# include "sensors/LDRSensor.h"
# endif
# if MAX6675_SUPPORT
# include "sensors/MAX6675Sensor.h"
# endif
# if MICS2710_SUPPORT
# include "sensors/MICS2710Sensor.h"
# endif
# if MICS5525_SUPPORT
# include "sensors/MICS5525Sensor.h"
# endif
# if MHZ19_SUPPORT
# include "sensors/MHZ19Sensor.h"
# endif
# if NTC_SUPPORT
# include "sensors/NTCSensor.h"
# endif
# if SDS011_SUPPORT
# include "sensors/SDS011Sensor.h"
# endif
# if SENSEAIR_SUPPORT
# include "sensors/SenseAirSensor.h"
# endif
# if PMSX003_SUPPORT
# include "sensors/PMSX003Sensor.h"
# endif
# if PULSEMETER_SUPPORT
# include "sensors/PulseMeterSensor.h"
# endif
# if PZEM004T_SUPPORT
# include "sensors/PZEM004TSensor.h"
# endif
# if SHT3X_I2C_SUPPORT
# include "sensors/SHT3XI2CSensor.h"
# endif
# if SI7021_SUPPORT
# include "sensors/SI7021Sensor.h"
# endif
# if SONAR_SUPPORT
# include "sensors/SonarSensor.h"
# endif
# if T6613_SUPPORT
# include "sensors/T6613Sensor.h"
# endif
# if TMP3X_SUPPORT
# include "sensors/TMP3XSensor.h"
# endif
# if V9261F_SUPPORT
# include "sensors/V9261FSensor.h"
# endif
# if VEML6075_SUPPORT
# include "sensors/VEML6075Sensor.h"
# endif
# if VL53L1X_SUPPORT
# include "sensors/VL53L1XSensor.h"
# endif
# if ADE7953_SUPPORT
# include "sensors/ADE7953Sensor.h"
# endif
# if SI1145_SUPPORT
# include "sensors/SI1145Sensor.h"
# endif
# if HDC1080_SUPPORT
# include "sensors/HDC1080Sensor.h"
# endif
//--------------------------------------------------------------------------------
struct sensor_magnitude_t {
struct sensor_magnitude_t {
private :
private :
@ -149,10 +331,108 @@ void Energy::reset() {
} // namespace sensor
} // namespace sensor
// -----------------------------------------------------------------------------
// Energy persistence
// -----------------------------------------------------------------------------
std : : vector < unsigned char > _sensor_save_count ;
unsigned char _sensor_save_every = SENSOR_SAVE_EVERY ;
bool _sensorIsEmon ( BaseSensor * sensor ) {
bool _sensorIsEmon ( BaseSensor * sensor ) {
return sensor - > type ( ) & sensor : : type : : Emon ;
return sensor - > type ( ) & sensor : : type : : Emon ;
}
}
sensor : : Energy _sensorRtcmemLoadEnergy ( unsigned char index ) {
return sensor : : Energy {
sensor : : KWh { Rtcmem - > energy [ index ] . kwh } ,
sensor : : Ws { Rtcmem - > energy [ index ] . ws }
} ;
}
void _sensorRtcmemSaveEnergy ( unsigned char index , const sensor : : Energy & source ) {
Rtcmem - > energy [ index ] . kwh = source . kwh . value ;
Rtcmem - > energy [ index ] . ws = source . ws . value ;
}
sensor : : Energy _sensorParseEnergy ( const String & value ) {
sensor : : Energy result ;
const bool separator = value . indexOf ( ' + ' ) > 0 ;
if ( value . length ( ) & & ( separator > 0 ) ) {
const String before = value . substring ( 0 , separator ) ;
const String after = value . substring ( separator + 1 ) ;
result . kwh = strtoul ( before . c_str ( ) , nullptr , 10 ) ;
result . ws = strtoul ( after . c_str ( ) , nullptr , 10 ) ;
}
return result ;
}
void _sensorApiResetEnergy ( const sensor_magnitude_t & magnitude , const char * payload ) {
if ( ! payload | | ! strlen ( payload ) ) return ;
if ( payload [ 0 ] ! = ' 0 ' ) return ;
auto * sensor = static_cast < BaseEmonSensor * > ( magnitude . sensor ) ;
auto energy = _sensorParseEnergy ( payload ) ;
sensor - > resetEnergy ( magnitude . global , energy ) ;
}
sensor : : Energy _sensorEnergyTotal ( unsigned char index ) {
sensor : : Energy result ;
if ( rtcmemStatus ( ) & & ( index < ( sizeof ( Rtcmem - > energy ) / sizeof ( * Rtcmem - > energy ) ) ) ) {
result = _sensorRtcmemLoadEnergy ( index ) ;
} else if ( _sensor_save_every > 0 ) {
result = _sensorParseEnergy ( getSetting ( { " eneTotal " , index } ) ) ;
}
return result ;
}
sensor : : Energy sensorEnergyTotal ( ) {
return _sensorEnergyTotal ( 0 ) ;
}
void _sensorResetEnergyTotal ( unsigned char index ) {
delSetting ( { " eneTotal " , index } ) ;
delSetting ( { " eneTime " , index } ) ;
if ( index < ( sizeof ( Rtcmem - > energy ) / sizeof ( * Rtcmem - > energy ) ) ) {
Rtcmem - > energy [ index ] . kwh = 0 ;
Rtcmem - > energy [ index ] . ws = 0 ;
}
}
void _magnitudeSaveEnergyTotal ( sensor_magnitude_t & magnitude , bool persistent ) {
if ( magnitude . type ! = MAGNITUDE_ENERGY ) return ;
auto * sensor = static_cast < BaseEmonSensor * > ( magnitude . sensor ) ;
const auto energy = sensor - > totalEnergy ( ) ;
// Always save to RTCMEM
if ( magnitude . global < ( sizeof ( Rtcmem - > energy ) / sizeof ( * Rtcmem - > energy ) ) ) {
_sensorRtcmemSaveEnergy ( magnitude . global , energy ) ;
}
// Save to EEPROM every '_sensor_save_every' readings
// Format is `<kwh>+<ws>`, value without `+` is treated as `<ws>`
if ( persistent & & _sensor_save_every ) {
_sensor_save_count [ magnitude . global ] =
( _sensor_save_count [ magnitude . global ] + 1 ) % _sensor_save_every ;
if ( 0 = = _sensor_save_count [ magnitude . global ] ) {
const String total = String ( energy . kwh . value ) + " + " + String ( energy . ws . value ) ;
setSetting ( { " eneTotal " , magnitude . global } , total ) ;
# if NTP_SUPPORT
if ( ntpSynced ( ) ) setSetting ( { " eneTime " , magnitude . global } , ntpDateTime ( ) ) ;
# endif
}
}
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
std : : vector < BaseSensor * > _sensors ;
std : : vector < BaseSensor * > _sensors ;
@ -163,10 +443,6 @@ bool _sensor_realtime = API_REAL_TIME_VALUES;
unsigned long _sensor_read_interval = 1000 * SENSOR_READ_INTERVAL ;
unsigned long _sensor_read_interval = 1000 * SENSOR_READ_INTERVAL ;
unsigned char _sensor_report_every = SENSOR_REPORT_EVERY ;
unsigned char _sensor_report_every = SENSOR_REPORT_EVERY ;
// Energy persistence
std : : vector < unsigned char > _sensor_save_count ;
unsigned char _sensor_save_every = SENSOR_SAVE_EVERY ;
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Private
// Private
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
@ -369,11 +645,11 @@ String magnitudeTopic(unsigned char type) {
}
}
String magnitudeTopic ( const sensor_magnitude_t & magnitude ) {
String _ magnitudeTopic( const sensor_magnitude_t & magnitude ) {
return magnitudeTopic ( magnitude . type ) ;
return magnitudeTopic ( magnitude . type ) ;
}
}
String magnitudeUnits ( const sensor_magnitude_t & magnitude ) {
String _ magnitudeUnits( const sensor_magnitude_t & magnitude ) {
const __FlashStringHelper * result = nullptr ;
const __FlashStringHelper * result = nullptr ;
@ -457,7 +733,7 @@ String magnitudeUnits(const sensor_magnitude_t& magnitude) {
String magnitudeUnits ( unsigned char index ) {
String magnitudeUnits ( unsigned char index ) {
if ( index > = magnitudeCount ( ) ) return String ( ) ;
if ( index > = magnitudeCount ( ) ) return String ( ) ;
return magnitudeUnits ( _magnitudes [ index ] ) ;
return _ magnitudeUnits( _magnitudes [ index ] ) ;
}
}
// Choose unit based on type of magnitude we use
// Choose unit based on type of magnitude we use
@ -548,11 +824,12 @@ double _magnitudeProcess(const sensor_magnitude_t& magnitude, double value) {
# if WEB_SUPPORT
# if WEB_SUPPORT
//void _sensorWebSocketMagnitudes(JsonObject& root, const String& ws_name, const String& conf_name) {
template < typename T > void _sensorWebSocketMagnitudes ( JsonObject & root , T prefix ) {
// Used by modules to generate magnitude_id<->module_id mapping for the WebUI
void sensorWebSocketMagnitudes ( JsonObject & root , const String & prefix ) {
// ws produces flat list <prefix>Magnitudes
// ws produces flat list <prefix>Magnitudes
const String ws_name = String ( prefix ) + " Magnitudes " ;
const String ws_name = prefix + " Magnitudes " ;
// config uses <prefix>Magnitude<index> (cut 's')
// config uses <prefix>Magnitude<index> (cut 's')
const String conf_name = ws_name . substring ( 0 , ws_name . length ( ) - 1 ) ;
const String conf_name = ws_name . substring ( 0 , ws_name . length ( ) - 1 ) ;
@ -560,33 +837,17 @@ template<typename T> void _sensorWebSocketMagnitudes(JsonObject& root, T prefix)
JsonObject & list = root . createNestedObject ( ws_name ) ;
JsonObject & list = root . createNestedObject ( ws_name ) ;
list [ " size " ] = magnitudeCount ( ) ;
list [ " size " ] = magnitudeCount ( ) ;
//JsonArray& name = list.createNestedArray("name");
JsonArray & type = list . createNestedArray ( " type " ) ;
JsonArray & type = list . createNestedArray ( " type " ) ;
JsonArray & index = list . createNestedArray ( " index " ) ;
JsonArray & index = list . createNestedArray ( " index " ) ;
JsonArray & idx = list . createNestedArray ( " idx " ) ;
JsonArray & idx = list . createNestedArray ( " idx " ) ;
for ( unsigned char i = 0 ; i < magnitudeCount ( ) ; + + i ) {
for ( unsigned char i = 0 ; i < magnitudeCount ( ) ; + + i ) {
//name.add(magnitudeName(i));
type . add ( magnitudeType ( i ) ) ;
type . add ( magnitudeType ( i ) ) ;
index . add ( magnitudeIndex ( i ) ) ;
index . add ( magnitudeIndex ( i ) ) ;
idx . add ( getSetting ( { conf_name , i } , 0 ) ) ;
idx . add ( getSetting ( { conf_name , i } , 0 ) ) ;
}
}
}
}
/*
template < typename T > void _sensorWebSocketMagnitudes ( JsonObject & root , T prefix ) {
// ws produces flat list <prefix>Magnitudes
const String ws_name = String ( prefix ) + " Magnitudes " ;
// config uses <prefix>Magnitude<index> (cut 's')
const String conf_name = ws_name . substring ( 0 , ws_name . length ( ) - 1 ) ;
_sensorWebSocketMagnitudes ( root , ws_name , conf_name ) ;
}
*/
bool _sensorWebSocketOnKeyCheck ( const char * key , JsonVariant & value ) {
bool _sensorWebSocketOnKeyCheck ( const char * key , JsonVariant & value ) {
if ( strncmp ( key , " pwr " , 3 ) = = 0 ) return true ;
if ( strncmp ( key , " pwr " , 3 ) = = 0 ) return true ;
if ( strncmp ( key , " sns " , 3 ) = = 0 ) return true ;
if ( strncmp ( key , " sns " , 3 ) = = 0 ) return true ;
@ -629,7 +890,7 @@ void _sensorWebSocketMagnitudesConfig(JsonObject& root) {
index . add < uint8_t > ( magnitude . global ) ;
index . add < uint8_t > ( magnitude . global ) ;
type . add < uint8_t > ( magnitude . type ) ;
type . add < uint8_t > ( magnitude . type ) ;
units . add ( magnitudeUnits ( magnitude ) ) ;
units . add ( _ magnitudeUnits( magnitude ) ) ;
{
{
String sensor_desc = magnitude . sensor - > slot ( magnitude . local ) ;
String sensor_desc = magnitude . sensor - > slot ( magnitude . local ) ;
@ -873,98 +1134,6 @@ void _sensorPost() {
}
}
}
}
sensor : : Energy _sensorRtcmemLoadEnergy ( unsigned char index ) {
return sensor : : Energy {
sensor : : KWh { Rtcmem - > energy [ index ] . kwh } ,
sensor : : Ws { Rtcmem - > energy [ index ] . ws }
} ;
}
void _sensorRtcmemSaveEnergy ( unsigned char index , const sensor : : Energy & source ) {
Rtcmem - > energy [ index ] . kwh = source . kwh . value ;
Rtcmem - > energy [ index ] . ws = source . ws . value ;
}
sensor : : Energy _sensorParseEnergy ( const String & value ) {
sensor : : Energy result ;
const bool separator = value . indexOf ( ' + ' ) > 0 ;
if ( value . length ( ) & & ( separator > 0 ) ) {
const String before = value . substring ( 0 , separator ) ;
const String after = value . substring ( separator + 1 ) ;
result . kwh = strtoul ( before . c_str ( ) , nullptr , 10 ) ;
result . ws = strtoul ( after . c_str ( ) , nullptr , 10 ) ;
}
return result ;
}
void _sensorApiResetEnergy ( const sensor_magnitude_t & magnitude , const char * payload ) {
if ( ! payload | | ! strlen ( payload ) ) return ;
if ( payload [ 0 ] ! = ' 0 ' ) return ;
auto * sensor = static_cast < BaseEmonSensor * > ( magnitude . sensor ) ;
auto energy = _sensorParseEnergy ( payload ) ;
sensor - > resetEnergy ( magnitude . global , energy ) ;
}
sensor : : Energy _sensorEnergyTotal ( unsigned char index ) {
sensor : : Energy result ;
if ( rtcmemStatus ( ) & & ( index < ( sizeof ( Rtcmem - > energy ) / sizeof ( * Rtcmem - > energy ) ) ) ) {
result = _sensorRtcmemLoadEnergy ( index ) ;
} else if ( _sensor_save_every > 0 ) {
result = _sensorParseEnergy ( getSetting ( { " eneTotal " , index } ) ) ;
}
return result ;
}
sensor : : Energy sensorEnergyTotal ( ) {
return _sensorEnergyTotal ( 0 ) ;
}
void _sensorResetEnergyTotal ( unsigned char index ) {
delSetting ( { " eneTotal " , index } ) ;
delSetting ( { " eneTime " , index } ) ;
if ( index < ( sizeof ( Rtcmem - > energy ) / sizeof ( * Rtcmem - > energy ) ) ) {
Rtcmem - > energy [ index ] . kwh = 0 ;
Rtcmem - > energy [ index ] . ws = 0 ;
}
}
void _magnitudeSaveEnergyTotal ( sensor_magnitude_t & magnitude , bool persistent ) {
if ( magnitude . type ! = MAGNITUDE_ENERGY ) return ;
auto * sensor = static_cast < BaseEmonSensor * > ( magnitude . sensor ) ;
const auto energy = sensor - > totalEnergy ( ) ;
// Always save to RTCMEM
if ( magnitude . global < ( sizeof ( Rtcmem - > energy ) / sizeof ( * Rtcmem - > energy ) ) ) {
_sensorRtcmemSaveEnergy ( magnitude . global , energy ) ;
}
// Save to EEPROM every '_sensor_save_every' readings
// Format is `<kwh>+<ws>`, value without `+` is treated as `<ws>`
if ( persistent & & _sensor_save_every ) {
_sensor_save_count [ magnitude . global ] =
( _sensor_save_count [ magnitude . global ] + 1 ) % _sensor_save_every ;
if ( 0 = = _sensor_save_count [ magnitude . global ] ) {
const String total = String ( energy . kwh . value ) + " + " + String ( energy . ws . value ) ;
setSetting ( { " eneTotal " , magnitude . global } , total ) ;
# if NTP_SUPPORT
if ( ntpSynced ( ) ) setSetting ( { " eneTime " , magnitude . global } , ntpDateTime ( ) ) ;
# endif
}
}
}
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Sensor initialization
// Sensor initialization
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
@ -1565,6 +1734,47 @@ void _sensorLoad() {
# endif
# endif
}
}
void _sensorReport ( unsigned char index , double value ) {
const auto & magnitude = _magnitudes . at ( index ) ;
// XXX: ensure that the received 'value' will fit here
// dtostrf 2nd arg only controls leading zeroes and the
// 3rd is only for the part after the dot
char buffer [ 64 ] ;
dtostrf ( value , 1 , magnitude . decimals , buffer ) ;
# if BROKER_SUPPORT
SensorReportBroker : : Publish ( magnitudeTopic ( magnitude . type ) , magnitude . global , value , buffer ) ;
# endif
# if MQTT_SUPPORT
mqttSend ( magnitudeTopicIndex ( index ) . c_str ( ) , buffer ) ;
# if SENSOR_PUBLISH_ADDRESSES
char topic [ 32 ] ;
snprintf ( topic , sizeof ( topic ) , " %s/%s " , SENSOR_ADDRESS_TOPIC , magnitudeTopic ( magnitude . type ) . c_str ( ) ) ;
if ( SENSOR_USE_INDEX | | ( sensor_magnitude_t : : counts ( magnitude . type ) > 1 ) ) {
mqttSend ( topic , magnitude . global , magnitude . sensor - > address ( magnitude . local ) . c_str ( ) ) ;
} else {
mqttSend ( topic , magnitude . sensor - > address ( magnitude . local ) . c_str ( ) ) ;
}
# endif // SENSOR_PUBLISH_ADDRESSES
# endif // MQTT_SUPPORT
# if THINGSPEAK_SUPPORT
tspkEnqueueMeasurement ( index , buffer ) ;
# endif // THINGSPEAK_SUPPORT
# if DOMOTICZ_SUPPORT
domoticzSendMagnitude ( magnitude . type , index , value , buffer ) ;
# endif // DOMOTICZ_SUPPORT
}
void _sensorCallback ( unsigned char i , unsigned char type , double value ) {
void _sensorCallback ( unsigned char i , unsigned char type , double value ) {
DEBUG_MSG_P ( PSTR ( " [SENSOR] Sensor #%u callback, type %u, payload: '%s' \n " ) , i , type , String ( value ) . c_str ( ) ) ;
DEBUG_MSG_P ( PSTR ( " [SENSOR] Sensor #%u callback, type %u, payload: '%s' \n " ) , i , type , String ( value ) . c_str ( ) ) ;
@ -1864,46 +2074,6 @@ void _sensorConfigure() {
}
}
void _sensorReport ( unsigned char index , double value ) {
const auto & magnitude = _magnitudes . at ( index ) ;
// XXX: ensure that the received 'value' will fit here
// dtostrf 2nd arg only controls leading zeroes and the
// 3rd is only for the part after the dot
char buffer [ 64 ] ;
dtostrf ( value , 1 , magnitude . decimals , buffer ) ;
# if BROKER_SUPPORT
SensorReportBroker : : Publish ( magnitudeTopic ( magnitude . type ) , magnitude . global , value , buffer ) ;
# endif
# if MQTT_SUPPORT
mqttSend ( magnitudeTopicIndex ( index ) . c_str ( ) , buffer ) ;
# if SENSOR_PUBLISH_ADDRESSES
char topic [ 32 ] ;
snprintf ( topic , sizeof ( topic ) , " %s/%s " , SENSOR_ADDRESS_TOPIC , magnitudeTopic ( magnitude . type ) . c_str ( ) ) ;
if ( SENSOR_USE_INDEX | | ( sensor_magnitude_t : : counts ( magnitude . type ) > 1 ) ) {
mqttSend ( topic , magnitude . global , magnitude . sensor - > address ( magnitude . local ) . c_str ( ) ) ;
} else {
mqttSend ( topic , magnitude . sensor - > address ( magnitude . local ) . c_str ( ) ) ;
}
# endif // SENSOR_PUBLISH_ADDRESSES
# endif // MQTT_SUPPORT
# if THINGSPEAK_SUPPORT
tspkEnqueueMeasurement ( index , buffer ) ;
# endif // THINGSPEAK_SUPPORT
# if DOMOTICZ_SUPPORT
domoticzSendMagnitude ( magnitude . type , index , value , buffer ) ;
# endif // DOMOTICZ_SUPPORT
}
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Public
// Public
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
@ -2139,7 +2309,7 @@ void sensorLoop() {
magnitude . sensor - > slot ( magnitude . local ) . c_str ( ) ,
magnitude . sensor - > slot ( magnitude . local ) . c_str ( ) ,
magnitudeTopic ( magnitude . type ) . c_str ( ) ,
magnitudeTopic ( magnitude . type ) . c_str ( ) ,
buffer ,
buffer ,
magnitudeUnits ( magnitude ) . c_str ( )
_ magnitudeUnits( magnitude ) . c_str ( )
) ;
) ;
}
}
# endif // SENSOR_DEBUG
# endif // SENSOR_DEBUG