disable/cancel smartSleep while sleep countdown is running



  • I am developing a LED switch node that should sleep as much as possible while the LED is off.
    As a controller I use FHEM which supports the smartSleep feature.
    When the node wakes up the controller sends new commands to the node (turn LED on). The node turns on the LED, but directly turns it off and goes to sleep mode again.
    Afer sleeptime the node wakes up, turns on the LED and stays on.
    I think the node has initiated to go to sleep while receiving the turn-on-command.
    Is it possible to turn smartSleep off (or cancel while the smartsleep countdown is running) in the receive method.
    I counld not find any method to do that. I tried sleep_disable() but this seems not to work.

    I use Mysensors 2.3.1 on an arduino nano.

    volatile uint8_t currentLevel = 0;  // Current dim level...
    
    void receive(const MyMessage &message)
    {
    	DEBUG_PRINT( "receive" );
    	DEBUG_PRINTLN( String(message.data) );	
    	if (message.type == V_STATUS) 
    	{
    		uint8_t requestedLevel = atoi( message.data );
    
    		requestedLevel = requestedLevel > 1 ? 1 : requestedLevel;
    
    		currentLevel = requestedLevel;
    		if (currentLevel == 1)
    		{
    			sleep_disable();	
    		}
    
    		DEBUG_PRINT(F("analogWrite: "));
    		DEBUG_PRINTLN(currentLevel);
    		setLED(currentLevel);
    
    		// saveState(LED_LEVEL_EEPROM,	currentLevel);
    		DEBUG_PRINT(F("LED_LEVEL_EEPROM New Value: "));
    		DEBUG_PRINTLN(currentLevel);
    		// Inform the gateway
    		send(msgSwitchState.set(currentLevel > 0));
    	}
    }
    

  • Mod

    @tmandel In receive(), just set the requestedLevel variable. Move all the other code to loop().

    Calling send() from inside the receive() function can cause problems, so by moving the code to loop you avoid that potential problem as well.



  • @mfalkvidd said in disable/cancel smartSleep while sleep countdown is running:

    @tmandel In receive(), just set the requestedLevel variable. Move all the other code to loop().

    Calling send() from inside the receive() function can cause problems, so by moving the code to loop you avoid that potential problem as well.

    Does this mean loop() will be processed once more, in case there's an ingoing message while node is preparing for smartSleep?
    Is there any further documentation on that?
    Additionally: will the timer before going asleep be reset in case of ingoing messages? To explain the background of this question: this might lead to some rework in the FHEM-module handling that part...

    Additionally: I made a report wrt. the controller overview on the newly supported features of the FHEM module some days ago. Could so. please include FHEM in the matrix based on that info?


  • Mod

    @rejoe2 said in disable/cancel smartSleep while sleep countdown is running:

    @mfalkvidd said in disable/cancel smartSleep while sleep countdown is running:

    @tmandel In receive(), just set the requestedLevel variable. Move all the other code to loop().

    Calling send() from inside the receive() function can cause problems, so by moving the code to loop you avoid that potential problem as well.

    Does this mean loop() will be processed once more, in case there's an ingoing message while node is preparing for smartSleep?
    Is there any further documentation on that?
    Additionally: will the timer before going asleep be reset in case of ingoing messages? To explain the background of this question: this might lead to some rework in the FHEM-module handling that part...

    I don't know (I don't even understand what you're asking 😉 ). But I have never used smartSleep so maybe that's why. Hopefully someone else can answer.

    Additionally: I made a report wrt. the controller overview on the newly supported features of the FHEM module some days ago. Could so. please include FHEM in the matrix based on that info?

    The quickest way is probably to edit the spreadsheet yourself (link is in the very first post in that thread).



  • @mfalkvidd

    I don't know (I don't even understand what you're asking 😉 ). But I have never used smartSleep so maybe that's why. Hopefully someone else can answer.

    I added the smartSleep() handling code to FHEM. Bevore going asleep, the node will send out a respective message; then the controller has 500UL time to send out any info towards the node.
    AFAI understood, receive() will finally be processed, but I never heard anything about loop(). The problem, @tmandel tries to solve should indeed be handled by loop(), but this would meen one should have the option to ask for one more loop() .

    The quickest way is probably to edit the spreadsheet yourself (link is in the very first post in that thread).

    I tried to do so, but had some problems, as I'm not registered @g**gle ;).
    So what's next...? (Maybe I ask s.o. registered there...)


  • Mod

    @rejoe2 I can edit the spreadsheet anonymously in a private browsing window so there should not be a need for a google account. Are you getting some sort of error message when trying to edit?



  • @mfalkvidd said in disable/cancel smartSleep while sleep countdown is running:

    @rejoe2 I can edit the spreadsheet anonymously in a private browsing window so there should not be a need for a google account. Are you getting some sort of error message when trying to edit?

    Thx, now the matix has been updated... 😁



  • @mfalkvidd

    I made some more investigation and sortet my code a little bit. The problem still exists.
    FHEM sends the next commant to my node when the node sends I_PRE_SLEEP_NOTIFICATION.
    But in this situation the smartSleep counter is already counting down.
    The node receives the value of 1 but goes to sleep right after that. After Next wakeup the node runs through the loop and stays awake.

    In my opinion there should be 2 solutions for that problem

    1. FHEM sends new values after I_POST_SLEEP_NOTIFICATION
    2. in mysensors there is a method to cancel smartSleep countdown
    //	###################   Debugging   #####################
    #define MY_DEBUG
    #define SER_DEBUG
    #define MY_RF24_PA_LEVEL 					RF24_PA_LOW
    #define MY_RADIO_RF24
    #define MY_RF24_CHANNEL 					96
    #define MY_TRANSPORT_WAIT_READY_MS 			(5000ul)
    #define MY_TRANSPORT_SANITY_CHECK
    
    #define MY_NODE_ID 							110
    #define MY_PARENT_NODE_ID 					0		//without this passive node broadcasts everything to parent 255 (dont know what happens, if 2 repeater receive this at the same time)
    // #define MY_PARENT_NODE_IS_STATIC
    // #define MY_PASSIVE_NODE
    
    
    // ###################   Node Spezifisch   #####################
    #define SKETCH_VER            				"1.0-002"        			// Sketch version
    #define SKETCH_NAME           				"LEDSwitch"   		// Optional child sensor name
    
    #define SLEEP_TIME							13333
    #define	TOGGLE_BUTTON						3
    
    #include <MySensors.h>
    
    
    #ifdef SER_DEBUG
    #define DEBUG_SERIAL(x) Serial.begin(x)
    #define DEBUG_PRINT(x) Serial.print(x)
    #define DEBUG_PRINTLN(x) Serial.println(x)
    #else
    #define DEBUG_SERIAL(x)
    #define DEBUG_PRINT(x) 
    #define DEBUG_PRINTLN(x) 
    #endif
    
    #define LED_PWM_PIN							5
    
    #define CHILD_SINGLE_LED_SWITCH				51
    #define CHILD_SINGLE_LED_SWITCH_TEXT		(F("Switch"))
    
    
    
    
    #define MAX_LED_LEVEL						1
    #define LED_LEVEL_EEPROM					0
    #define	BLINK_DELAY							180
    
    
    bool justReceived = false;
    uint8_t	lastLevel = 1;
    uint32_t wakeupTime = 0;
    uint32_t waitBeforeNextSleep = 5000;
    volatile uint8_t currentLevel = 0; 			// Current LED level 0 or 1
    
    MyMessage msgSwitchState					(CHILD_SINGLE_LED_SWITCH,		V_STATUS);			//51
    
    
    void preHwInit() 
    {
    	DEBUG_SERIAL(MY_BAUD_RATE);
    	pinMode(LED_PWM_PIN, OUTPUT);   // sets the pin as output
    	pinMode(TOGGLE_BUTTON, INPUT_PULLUP);   // sets the pin as output
    	DEBUG_PRINTLN("preHwInit done");
    
    }
    
    void before() 
    {
    	digitalWrite( LED_PWM_PIN, currentLevel );
    }
    
    
    void setup()
    {
    	DEBUG_PRINTLN("Setup");
    }
    
    void presentation()
    {
    	sendSketchInfo(SKETCH_NAME, SKETCH_VER );
    	present(CHILD_SINGLE_LED_SWITCH, 	S_BINARY, 		CHILD_SINGLE_LED_SWITCH_TEXT);
    }
    
    
    void loop()
    {
    	uint32_t currentTime = millis();
    	if (justReceived)//
    	{
    		DEBUG_PRINTLN("justReceived");
    		justReceived = false;
    	}
    	
    	if (lastLevel != currentLevel) 
    	{
    		lastLevel = currentLevel;
    		setLED(currentLevel);
    	}
    	
    	if ((currentTime - wakeupTime) > waitBeforeNextSleep)
    	{
    		DEBUG_PRINTLN("_");
    		if (currentLevel == 0)
    		{
    			DEBUG_PRINTLN("prepare to sleep");
    			int8_t wakeupReason = sleep(digitalPinToInterrupt(TOGGLE_BUTTON), FALLING , SLEEP_TIME, true);
    			if (wakeupReason == digitalPinToInterrupt(TOGGLE_BUTTON))
    			{
    				currentLevel = 1;
    				DEBUG_PRINTLN("LED on after wake by ButtonIRQ");
    			}
    			wakeupTime = millis();
    			DEBUG_PRINT("leaving sleep: wakeup reason: ");
    			DEBUG_PRINTLN(wakeupReason);
    		}
    	}
    	else
    	{
    		DEBUG_PRINT(".");
    	}
    }
    
    
    
    void receive(const MyMessage &message)
    {
    	DEBUG_PRINT( "receive: " );
    	DEBUG_PRINTLN( message.getByte() );	
    	if (message.type == V_STATUS) 
    	{
    		justReceived = true;
    		uint8_t requestedLevel = message.getByte();
    		if (requestedLevel == 0)
    		{
    			currentLevel=0;
    		}
    		else
    		{
    			currentLevel=1;
    		}
    	}
    }
    
    
    void setLED(uint8_t newLevel)
    {
    	DEBUG_PRINT( "setLED: " );
    	DEBUG_PRINTLN( newLevel );	
    	if (newLevel == 0)
    	{
    		analogWrite(LED_PWM_PIN,0);
    	}
    	else
    	{
    		analogWrite(LED_PWM_PIN,newLevel<<3);
    	}
    	send( msgSwitchState.set(currentLevel) );
    }
    
    
    

    15:06:44.843 -> setLED: 0
    15:06:44.843 -> 3224 TSF:MSG:SEND,110-110-0-0,s=51,c=1,t=2,pt=1,l=1,sg=0,ft=0,st=OK:0
    15:06:44.843 -> prepare to sleep
    15:06:44.843 -> 3230 MCO:SLP:MS=13333,SMS=1,I1=1,M1=2,I2=255,M2=255
    15:06:44.843 -> 3238 TSF:MSG:SEND,110-110-0-0,s=255,c=3,t=32,pt=5,l=4,sg=0,ft=0,st=OK:500
    15:06:45.346 -> 3745 TSF:TDI:TSL
    15:07:00.209 -> 3746 MCO:SLP:WUP=-1
    15:07:00.209 -> 3748 TSF:TRI:TSB
    15:07:00.209 -> 3757 TSF:MSG:SEND,110-110-0-0,s=255,c=3,t=33,pt=5,l=4,sg=0,ft=0,st=OK:13333
    15:07:00.209 -> leaving sleep: wakeup reason: -1
    15:07:00.209 -> prepare to sleep
    15:07:00.209 -> 3764 MCO:SLP:MS=13333,SMS=1,I1=1,M1=2,I2=255,M2=255
    15:07:00.209 -> 3774 TSF:MSG:SEND,110-110-0-0,s=255,c=3,t=32,pt=5,l=4,sg=0,ft=0,st=OK:500
    15:07:00.275 -> 3817 TSF:MSG:READ,0-0-110,s=51,c=1,t=2,pt=0,l=1,sg=0:1
    15:07:00.275 -> receive: 1
    15:07:00.711 -> 4281 TSF:TDI:TSL
    15:07:15.543 -> 4282 MCO:SLP:WUP=-1
    15:07:15.543 -> 4284 TSF:TRI:TSB
    15:07:15.577 -> 4292 TSF:MSG:SEND,110-110-0-0,s=255,c=3,t=33,pt=5,l=4,sg=0,ft=0,st=OK:13333
    15:07:15.577 -> leaving sleep: wakeup reason: -1
    15:07:15.577 -> justReceived
    15:07:15.577 -> setLED: 1
    15:07:15.577 -> 4301 TSF:MSG:SEND,110-110-0-0,s=51,c=1,t=2,pt=1,l=1,sg=0,ft=0,st=OK:1



  • @tmandel said in disable/cancel smartSleep while sleep countdown is running:

    In my opinion there should be 2 solutions for that problem

    1. FHEM sends new values after I_POST_SLEEP_NOTIFICATION
    2. in mysensors there is a method to cancel smartSleep countdown

    My first attempt was to built to FHEM code like you suggested in #1. But this led to lost messages in the end, most likely due to interferring RF signals. This is the background why I was very interested in deeper insights on possibilities to cancel/delay the node's going asleep, because imo this would be the cleaner implementation.
    I'm not too deep im C coding, but perhaps as trial to achieve #2 you could just call _process() from within the receive function (as the last statement)?



  • @rejoe2 Thanks for your effort with trying to change the FHEM Module.
    I think I found a way to cancel the smartSleep counter.

    In MySensorsCore.cpp in line 619 I changed

    wait(MY_SMART_SLEEP_WAIT_DURATION_MS);		// listen for incoming messages
    

    to

    if (wait(MY_SMART_SLEEP_WAIT_DURATION_MS, C_SET, I_VERSION)) //cancel sleeping request if there is a new C_SET Command
    {
    	CORE_DEBUG(PSTR("smartsleep canceled\n"));
    	return MY_SLEEP_NOT_POSSIBLE;
    }
    

    This seems to work in MySenors but in FHEM the state says "asleep".
    FHEM sends no more messages. Instead it increases the retainedMessages counter.

    Even if I change the code to send I_POST_SLEEP_NOTIFICATION before cancel the smartSleep.

    if (wait(MY_SMART_SLEEP_WAIT_DURATION_MS, C_SET, I_VERSION)) //cancel sleeping request if there is a new C_SET Command
    {
    	(void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
    					   I_POST_SLEEP_NOTIFICATION).set(MY_SMART_SLEEP_WAIT_DURATION_MS + 20));
    	CORE_DEBUG(PSTR("smartsleep canceled\n"));
    	return MY_SLEEP_NOT_POSSIBLE;
    }
    
    

    Maybe we need a new internal message I_SMARTSLEEP_CANCELED.

    Do you have an other idea?



  • Wow, very impressive code change!
    Imo sending the post-sleep notification should at least lead to the node beeing indicated as "awoken" in FHEM. But as already mentionned, all new messages towards the nodes need a new pre-sleep-notification.
    So you at first will have to send this type of message; if this message is lost, this will also lead to an increasing nr. of retained messages when "sending" out new info (the queue will be extended, but nothing really send out).

    To create a new status like "awoken and ready to receive" might be a good idea; to change the FHEM-code for support of that kind of feature would not be a big issue (at least I hope so 😁 ).



  • Regarding the post sleep message, i believe that the current controllers programming (certainly Domoticz and FHEM it seems) should accept the POST_SLEEP_MESSAGE as an indication that the node is ready to receive messages.
    My understanding of the PRE_SLEEP_MESSAGE was to allow very recent messages to be received by the node, instead it seems that the controllers implementations assume that the node will only receive messages during that period.

    A clarification in the Mysensors documentation to better describe the SmartSleep design would help to sort these behaviors.



  • @jlaraujo said in disable/cancel smartSleep while sleep countdown is running:

    Regarding the post sleep message, i believe that the current controllers programming (certainly Domoticz and FHEM it seems) should accept the POST_SLEEP_MESSAGE as an indication that the node is ready to receive messages.

    Some additional remarks on the FHEM code:
    Indeed, as soon as the POST_SLEEP_MESSAGE is received, all new messages will be sent out without enqueueing (additionally, also requesting data will lead to direct sending of all new info until PRE_SLEEP_MESSAGE is received + a waiting time has elapsed).
    But: direct sending out enqueued messages after reption of POST_SLEEP has turned out to be not a good idea, as already mentionned. This is why imo this message as such isn't a good criteria to decide about sending out enqueued data.
    Perhaps it could be a good idea to start sending also enqueued info before the PRE_SLEEP, but only in rare cases we know the node to wait for info from controller side (e.g. after request time or status info messages).
    Other option could be to define an individual waiting period per node after PRE_SLEEP to start sending (new Attribute).

    @tmandel : Please let me know if either way would help. In any case, I'd need someone to do testing in case of code changes in that part. Programming timer functions not killing fhem in some rare cases turnded out to be a tricky job 😬 .
    In any case, it could be a way around your troubles to request most recent info before going to sleep.



  • @rejoe2
    would it be possible to send out all queued messages if the controllers receives a heartbeat from the node? Maybe this should be a good solution, event if last POST_SLEEP_MESSAGE got lost. We dont have to invent new message types.

    I could test your code on my development FHEM system.

    Like @jlaraujo said I would like to know what the main-develepers of mysensors have to say about the prefered design of smartSleep.



  • @tmandel said in disable/cancel smartSleep while sleep countdown is running:

    @rejoe2
    would it be possible to send out all queued messages if the controllers receives a heartbeat from the node? Maybe this should be a good solution, event if last POST_SLEEP_MESSAGE got lost. We dont have to invent new message types.

    I could test your code on my development FHEM system.

    Using heartbeat imo is a good idea. This usually isn't used in smartSleeping nodes (smartsleep signals themselves renew alive state), so we could easily use that as a "deliever what you have" request.
    Additionally, I have also changed other parts of the logic a bit: in case any information is sent out while node is alive will initiate also sending the queue, so e.g. any kind of requesting data would have the same result as heartbeat (or even a "by-accident at the right time" sending trial)...

    So here's a completely untested version. "Should" work, but no guarantee:
    https://github.com/rejoe2/FHEM/blob/master/10_MYSENSORS_DEVICE.pm



  • update: above mentionned module has been updated and now should load without trouble



  • For those who are interested.
    At the moment my patch runs quiet good.

    @mfalkvidd maybe you can copy this patch to the developement branch of mysensors.

    I don't know if the CORE_DEBUG text meets the guidelines.
    This patch needs a new #define to wait some time before sending a new awake message to the controller.
    If time is to short sometimes asleep and awake where executed reversed by the controller.

    #define MY_SMART_SLEEP_REVOKE_WAIT_DURATION_MS (300ul)
    
    //Tmandel: MySensorsCore.cpp Line 619
    // wait(MY_SMART_SLEEP_WAIT_DURATION_MS);		// listen for incoming messages
    if (wait(MY_SMART_SLEEP_WAIT_DURATION_MS, C_SET, I_VERSION)) //cancel sleeping request if there is a new C_SET Command
    {
    	#define MY_SMART_SLEEP_REVOKE_WAIT_DURATION_MS (300ul) 	// 300 seems to be OK (smaller values let the controller think that the node is still sleeping)
    	CORE_DEBUG(PSTR("!MCO:SLP:RVKE\n")); //sleep revoked
    	wait(MY_SMART_SLEEP_REVOKE_WAIT_DURATION_MS);
    	CORE_DEBUG(PSTR("MCO:SLP:WUP=%" PRIi8 "\n"), MY_SLEEP_NOT_POSSIBLE);	// inform controller about wake-up
    	(void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
    					   I_POST_SLEEP_NOTIFICATION).set(MY_SMART_SLEEP_WAIT_DURATION_MS + MY_SMART_SLEEP_REVOKE_WAIT_DURATION_MS));
    	return MY_SLEEP_NOT_POSSIBLE;
    }
    				
    

    @rejoe2 Thank you for your effort with patching the FHEM module.



  • @tmandel

    @rejoe2 Thank you for your effort with patching the FHEM module.

    You're wellcome!

    Wrt your patch: To get that into the mysensors repo, you'll have to use the (very painfull, but due to copyright reasons necessary) regular github mechanisms to get registered as kind of mysensors developer. Then you can send it as a pull request against the MySensors repo. As you use a new flag (?), you'll have to add some short decription wrt that also.
    I'm not very familiar with all these processes, but in case you need help wrt that you may also contact me via FHEM forum for assistance.

    Btw: Good job!👏 👏 👏


 

248
Online

8.6k
Users

9.4k
Topics

99.0k
Posts