How to deal with a request for information from controller?



  • I'm building some sensors and planning to use a self build controller in the future. For the meantime, I'm sending commands from my computer through a serial terminal to the gateway which them forwards these commands to the nodes.
    I'm trying to figure out how to make the node respond to a req message from the controller and to send the acks as requested.

    This is what I'm sending from the controller:
    51;99;2;1;2;\n
    Which to me, means:
    node id: 51
    child id:99
    message type: 2 (req)
    ack: 1 (request ack)
    sub type: 1 (V_LIGHT)
    payload: none

    The purpose of this message is to get the current status of a light actuator. I need this because this light actuator has a button to control the lights. My sketch does inform the gateway when I switch the light on/off through the button attached to the node, but I want to deal with the case where the communication may have failed between the node and the gateway at the time the button has been used to control the lights. This situation could be the controller is down for some reason, there is something interfering with the radio comms, etc.

    Well, I suppose I need to deal with the ack response and the req response in my sketch. Am I right?
    If so, I'm looking for examples on how to differentiate between a message that is sent from the controller to the node (obviously through the gateway) to either change the light status OR to request the current light status. I know the sub-type of the message would change from 1 to 2 respectively, but I don't know what is the way to read this value from the incoming messages.
    The other thing I'm looking for, is if the ack response is automatically generated and sent from the controller, and if not, how do I read an ack incoming message and how do I respond to it. A simple function example is well enough.

    Please correct me if I'm wrong in any of my previous statements.

    Thanks!



  • Well, it seems nobody has either the motivation or the answer itself... Wonder if I should have posted this question in the Development forum instead...
    I've been researching and doing some trial and error since I posted the question, and it seems I found a way of doing it. Not sure if this is the correct way because I just did not find any documentation about it, but I'll share my discoveries anyway since it can help others.

    It seems the ack part is done automatically by the library itself. I've been able to see ack messages returning from the gateway to the controller without doing any treatment myself in my sketch.

    Regarding how to differentiate "req" messages sent from the controller to a node, I've been able to detect if a message is a "req" one by reading the value of mGetCommand(message) in my sketch. If this is equal to 2, then it is a "req" message, if it is equal to 1, then it is a "set" message.

    void incomingMessage(const MyMessage &message) {
    //debug only
    //Serial.print("message.destination=");
    //Serial.println(message.destination);
    //Serial.print("message.type=");
    //Serial.println(message.type);
    //Serial.print("message.sensor");
    //Serial.println(message.sensor);
    
    int comando = mGetCommand(message);
    //Serial.print("comando=");
    //Serial.println(comando);
    
      if (message.type==V_LIGHT) {
        //comando=2 means information has been requested from controller, so we will send it back.
        //comando=1 means a new status has been received from controller to the node, so we will act as requested.
        if (comando==2){
          gw.send(lightMsg.set(digitalRead(lightPin)));
        }
        if (comando==1){
          // Change relay state
          setLightState(message.getBool(), false); 
          // Write some debug info
          Serial.print("Incoming light status:");
          Serial.println(message.getBool());
        }
       }
      if (message.type==V_VAR1) {
        // Toggle program mode
        if (programModeActive == false){     // If we're not in programming mode, turn it on.
          programmingmode(1);
        } else {                             // If we are in programing mode, turn it off.
          programmingmode(0);
        } 
        // Write some debug info
        Serial.print("Toggled program mode:");
        Serial.println(message.getBool()); 
      }
    }
    

    And below are the tests that show what I mentioned.

    Sent this, which sets the light child 99 on node 51 to 1 (on), requesting an ack
    51;99;1;1;2;1

    0;0;3;0;14;Gateway startup complete.
    0;0;3;0;9;send: 0-0-51-51 s=99,c=1,t=2,pt=0,l=1,st=ok:1
    0;0;3;0;9;read: 51-51-0 s=99,c=1,t=2,pt=0,l=1:1
    51;99;1;1;2;1
    

    The last one is the ack itself that returned from the gateway to the controller.

    Then sent this, which requests the status of light child 99 on node 51, requesting an ack. Should return the ack and the response containing the current status of the light (1).
    51;99;2;1;2;anything

    0;0;3;0;9;send: 0-0-51-51 s=99,c=2,t=2,pt=0,l=8,st=ok:anything
    0;0;3;0;9;read: 51-51-0 s=99,c=2,t=2,pt=0,l=8:anything
    51;99;2;1;2;anything
    0;0;3;0;9;read: 51-51-0 s=99,c=1,t=2,pt=2,l=2:1
    51;99;1;0;2;1
    

    This "51;99;2;1;2;anything" is the ack generated automatically by the library, and this "51;99;1;0;2;1" is the response that my program performs. Correct!

    Then sent this, which sets the light child 99 on node 51 to 0 (off), requesting an ack
    51;99;1;1;2;0

    0;0;3;0;9;send: 0-0-51-51 s=99,c=1,t=2,pt=0,l=1,st=ok:0
    0;0;3;0;9;read: 51-51-0 s=99,c=1,t=2,pt=0,l=1:0
    51;99;1;1;2;0
    

    The last one above is the ack generated automatically by the library.

    Then sent this, which requests the status of light child 99 on node 51, requesting an ack. Should return the ack and the response containing the current status of the light (0).
    51;99;2;1;2;anything

    0;0;3;0;9;send: 0-0-51-51 s=99,c=2,t=2,pt=0,l=8,st=ok:anything
    0;0;3;0;9;read: 51-51-0 s=99,c=2,t=2,pt=0,l=8:anything
    51;99;2;1;2;anything
    0;0;3;0;9;read: 51-51-0 s=99,c=1,t=2,pt=2,l=2:0
    51;99;1;0;2;0
    

    This "51;99;2;1;2;anything" is the ack generated automatically by the library, and this "51;99;1;0;2;0" is the response that my program performs. Correct!



  • Hi Marcus, you will find here how you can differentiate the messages:

    http://www.mysensors.org/download/serial_api_14

    Type Value Comment
    presentation 0: Sent by a node when they present attached sensors. This is usually done in setup() at startup.
    set 1: This message is sent from or to a sensor when a sensor value should be updated
    req 2: Requests a variable value (usually from an actuator destined for controller).
    internal 3: This is a special internal message. See table below for the details
    stream 4: Used for OTA firmware updates

    This is exact the way how we manage the message to deal with VoxCommando in our python code - example code snippet below:

    elif msgType=="0":
    #PRESENTATION Message
    log( "PRESENTATION Message")
    #registerID(sensorID)
    presTypeNum=dataParts[4]
    presType = presTypes[presTypeNum]
    log( "presType: "+presTypeNum+": "+presType)
    vc.triggerEvent("MySensorsPres."+presType+"."+friendlyID+"."+msgPayload, List[str]([dataParts[0],dataParts[1],msgPayload, presType]))

    elif msgType=="3":
        #INTERNAL Message
        #log( "INTERNAL Message" )
        IntsubTypeNum=dataParts[4]
        if IntsubTypeNum=="14":
            #gateway is ready!
            vc.triggerEvent("MySensors.GatewayReady",None)
        IntsubType = IntsubTypes[IntsubTypeNum]
        log( "Internal Message subType: "+IntsubTypeNum+": "+IntsubType )
        vc.triggerEvent("MySensorsIntsub."+IntsubType+"."+friendlyID+"."+msgPayload, List[str]([dataParts[0],dataParts[1],msgPayload, IntsubType]))
        if sData=="255;255;3;0;3;" :
            log( "Sensor has requested a new ID" )
            assignFreeID()


  • Thanks Kalle!
    But this part I was already aware. My doubt is how I could achieve the same on the node, meaning the sketch itself should be able to respond to a request from the controller (not the opposite as you showed above). I other words, my doubt was: how would the node respond for a request for current status of such node.

    This is also not implemented in the RGB sketch written by Dave, hence all the queries for status return values from the map tables, not the actual node. I'm going to implement the capacity of being queried on the RGB sketch, together with other small changes. The idea is that the node can be queried for current status at anytime because after this mod it will not be controlled exclusively by Vox Commando, but I will be able to control it also by claps and a wall switch.

    I've found the solution for this specific thread anyway, and as I showed in my previous post, it is working like a charm now!

    Good to hear from ya! Thanks!



  • Ahh ok, know I understand it - so it looks like you have successfully merged this funktion in the sketch.
    Is it possible to share the sketch which has include the ack?

    Thanks
    Kallle



  • Yes, I have done it already. Now I need to migrate all enhancements from my "Secret Clap Lights" sketch to the RGB controller sketch developed by Dave. I will share it with you guys once it is done. I'm now working on some enhancements on the Vox Commando part. Not sure yet if all of my ideas will work, but they look promising.

    I'm working to get Vox Commando to be smarter when working with My Sensors.
    The idea consists of making it know in advance what commands make sense for each type of sensor/actuator. Different values will be accepted as phrases and payloads for different devices automatically. No need to create a new command for every new standard sensor that you add to your network.
    It is a hard job it seems, but I think it is possible.

    Thanks!



  • @hek
    I have solved this by implementing a response for messages of type=2 on every sensor that I build, in addition to the processing for type=1 that is already included in the examples. Could you please confirm if this is supported or designed to work like this?
    I'm using my own custom controller that has support to send messages of type=2 (type=request) and it does process the responses from the nodes correctly, because the responses from the nodes are just normal set messages (type=1).



  • @marcusvdt

    Very interesting point!

    I had the exact same though.

    Wouldn't it be nice to have a feature on the controller to request the latest info from the sensor using the request message.

    Most examples I see do not implement this. I wonder if the original idea behind the request message was for inter sensor communication.

    Probably when the sensor is sleeping, an incoming message will be ignored. I wonder if when it wakes up and does the process it picks it up?

    The use case is something like a temperature sensor, sending the datava every 15 minutes and sleeping in between.

    Can you post your latest code snippet?

    Cheers



  • @barduino
    I'll post it for you tomorrow, but my main concern is to keep the things as much as possible conform the design of this protocol. It's because I don't want that my future version upgrades turn into a nightmare.
    That's why I wanted someone from mysensors staff to tell me about this.

    IMHO, there are many legitimate reasons for a controller needing to request information to sensors.
    In my case, I have light actuators that I designed to work either from a standard wall switch or by commands sent from the controller or by claps. I find the need to request information from the actuator node because sometimes the computer where the controller is running could be unreachable. Also some times the radio can fail. And sometimes I could just want to make sure the current status shown on my controller matches the current status of the actual node. Also, like you said, this allows node to request information from other nodes.


  • Admin

    @marcusvdt

    You are right, the idea (with REQ comands) was to let node requests response with a SET-command back to the requester. The examples does not have this functionality implemented. And as you might have suspected it was mainly thought to be used between sensors. But I haven't really had any use for it myself in my setup (easier to just push messages that do the request-setup).

    But there are use-cases, especially for sleeping nodes that can wake up and request data and go back to sleep when they have received their response.



  • Hi, is there any way in which I can identify the message type in my incomingMessage function?

    void incomingMessage(const MyMessage &message)
    {
    	**//I need to identify firstly the command type (REQ, SET,PRESENTATION,etc)**
    	// if(message.type == C_REQ)
    if (message.type == V_LIGHT) // this would be the subtype
    	{ 
    		if ((message.sensor - StartingChildID)  < noZones)
    		{   
    			Relays[message.sensor - StartingChildID].relayState = message.getBool();
    		}
    	}
    }
    

    I need to be able to request values at will from nodes. Mainly for debug purposes.

    Thanks. Regards


  • Admin

    mGetCommand(message)



  • Shouldn't be nice to have this feature directly emebebed in Mysensors library (as the ack request feature)?
    I mean, if I send a status request for a sensor through the controller, to ahve the library automaticcaly answer this type of messages.



  • @hek Why there is no method getCommand() in MyMessage object like isAck()?


  • Admin

    @alfredocdmiranda

    good question. 🙂



  • Is there any chance to create it for next version?


  • Admin

    @alfredocdmiranda

    I'll put it on the list.



  • @marcusvdt said:

    lightMsg

    Hello
    I wanted the same functionality and during forum found your post.
    Can you please post your code as attached code block has missing functions e.g lightMsg.set... SetLightState(..

    Thanks
    Ratnanabh



  • I would like to share my sketch for 2.0.0-beta. Its a node with two relays and the ability to read the status from mqtt.

    // Relais Node for MySensors 2.0.0-beta
    
    // I am using the filename and compile date for sktech announcements:
    #include <string.h>
    // Strip the path info from __FILE__ 
    // For Windows use '\\' instead of '/'.
    #define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
     
    // #define MY_DEBUG
    #define MY_RADIO_NRF24
    #define SKETCH_NAME __FILENAME__
    #define SKETCH_DATE __DATE__
    #define MY_NODE_ID 2
    #define MY_PARENT_NODE_ID 0
    #define MY_REPEATER_FEATURE false
    
    #include <SPI.h>
    #include <MySensor.h>
    
    #define RELAY_1  3  // Arduino Digital I/O pin number for first relay (second on pin+1 etc)
    #define NUMBER_OF_RELAYS 2 // Total number of attached relays
    #define RELAY_ON 0  // GPIO value to write to turn on attached relay
    #define RELAY_OFF 1 // GPIO value to write to turn off attached relay
    
    void before() { 
      for (int sensor=1, pin=RELAY_1; sensor<=NUMBER_OF_RELAYS;sensor++, pin++) {
        // Then set relay pins in output mode
        pinMode(pin, OUTPUT);   
        // Set relay to last known state (using eeprom storage) 
        digitalWrite(pin, loadState(sensor)?RELAY_ON:RELAY_OFF);
      }
    }
    
    void setup() {
      
    }
    
    void presentation()  
    {   
      // Send the sketch version information to the gateway and Controller
      sendSketchInfo(SKETCH_NAME, SKETCH_DATE);
    
      for (int sensor=1, pin=RELAY_1; sensor<=NUMBER_OF_RELAYS;sensor++, pin++) {
        // Register all sensors to gw (they will be created as child devices)
        present(sensor, S_LIGHT);
      }
    }
    
    void loop() 
    {
      
    }
    
    void receive(const MyMessage &message) {
    int msgcmd = mGetCommand(message);
      
      // Verify its a switch command (V_LIGHT)
      if (message.type==V_LIGHT) {
        // Command type 1 is a set command 
         if (msgcmd==1){
           // Change relay state
           digitalWrite(message.sensor-1+RELAY_1, message.getBool()?RELAY_ON:RELAY_OFF);
           // Store state in eeprom
           saveState(message.sensor, message.getBool());
         }
       // Command type 2 is a request for the sensor state
       if (msgcmd==2){
           // construct message body with requested sensor-id and type V_LIGHT (= switch) 
           MyMessage msg(message.sensor,V_LIGHT);
           // Example: requested sensor is 2 (message.sensor). 
           // The programm reads the digital pin 4, calculated by 2 (sensor) - 1 (static) + 3 (first relay pin)
           // after that, the reading is compared to the RELAY_ON value (might be invers logic, regarding to your relais schematic)
           // if it is matching, the payload is set to 1, otherwise its set to 0
           send(msg.set(digitalRead(message.sensor-1+RELAY_1) == RELAY_ON ? 1 : 0));
        }
      }
    }
    

    and here are the lines in my openhab2 items.item:

    Switch node2_sw1   "N2SW1"    (all,node2)    { mqtt=">[mosquitto:mysensors-in/2/1/1/0/2:command:ON:1],>[mosquitto:mysensors-in/2/1/1/0/2:command:OFF:0],<[mosquitto:mysensors-out/2/1/2/0/2:state:default]" }
    Switch node2_sw2   "N2SW2"    (all,node2)    { mqtt=">[mosquitto:mysensors-in/2/2/1/0/2:command:ON:1],>[mosquitto:mysensors-in/2/2/1/0/2:command:OFF:0],<[mosquitto:mysensors-out/2/2/2/0/2:state:default]" }
    

    I would like to repeat: Its for the last versions of mysensors and openhab2. Notice the absence of any "gw." and the different structure in the mqtt strings !

    Can someone tell me, if it is a problem to redefine the "MyMessage msg(message.sensor,V_LIGHT); " every time a request is done? I am not sure.

    Greetings



  • I dont see openHAB requesting any state, so maybe its a better idea to read the state of the output pin immediately after setting the relay and publish this and bind it to a control lamp. If this isnt toggling, the command wasnt received.


 

202
Online

8.8k
Users

9.5k
Topics

100.2k
Posts