@@ -496,8 +496,12 @@ class Magnitude {
496
496
497
497
double min_delta { 0.0 }; // Minimum value change to report
498
498
double max_delta { 0.0 }; // Maximum value change to report
499
- double correction { 0.0 }; // Value correction (applied when processing)
500
- double zero_threshold { Value::Unknown }; // Reset value to zero when below threshold (applied when reading)
499
+ //
500
+ double min_threshold { Value::Unknown }; // Minimum value to report
501
+ double max_threshold { Value::Unknown }; // Maximum value to report
502
+
503
+ double zero_threshold { Value::Unknown }; // Reset value to zero when equal or below threshold (applied when reading)
504
+ double correction { 0.0 }; // Value correction (applied when reading)
501
505
};
502
506
503
507
static_assert (
@@ -1078,6 +1082,10 @@ PROGMEM_STRING(MinDelta, "MinDelta");
1078
1082
PROGMEM_STRING (Precision, " Precision" );
1079
1083
PROGMEM_STRING (Ratio, " Ratio" );
1080
1084
PROGMEM_STRING (Units, " Units" );
1085
+
1086
+ PROGMEM_STRING (MinThreshold, " MinThreshold" );
1087
+ PROGMEM_STRING (MaxThreshold, " MaxThreshold" );
1088
+
1081
1089
PROGMEM_STRING (ZeroThreshold, " ZeroThreshold" );
1082
1090
1083
1091
PROGMEM_STRING (Mains, " Mains" );
@@ -3449,6 +3457,14 @@ void list(JsonObject& root) {
3449
3457
});
3450
3458
}
3451
3459
3460
+ void threshold_or_nan (JsonArray& out, const double & threshold) {
3461
+ if (!std::isnan (threshold)) {
3462
+ out.add (threshold);
3463
+ } else {
3464
+ out.add (" NaN" );
3465
+ }
3466
+ }
3467
+
3452
3468
void settings (JsonObject& root) {
3453
3469
if (!sensor::ready ()) {
3454
3470
return ;
@@ -3457,7 +3473,7 @@ void settings(JsonObject& root) {
3457
3473
// XXX: inject 'null' in the output. need this for optional fields, since the current
3458
3474
// version of serializer only does this for char ptr and even makes NaN serialized as
3459
3475
// NaN, instead of more commonly used null (but, expect this to be fixed after switching to v6+)
3460
- static const char * const NullSymbol { nullptr };
3476
+ constexpr char * const NullSymbol { nullptr };
3461
3477
3462
3478
espurna::web::ws::EnumerablePayload payload{root, STRING_VIEW (" magnitudes-settings" )};
3463
3479
payload (STRING_VIEW (" values" ), magnitude::count (),
@@ -3479,11 +3495,15 @@ void settings(JsonObject& root) {
3479
3495
}},
3480
3496
{settings::suffix::ZeroThreshold, [](JsonArray& out, size_t index ) {
3481
3497
const auto threshold = magnitude::get (index ).zero_threshold ;
3482
- if (!std::isnan (threshold)) {
3483
- out.add (threshold);
3484
- } else {
3485
- out.add (" NaN" );
3486
- }
3498
+ threshold_or_nan (out, threshold);
3499
+ }},
3500
+ {settings::suffix::MinThreshold, [](JsonArray& out, size_t index ) {
3501
+ const auto threshold = magnitude::get (index ).min_threshold ;
3502
+ threshold_or_nan (out, threshold);
3503
+ }},
3504
+ {settings::suffix::MaxThreshold, [](JsonArray& out, size_t index ) {
3505
+ const auto threshold = magnitude::get (index ).max_threshold ;
3506
+ threshold_or_nan (out, threshold);
3487
3507
}},
3488
3508
{settings::suffix::MinDelta, [](JsonArray& out, size_t index ) {
3489
3509
out.add (magnitude::get (index ).min_delta );
@@ -3964,23 +3984,30 @@ void configure_magnitude(Magnitude& magnitude) {
3964
3984
: magnitude::decimals (magnitude.units ));
3965
3985
}
3966
3986
3967
- // Per-magnitude min & max delta settings for reporting the value
3968
- // - ${prefix}MinDelta${index} controls whether we report when report counter overflows
3969
- // (default is set to 0.0 aka value has changed from the last recorded one)
3970
- // - ${prefix}MaxDelta${index} will trigger report as soon as read value is greater than the specified delta
3971
- // (default is 0.0 as well, but this needs to be >0 to actually do something)
3987
+ // Per-magnitude min & max delta of the report value, may trigger reports independent of the read counter overflow
3988
+ // - ${prefix}MinDelta${index} for value change greater than or equal to the specified delta
3989
+ // - ${prefix}MaxDelta${index} for value change less than or equal to the specified delta
3990
+ // Both are 0. by default, meaning these checks are ignored when processing read data
3972
3991
magnitude.min_delta = getSetting (
3973
3992
settings::keys::get (magnitude, settings::suffix::MinDelta),
3974
3993
build::DefaultMinDelta);
3975
3994
magnitude.max_delta = getSetting (
3976
3995
settings::keys::get (magnitude, settings::suffix::MaxDelta),
3977
3996
build::DefaultMaxDelta);
3978
3997
3979
- // Sometimes we want to ensure the value is above certain threshold before reporting
3998
+ // Overwrite value with 0 when below a certain threshold. Happens when report is triggered, before any further checks
3980
3999
magnitude.zero_threshold = getSetting (
3981
4000
settings::keys::get (magnitude, settings::suffix::ZeroThreshold),
3982
4001
Value::Unknown);
3983
4002
4003
+ // Per-magnitude min & max checks of the report value
4004
+ magnitude.min_threshold = getSetting (
4005
+ settings::keys::get (magnitude, settings::suffix::MinThreshold),
4006
+ Value::Unknown);
4007
+ magnitude.max_threshold = getSetting (
4008
+ settings::keys::get (magnitude, settings::suffix::MaxThreshold),
4009
+ Value::Unknown);
4010
+
3984
4011
// When we don't save energy, purge existing value in both RAM & settings
3985
4012
if (isEmon (magnitude.sensor ) && (MAGNITUDE_ENERGY == magnitude.type ) && (0 == energy::every ())) {
3986
4013
energy::reset (magnitude.index_global );
@@ -4175,6 +4202,66 @@ bool ready_to_read() {
4175
4202
return internal::read_flag.wait (internal::read_interval);
4176
4203
}
4177
4204
4205
+ bool ready_to_report (ValuePair& out, const ValuePair& processed, const Magnitude& magnitude, bool report) {
4206
+ // Ensure that reported value change is greater or equal to this delta value
4207
+ const bool compare_min_delta { magnitude.min_delta > build::DefaultMinDelta };
4208
+ report = report || compare_min_delta;
4209
+
4210
+ // Ensure that reported value change is less or equal to this delta value
4211
+ const bool compare_max_delta { magnitude.max_delta > build::DefaultMaxDelta };
4212
+ report = report || compare_max_delta;
4213
+
4214
+ // Ensure that reported value is greater than or equal to this value
4215
+ const bool check_min_threshold { !std::isnan (magnitude.min_threshold ) };
4216
+ report = report || check_min_threshold;
4217
+
4218
+ // Ensure that reported value is less than or equal to this value
4219
+ const bool check_max_threshold { !std::isnan (magnitude.max_threshold ) };
4220
+ report = report || check_max_threshold;
4221
+
4222
+ // Figure out whether report value is zero or not
4223
+ const bool check_zero_threshold { !std::isnan (magnitude.zero_threshold ) };
4224
+ report = report || check_zero_threshold;
4225
+
4226
+ if (report) {
4227
+ if (magnitude.filter ->ready ()) {
4228
+ out = ValuePair{
4229
+ .value = magnitude.filter ->value (),
4230
+ .units = processed.units ,
4231
+ };
4232
+ magnitude.filter ->restart ();
4233
+ } else {
4234
+ out = processed;
4235
+ }
4236
+
4237
+ if (check_zero_threshold && out.value < magnitude.zero_threshold ) {
4238
+ out.value = 0.0 ;
4239
+ }
4240
+
4241
+ const bool previous_report { !std::isnan (magnitude.reported .value ) };
4242
+
4243
+ if (report && previous_report && compare_min_delta) {
4244
+ report = std::abs (out.value - magnitude.reported .value )
4245
+ >= magnitude.min_delta ;
4246
+ }
4247
+
4248
+ if (report && previous_report && compare_max_delta) {
4249
+ report = std::abs (out.value - magnitude.reported .value )
4250
+ <= magnitude.max_delta ;
4251
+ }
4252
+
4253
+ if (report && check_min_threshold) {
4254
+ report = out.value >= magnitude.min_threshold ;
4255
+ }
4256
+
4257
+ if (report && check_max_threshold) {
4258
+ report = out.value <= magnitude.max_threshold ;
4259
+ }
4260
+ }
4261
+
4262
+ return report;
4263
+ }
4264
+
4178
4265
void loop () {
4179
4266
// TODO: allow to do nothing
4180
4267
if (internal::state == State::Idle) {
@@ -4296,45 +4383,10 @@ void loop() {
4296
4383
energy::update (magnitude, report);
4297
4384
}
4298
4385
4299
- // Ensure that reported value change is greater or equal to this delta value
4300
- const bool compare_min_delta { magnitude.min_delta > build::DefaultMinDelta };
4301
- report = report || compare_min_delta;
4302
-
4303
- // Ensure that reported value change is less or equal to this delta value
4304
- const bool compare_max_delta { magnitude.max_delta > build::DefaultMaxDelta };
4305
- report = report || compare_max_delta;
4306
-
4307
- // Ensure that report value is greater than this threshold value
4308
- const bool check_zero_threshold { !std::isnan (magnitude.zero_threshold ) };
4309
- report = report || check_zero_threshold;
4310
-
4311
- if (report) {
4312
- if (magnitude.filter ->ready ()) {
4313
- state.report = ValuePair{
4314
- .value = magnitude.filter ->value (),
4315
- .units = state.processed .units ,
4316
- };
4317
- magnitude.filter ->restart ();
4318
- } else {
4319
- state.report = state.processed ;
4320
- }
4321
-
4322
- const bool previous_report { !std::isnan (magnitude.reported .value ) };
4323
-
4324
- if (report && previous_report && compare_min_delta) {
4325
- report = std::abs (state.report .value - magnitude.reported .value )
4326
- >= magnitude.min_delta ;
4327
- }
4328
-
4329
- if (report && previous_report && compare_max_delta) {
4330
- report = std::abs (state.report .value - magnitude.reported .value )
4331
- <= magnitude.max_delta ;
4332
- }
4333
-
4334
- if (report && check_zero_threshold) {
4335
- report = state.report .value >= magnitude.zero_threshold ;
4336
- }
4337
- }
4386
+ // Prepare and verify report value before proceeding
4387
+ report = ready_to_report (
4388
+ state.report , state.processed ,
4389
+ magnitude, report);
4338
4390
4339
4391
// If flag was not reset by the checks above, continue and finally report the value
4340
4392
if (report) {
0 commit comments