-
Notifications
You must be signed in to change notification settings - Fork 78
Expand file tree
/
Copy pathChild.cpp
More file actions
309 lines (285 loc) · 10 KB
/
Copy pathChild.cpp
File metadata and controls
309 lines (285 loc) · 10 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
/*
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2017 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
/******************************************
Child: data structure for representing a Child of a Sensor
*/
#include "Child.h"
Child::Child() {
}
// constructor
Child::Child(Sensor* sensor, value_format format, uint8_t child_id, uint8_t presentation, uint8_t type, const char* description) {
_sensor = sensor;
setFormat(format);
_child_id = child_id;
_presentation = presentation;
_type = type;
_description = description;
// register the child with the sensor
_sensor->registerChild(this);
#if NODEMANAGER_EEPROM == ON
// define the EEPROM starting address for this child
_eeprom_address = EEPROM_CHILD_OFFSET+_child_id*EEPROM_CHILD_SIZE;
#endif
}
// setter/getter
void Child::setChildId(uint8_t value) {
_child_id = value;
}
uint8_t Child::getChildId() {
return _child_id;
}
void Child::setFormat(value_format value) {
_format = value;
// set default float precision
if (_format == FLOAT) _float_precision = 2;
if (_format == DOUBLE) _float_precision = 4;
}
value_format Child::getFormat() {
return _format;
}
void Child::setPresentation(uint8_t value) {
_presentation = value;
}
uint8_t Child::getPresentation() {
return _presentation;
}
void Child::setType(uint8_t value) {
_type = value;
}
uint8_t Child::getType() {
return _type;
}
void Child::setFloatPrecision(uint8_t value) {
_float_precision = value;
}
void Child::setDescription(const char* value) {
_description = value;
}
const char* Child::getDescription() {
return _description;
}
void Child::setValueProcessing(child_processing value) {
_value_processing = value;
}
void Child::setSendWithoutValue(bool value) {
_send_without_value = value;
}
// set the value to the child
void Child::setValue(int value) {
_setValueNumber(value);
}
void Child::setValue(float value) {
_setValueNumber(value);
}
void Child::setValue(double value) {
_setValueNumber(value);
}
void Child::setValue(const char* value) {
_value_string = value;
_samples = 1;
debug(PSTR(LOG_LOOP "%s(%d):SET t=%d v=%s\n"),_description,_child_id,_type,_value_string);
}
// store a new value and update the total
void Child::_setValueNumber(double value) {
if (isnan(value)) return;
if (_value_processing != NONE) _total = _total + value;
// keep track of the samples
_samples++;
// process the value
if (_value_processing == AVG) _value = _total / _samples;
if (_value_processing == SUM) _value = _total;
if (_value_processing == NONE) _value = value;
// print out a debug message
if (_format == INT) debug(PSTR(LOG_LOOP "%s(%d):SET t=%d v=%d\n"),_description,_child_id,_type,(int)_value);
if (_format == FLOAT) debug(PSTR(LOG_LOOP "%s(%d):SET t=%d v=%d.%02d\n"),_description,_child_id,_type,(int)_value, (int)(_value*100)%100);
if (_format == DOUBLE) debug(PSTR(LOG_LOOP "%s(%d):SET t=%d v=%d.%04d\n"),_description,_child_id,_type,(int)_value, (int)(_value*10000)%10000);
#if NODEMANAGER_EEPROM == ON
// if the value is supposed to be persisted in EEPROM, save it
if (_persist_value) saveValue();
#endif
}
// return the value of the child in the requested format
int Child::getValueInt() {
return (int)_value;
}
float Child::getValueFloat() {
return (float)_value;
}
double Child::getValueDouble(){
return _value;
}
const char* Child::getValueString(){
return _value_string;
}
// check if the value must be sended back to the controller
bool Child::valueReadyToSend() {
// do not send if no samples have been collected, unless instructed otherwise
if (!_send_without_value && _samples == 0) return false;
#if NODEMANAGER_CONDITIONAL_REPORT == ON
// ignore conditional reporting settings if it is the first run
if (!_sensor->getFirstRun()) {
// if the value is a number
if (_format != STRING) {
// if below or above the thresholds, do not send the value
if (_value < _min_threshold || _value > _max_threshold) return false;
// if the force update timer is over, send the value
if (!_force_update_timer->isOver()) {
// if the value does not differ enough from the previous one, do not send the value
if (_value > (_last_value - _value_delta) && _value < (_last_value + _value_delta)) {
return false;
}
}
}
// if the value is a string
else {
// if a delta is configured, do not report if the string is the same as the previous one
if (_value_delta > 0 && strcmp(_value_string, _last_value_string) == 0) {
return false;
}
}
}
#endif
return true;
}
// send the value back to the controller
void Child::sendValue(bool force) {
#if NODEMANAGER_CONDITIONAL_REPORT == ON
// update last value if mode = UPDATE_ALWAYS
if (_last_value_mode == UPDATE_ALWAYS) _last_value = _value;
#endif
// ignore conditional reporting settings if forced to report
if (!force && !valueReadyToSend()) return;
// we report value
#if NODEMANAGER_CONDITIONAL_REPORT == ON
// if the force update timer is over restart it
if (_force_update_timer->isOver()) _force_update_timer->start();
// keep track of the previous value
if (_format != STRING) _last_value = _value;
else _last_value_string = _value_string;
#endif
// send the value to the gateway
if (_format == INT) nodeManager.sendMessage(_child_id,_type,(int)_value);
if (_format == FLOAT) nodeManager.sendMessage(_child_id,_type,(float)_value,_float_precision);
if (_format == DOUBLE) nodeManager.sendMessage(_child_id,_type,_value,_float_precision);
if (_format == STRING) nodeManager.sendMessage(_child_id,_type,_value_string);
// reset the counters
if (_value_processing != SUM) reset();
}
// print the child value to a device
void Child::print(Print& device) {
if (_format == INT) device.print((int)_value);
if (_format == FLOAT) device.print((float)_value,_float_precision);
if (_format == DOUBLE) device.print((double)_value,_float_precision);
if (_format == STRING) device.print(_value_string);
}
// reset the counters
void Child::reset() {
if (_format != STRING) {
if (_value_processing != NONE) {
// reset the counters
_total = 0;
_value = 0;
#if NODEMANAGER_EEPROM == ON
// if the value is supposed to be persisted in EEPROM, save it
if (_persist_value) saveValue();
#endif
}
} else _value_string = "";
_samples = 0;
}
#if NODEMANAGER_CONDITIONAL_REPORT == ON
// setter/getter
void Child::setForceUpdateTimerValue(unsigned long value) {
_force_update_timer->setMode(TIME_INTERVAL);
_force_update_timer->setValue(value*60);
_force_update_timer->start();
}
void Child::setMinThreshold(float value) {
_min_threshold = value;
}
void Child::setMaxThreshold(float value) {
_max_threshold = value;
}
void Child::setValueDelta(float value) {
_value_delta = value;
}
void Child::setUpdateLastValue(last_value_mode value) {
_last_value_mode = value;
}
// return the last value of the child in the requested format
int Child::getLastValueInt() {
return (int)_last_value;
}
float Child::getLastValueFloat() {
return (float)_last_value;
}
double Child::getLastValueDouble(){
return _last_value;
}
const char* Child::getLastValueString(){
return _last_value_string;
}
#endif
#if NODEMANAGER_EEPROM == ON
// setter/getter
void Child::setPersistValue(bool value) {
_persist_value = value;
}
bool Child::getPersistValue() {
return _persist_value;
}
// save value to EEPROM - subclass needs to implement
void Child::saveValue() {
if (_format == STRING) return;
// number is too large to be saved or we run out of EEPROM slots
if (_value >= 10000 || ((_eeprom_address+EEPROM_CHILD_SIZE) > 255) ) return;
debug(PSTR(LOG_EEPROM "%s(%d):SAVE\n"),_description,_child_id);
// save the type
nodeManager.saveToMemory(_eeprom_address+EEPROM_CHILD_TYPE,_type);
// encode the sign (e.g. 0 if > 0, 1 otherwise)
nodeManager.saveToMemory(_eeprom_address+EEPROM_CHILD_SIGN, _value >= 0 ? 0 : 1);
// encode and save the integer value (e.g. 7240 -> int_1 = 72, int_2 = 40)
nodeManager.saveToMemory(_eeprom_address+EEPROM_CHILD_INT_1,(int)(_value/100)%100);
nodeManager.saveToMemory(_eeprom_address+EEPROM_CHILD_INT_2,(int)_value%100);
// encode and save the first part of the decimal value (e.g. 7240.12 -> dec_1 = 12)
if (_format == FLOAT || _format == DOUBLE) nodeManager.saveToMemory(_eeprom_address+EEPROM_CHILD_DEC_1,(int)(_value*100)%100);
// encode and save the second part of the decimal value (e.g. 7240.1244 -> dec_2 = 44)
if (_format == DOUBLE) nodeManager.saveToMemory(_eeprom_address+EEPROM_CHILD_DEC_1,(int)(_value*10000)%100);
}
// load value from EEPROM
void Child::loadValue() {
if (_format == STRING) return;
// ensure we are not going to read beyond the available EEPROM slots
if (((_eeprom_address+EEPROM_CHILD_SIZE) > 255) ) return;
// ensure the type is valid
if (nodeManager.loadFromMemory(_eeprom_address+EEPROM_CHILD_TYPE) != _type) return;
debug(PSTR(LOG_EEPROM "%s(%d):LOAD\n"),_description,_child_id);
// decode the integer part
double value = 0;
value = nodeManager.loadFromMemory(_eeprom_address+EEPROM_CHILD_INT_1)*100 + nodeManager.loadFromMemory(_eeprom_address+EEPROM_CHILD_INT_2);
if (value == 255) return;
// decode the sign
if (nodeManager.loadFromMemory(_eeprom_address+EEPROM_CHILD_SIGN) == 1) value = value * -1;
// decode the first part of the decimal value
if (_format == FLOAT || _format == DOUBLE) value += nodeManager.loadFromMemory(_eeprom_address+EEPROM_CHILD_DEC_1)/100.0;
// decode the second part of the decimal value
if (_format == DOUBLE) value += nodeManager.loadFromMemory(_eeprom_address+EEPROM_CHILD_DEC_2)/10000.0;
setValue(value);
}
#endif