-
Notifications
You must be signed in to change notification settings - Fork 82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Architecture Review #391
Architecture Review #391
Conversation
@user2684, |
@Akubi on the EEPROM issue, it should be solved with commit 981308c. The problem was a bit more complex actually and two issues were there. One was as you pointed out with reset() and the other was with _value_processing. _value_processing default is AVG (which fit any sensor taking measures) so DigitalOutput was averaging the values messing up everything. I added setValueProcessing(NONE) to DigitalOutput (and all the other actuators) so only the last value set is retained. As for reset(), resetting the counters is required by any sensor which is taking a measure and start over after reporting so that piace of code has to stay there. BUT when value processing is NONE, there is nothing to do with the counters so that code is not executed. I tested it out and seems to work, let me know if works on your side as well. Looking at the other issues now. Thanks! |
@Akubi sorry for spamming first of all. As for the timer issue, I've implemented your recommendation in 7405c13. I'm still not fully convinced it can scale due to the way List is implemented (e.g. with too many push could cause memory issue). But I couldn't find an alternative implementation: the only place where this update can take place is in NodeManager::loop() and there are too many timers around to ask Sensor to update them consistently. We would need to test this a bit to be sure fixing one problem has not introduced another. Btw, this makes me realize v1.7 implementation was buggy as well, since too many timers were not updated consistently (e.g. the safeguard timer of DigitalOutput). Big finding, thanks. I've also renamed in 52581f5 NodeManager.h and cpp file in Node.h and cpp to avoid the conflict you mentioned. Moving on to the next issue :P |
@Akubi getting to your last finding about setForceUpdateTimerValue. Heck you are fully right. I see two alternatives, going to the unsigned long way with an implicit upper bound or adding an additional variable just to keep track of the days. Probably the first is best but I agree with the concerns regarding memory utilization since there are many timers the problem tends to be bigger. Let me know if you found a better approach, otherwise I'll go with this solution. Thanks! |
@user2684, I set up a proposal for solving the timer issue based on unsigned long. Of course memory usage can be an issue. In order to address this I changed memory layout of the NodeManager itself. |
@user2684, I'm back from my WE. Do you already take a first look to my proposal ? |
@Akubi sorry for the delayed answer (once again) :-/ Yes I reviewed the package, there are a number of interesting things so I took me a while to go through it. First of all the timers, I like your idea to use _target instead of _elapsed. I've implemented it in a similar way with the following little differences:
I've also migrated to unsigned long the variables in Timer and NodeManager. |
@user2684, your are welcome. |
@Akubi finally done with the data refactoring, I took a while since has to be done mostly manually. Now this PR and your version should be mostly aligned since included also your changes to sleepBetweenSend, the order in the constants and string support in ConfigurationRequest. Plus this PR already included the most robust logic of the timers implemented a while back. The only things missing are your changes to SensorACS712 and Diplay5110 which would probably better to go through dedicated PRs (once this will be merged). I'll check all of those changes in the next few days so to be sure there is no wrong copy and paste from my side. |
@user2684, Thank for feedback and for taking in account my proposals. |
After doing some tests I've noticed a few issues especially with Child and sensors not supposed to report periodically (e.g. relays): since _samples was not updated, value was never reported. Fixed it together with a few other minor things. I'm going now to merge this PR since seems kind of working and don't want to stop other PRs to get in. Maybe we can plan a sort of review of this later on, for sure before releasing v1.8. |
And regarding memory utilization monitoring, would be great if we can take this somehow into consideration, maybe in a different PR/release. There is too much floating on the stack, the risk something goes wrong with the memory continuously increases :-) (Added #406) |
@user2684, perfect thank. I'm also doing some test and found troubles with CONDITIONAL_REPORT. |
@user2684 OK perfect thank. |
This PR reviews the architecture of NodeManager.
NodeManager files structure:
sensors
directory which can be directly included by the user when needed. Implementation of the class is inline. Required libraries and OTA configuration request handling is now centralized inside the class. This approach should facilitate integration of new sensors (also because can be developed in parallel) and centralize all the sensor-related tasksexamples/Template
and examples from the README file into individual sketches inexamples
keywords.txt
file with NodeManager's capabilities defined as LITERAL1, NodeManager classes and sensors as KEYWORD1 and NodeManager functions as KEYWORD2. Sensor specific functions are not supposed to be defined hereNodeManager core capabilities:
nodeManager
is now created as extern in within the library. This also simplify the code since there is no more the need to pass its reference throughout the code and save some more memoryUSE_*
defines since now the user can import a sensor directly from the sketch, just before invoking the constructor (e.g.#include <sensors/SensorBattery.h>
)FEATURE_*
intoNODEMANAGER_*
(e.g.FEATURE_DEBUG
is nowNODEMANAGER_DEBUG
). This is because being a library we need to have our variables somehow linked to itNODEMANAGER_OTA_CONFIGURATION
configuration setting, OFF by default, when ON, enables OTA configuration. There is no more the need to create an instance ofSensorConfiguration
since done automatically within the code whenNODEMANAGER_OTA_CONFIGURATION
is ONNODEMANAGER_SERIAL_INPUT
default to OFF; which allows reading from the serial port at the end of each loop cycle expecting command formatted according to the MySensors serial protocol. Useful for debugging interactive sensors such as relays without the need to send messages over the network (fixes Ability to provide messages through Serial input for debugging purposes #303)NODEMANAGER_DEBUG_VERBOSE
, default to OFF, so to allow saving additional memory by moving heavy debug output like those in LOG:MSG into this modeNODEMANAGER_POWER_MANAGER
isON
,powerOn()
andpowerOff()
are also called duringNodeManager
andSensor
setup() (fixes PowerManager should be turned on also during setup() #361)debug()
macro which is using MySensors'hwDebugPrint()
resulting in a better compatibility, more readable and concise code (fixes For debug output, use the same approach of MySensors library #323 NodeManager and MySensors openHAB binding: constant reset #373)sleepBetweenSend()
callwait()
and no moresleep()
to avoid retries with Ack messages (fixes sleepBetweenSend causes ack retries #405)NodeManager API:
NodeManager::setTimezone()
to set the hour offset to be applied for when received a UTC timestamp from the controller (fixes Add option to store the time in the local timezone #368)setSyncTimeOnSetup()
,setSyncTimeAfterSleep()
andsetSyncTimeAfterInterval()
to the NodeManager class to better control how often, if and when to sync the time with the controller, especially when the user wants to rely on a RTC (for this reason time with the RTC if configured is always synchronized regardless of these settings) (fixes RTC: add option to customize how often to sync with controller #353)Sensor API:
setReportTimerMode()
,setReportTimerValue()
,setMeasureTimerMode()
andsetMeasureTimerValue()
. Other functions likesetReportInterval*()
have been kept for compatibility and simply callssetReportTimerMode(TIME_INTERVAL)
andsetReportTimerValue(seconds)
Sensor::onOTAConfiguration(ConfigurationRequest* request)
which can be implemented by each sensor to support OTA configuration requestsSensor::onReceive(MyMessage* message)
since the same code was used for most of the sensors which can be still overridden by each sensor. When a child receive a REQ message and the type matches the type of the request, executes its onLoop function. RemovedonReceive()
from all the sensors using the default logicsetInterruptStrict()
toSensor
. When true (the default), the value of the pin is checked and the interrupt ignored if RISING and not HIGH or FALLING and not LOW (fixes For interrupt-based sensor add global option to verify value #352)setInitialValue()
(renamed intosetPinInitialValue()
),setInterruptMode()
,setWaitAfterInterrupt()
fromSensorInterrupt
andSensorPulseMeter
intoSensor
and their logic intoSensor::setup()
so that present and future interrupt-based sensors can use themSensorInterrupt::setArmed()
since can be emulated by the new Timer classSensor::onBefore()
since no sensors was using it so to save additional storage. Debug output is no more printing the registration of the sensors since redundant with the presentation in the networkgetDisplay()
method toDisplay
subclasses so the user can have access to the LCD object for advanced configurations if needed (fixes Make LCD display object accessible in Display class and subclasses #367)Child API:
ChildInt
,ChildFloat
,ChildDouble
andChildString
. All the required conversion now happens transparently within the Child class which takes as an additional parameter the value format (INT, FLOAT, DOUBLE, STRING). This simplify the code (avoid casting Child to the right subclass for every sensor) and save an additional 500+ bytes of storage.Child::setValue()
now prints out the value just set, together with the child description and id. In this way there is no more the need to have a manual debug output for each sensorNODEMANAGER_EEPROM
is ON andsetPersistValue()
is set to true. Particularly useful when e.g. the sensor is counting and a power outage takes place or for restoring the last value set to a relay. Enabled by default onSensorPulseMeter
and subclasses (fixes Allow SensorPulseMeter and subclasses to persist counters in EEPROM #304) and in SensorDigitalOutput and subclassessetValueProcessing()
toChild
to configure the behavior of the child when setValue() is called multiple times. It can be NONE (ignore the previous values but the last one), AVG (averages the values), SUM (sum up the values) (default: AVG). Adapted SensorPulseMeter and its subclasses to usesetValueProcessing(SUM)
and output class to usesetValueProcessing(NONE)
getLastValueInt()
,getLastValueFloat()
,getLastValueDouble()
,getLastValueString()
, provided NODEMANAGER_CONDITIONAL_REPORT is set to ONsetSendWithoutValue()
which allow to send the value to the gateway even if no samples have been collected. Useful for sensors like Rain Gauge which has to report anyway at the end of the cyclesetValue*()
and in justsetValue()
Timer class:
setMode()
which sets the way the timer operates. It can be eitherTIME_INTERVAL
(e.g. report every X seconds with the amount of time set withsetValue()
),IMMEDIATELY
(e.g. report at every cycle, useful for sensors like actuators which should report as soon as the value has changed),DO_NOT_REPORT
(e.g. never report, useful for when there is no need to report, like a Display) and whenFEATURE_TIME
isON
,EVERY_MINUTE/EVERY_HOUR/EVERY_DAY
(e.g. to report the value set in the previous timeframe, useful for sensors reporting an accumulated value linked to a timeframe at regular intervals),AT_MINUTE/AT_HOUR/AT_DAY
(e.g. report at a given minute/hour/day, useful if the measure is expected at a specified time, set withsetValue()
) (fixes Add ability to report at a configured minute of the hour #394 Add ability to unset the reporting timer #357)update()
is no more called by Sensor but when a timer is created it registers itself against the NodeManager class which is responsible to call it on each register timer (fixes Timer elapsed time not accurate #404)Other changes:
Request
class intoConfigurationRequest
since used only bySensorConfiguration
ConfigurationRequest
now support string input (can be useful for Display class and subclasses)Including NodeManager's library in your projects
To make use of the library version, replace in the main sketch:
#include "NodeManagerLibrary.h"
with:
#include <MySensors_NodeManager.h>
This makes (on purpose, to avoid conflicts) NodeManager's v1.7 and v1.8 not compatible
Using both measure and report timer
The following will configure the sensor to take a measure every 10 seconds and report (the calculated average) every 30 seconds
The following will allow a sensor to take a measure every minute so to be printed on a LCD display but reporting to the network every 5 minutes. Since DisplaySSD1306 calls setReportTimerMode(DO_NOT_REPORT), the screen will be updated at the configured interval:
Log Parser Integration
The following regexp can be used to make Log Parser understanding NodeManager's debug output:
NM_Log Parser Tester.zip