Sending commands from nRF Cloud to LTE Device

Hi!

In our project, we need to send cloud-to-device commands. By "command" I mean a request to do some short action, not a request to change device's configuration/state. We see two ways of sending such requests:

1. C2D messages

2. Device shadow

What are the pros and cons of each way?

Here I see a recommendation to use Device shadow for commands sending. But if we talk about commands in the sense I described above, would this recommendation be still actual?

If yes, is there a recommended way (or some sample) of Device shadow usage for sending such commands? I mean, which fields Device shadow should contain and some algorithm of updating desired and reported section during sending command and its execution.

In case of C2D messages, the main problem seems to be command delivery when the device is offline. Right? Or is there some mechanism for this case in nRF Cloud?

Thank you!

Parents
  • Hello,

    So, there are two separate topics to subscribe. Please see https://docs.nordicsemi.com/bundle/nrf-cloud/page/APIs/MQTT/Topics.html#shadow-topics 

    `${deviceId}/shadow/get/accepted` - nRF Cloud will send delta only when you send empty message on  $aws/things/${deviceId}/shadow/get.
    `${deviceId}/shadow/update/delta` - nRf Cloud will send event automatically when change happens
    SO, you need to subscribe both. And when device is connecting again after offline period you send a empty message to the $aws/things/${deviceId}/shadow/get. This will trigger nRF Cloud to send the delta to the "get/accepted".
    Justin's coment that cloud would be sending even on "update/delta" is incorrect in the use case you described. Yes could will be sending the message if there is connection (device is online and keeping the connection alive). But if device is sleeping, the connection will close after 30 seconds. 
  • Hi  ,

    what you describe is what I would expect to see, but it's not what I am seeing.

    When the device comes online and sends an empty message to  $aws/things/${deviceId}/shadow/get it gets a shadow back, but that shadow does not contain the pending delta, even though the delta is pending in the cloud:

    i.e. a cloud API FetchDevice shows 

     "state": {

    "desired": {

    "nrfcloud_mqtt_topic_prefix": "prod/...",

    "pairing": {

    "state": "paired",

    "topics": {

    "d2c": "prod/.../d2c",

    "c2d": "prod/.../m/d/devsim4/+/r"

    }

    },

    "config": {

    "cmd": "command26"

    }

    },

     

    but the shadow received via ${deviceId}/shadow/get/accepted shows the following (note that the pending delta "config" is missing:

      "desired": {

        "nrfcloud_mqtt_topic_prefix": "prod/.../"

        "pairing": {

          "state": "paired",

          "topics": {

            "d2c": "prod/.../d2c",

            "c2d": "prod/.../+/r"

          }

        },

      },

    Here is the relevant code in the device simulator, this is executed in the onconnect callback:

     // subscribe to shadow gets `${deviceId}/shadow/get/accepted`
     await device.subscribe(device.topics.shadow.accepted);
    device.registerListener(device.topics.shadow.accepted, (param: { topic: string; payload: object}) =>
    {
    log.info(`shadow accepted: ${JSON.stringify(param.payload)}`);
    processCmd(param);
    });


    // subscribe to shadow rejected `${deviceId}/shadow/get/rejected`
     await device.subscribe(device.topics.shadow.rejected);
    device.registerListener(device.topics.shadow.rejected, (param: { topic: string; payload: object}) =>
    {
    log.info(`shadow rejected: ${JSON.stringify(param.payload)}`);
    });

    // subscribe to shadow updates `${deviceId}/shadow/update/delta`
     await device.subscribe(device.topics.shadow.delta);
    device.registerListener(device.topics.shadow.delta, (param: { topic: string; payload: object}) =>
    {
    log.info(`shadow delta: ${JSON.stringify(param.payload)}`);
    processCmd(param);
    });


    // since we just connected, get the current shadow from the cloud `$aws/things/${deviceId}/shadow/get`
     await device.publish(device.topics.shadow.get, {}); // payload is irrelevant

    Thanks,
    -- Terrence

  • Hello, 

    Can it be that your client already reports back the command and shadow send bending delta again? 

    I am seeing with my implementation that the nRF Cloud send new notification on /update/delta after I reported the previous command. Please see log below

    ************** MESSAGE SENT ***********
    TOPIC: $aws/things/nrfsim-608892161600763900000/shadow/get
    MESSAGE: [
    ""
    ]
    ***************************************

    ************** MESSAGE RECEIVED ***********
    TOPIC: nrfsim-608892161600763900000/shadow/get/accepted
    MESSAGE: {
    "desired": {
    "pairing": {
    "state": "paired",
    "topics": {
    "d2c": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/m/d/nrfsim-608892161600763900000/d2c",
    "c2d": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/m/d/nrfsim-608892161600763900000/+/r"
    }
    },
    "nrfcloud_mqtt_topic_prefix": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/"
    },
    "reported": {
    "control": {
    "alertsEn": true,
    "logLvl": 3
    }
    },
    "config": {
    "activeMode": true,
    "locationTimeout": 300,
    "activeWaitTime": 300,
    "movementResolution": 120,
    "movementTimeout": 3600,
    "accThreshAct": 4,
    "accThreshInact": 4,
    "accTimeoutInact": 60,
    "cmd": "command_100",
    "nod": []
    }
    }
    *******************************************


    ************** MESSAGE SENT ***********
    TOPIC: $aws/things/nrfsim-608892161600763900000/shadow/update
    MESSAGE: {
    "state": {
    "reported": {
    "config": {
    "cmd": "command_100"
    }
    }
    }
    }
    ***************************************

    NOTE! Extra delta received as it was not reported(cleared) by device 
    ************** MESSAGE RECEIVED ***********
    TOPIC: nrfsim-608892161600763900000/shadow/update/delta
    MESSAGE: {
    "state": {
    "nrfcloud_mqtt_topic_prefix": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/",
    "pairing": {
    "state": "paired",
    "topics": {
    "d2c": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/m/d/nrfsim-608892161600763900000/d2c",
    "c2d": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/m/d/nrfsim-608892161600763900000/+/r"
    }
    }
    }
    }
    *******************************************

    NOTE! Update of the config CMD in server side

    ************** MESSAGE RECEIVED ***********
    TOPIC: nrfsim-608892161600763900000/shadow/update/delta
    MESSAGE: {
    "state": {
    "nrfcloud_mqtt_topic_prefix": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/",
    "pairing": {
    "state": "paired",
    "topics": {
    "d2c": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/m/d/nrfsim-608892161600763900000/d2c",
    "c2d": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/m/d/nrfsim-608892161600763900000/+/r"
    }
    },
    "config": {
    "cmd": "command_101"
    }
    }
    }
    *******************************************

    ************** MESSAGE SENT ***********
    TOPIC: $aws/things/nrfsim-608892161600763900000/shadow/update
    MESSAGE: {
    "state": {
    "reported": {
    "config": {
    "cmd": "command_101"
    }
    }
    }
    }
    ***************************************

    NOTE! Extra delta received as it was not reported(cleared) by device 

    ************** MESSAGE RECEIVED ***********
    TOPIC: nrfsim-608892161600763900000/shadow/update/delta
    MESSAGE: {
    "state": {
    "nrfcloud_mqtt_topic_prefix": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/",
    "pairing": {
    "state": "paired",
    "topics": {
    "d2c": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/m/d/nrfsim-608892161600763900000/d2c",
    "c2d": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/m/d/nrfsim-608892161600763900000/+/r"
    }
    }
    }
    }
    *******************************************

    In other words, you cannot compare the device's received delta only to the "desired" in server. In server side you need to check both "reported" and "desired". If both contains the same value ("cmd":"command_101") it is not going to be as delta.   

    For example, after sending a new command to device my shadow looks like this:

    But device receives only:

    as the config part does not have delta

Reply
  • Hello, 

    Can it be that your client already reports back the command and shadow send bending delta again? 

    I am seeing with my implementation that the nRF Cloud send new notification on /update/delta after I reported the previous command. Please see log below

    ************** MESSAGE SENT ***********
    TOPIC: $aws/things/nrfsim-608892161600763900000/shadow/get
    MESSAGE: [
    ""
    ]
    ***************************************

    ************** MESSAGE RECEIVED ***********
    TOPIC: nrfsim-608892161600763900000/shadow/get/accepted
    MESSAGE: {
    "desired": {
    "pairing": {
    "state": "paired",
    "topics": {
    "d2c": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/m/d/nrfsim-608892161600763900000/d2c",
    "c2d": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/m/d/nrfsim-608892161600763900000/+/r"
    }
    },
    "nrfcloud_mqtt_topic_prefix": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/"
    },
    "reported": {
    "control": {
    "alertsEn": true,
    "logLvl": 3
    }
    },
    "config": {
    "activeMode": true,
    "locationTimeout": 300,
    "activeWaitTime": 300,
    "movementResolution": 120,
    "movementTimeout": 3600,
    "accThreshAct": 4,
    "accThreshInact": 4,
    "accTimeoutInact": 60,
    "cmd": "command_100",
    "nod": []
    }
    }
    *******************************************


    ************** MESSAGE SENT ***********
    TOPIC: $aws/things/nrfsim-608892161600763900000/shadow/update
    MESSAGE: {
    "state": {
    "reported": {
    "config": {
    "cmd": "command_100"
    }
    }
    }
    }
    ***************************************

    NOTE! Extra delta received as it was not reported(cleared) by device 
    ************** MESSAGE RECEIVED ***********
    TOPIC: nrfsim-608892161600763900000/shadow/update/delta
    MESSAGE: {
    "state": {
    "nrfcloud_mqtt_topic_prefix": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/",
    "pairing": {
    "state": "paired",
    "topics": {
    "d2c": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/m/d/nrfsim-608892161600763900000/d2c",
    "c2d": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/m/d/nrfsim-608892161600763900000/+/r"
    }
    }
    }
    }
    *******************************************

    NOTE! Update of the config CMD in server side

    ************** MESSAGE RECEIVED ***********
    TOPIC: nrfsim-608892161600763900000/shadow/update/delta
    MESSAGE: {
    "state": {
    "nrfcloud_mqtt_topic_prefix": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/",
    "pairing": {
    "state": "paired",
    "topics": {
    "d2c": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/m/d/nrfsim-608892161600763900000/d2c",
    "c2d": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/m/d/nrfsim-608892161600763900000/+/r"
    }
    },
    "config": {
    "cmd": "command_101"
    }
    }
    }
    *******************************************

    ************** MESSAGE SENT ***********
    TOPIC: $aws/things/nrfsim-608892161600763900000/shadow/update
    MESSAGE: {
    "state": {
    "reported": {
    "config": {
    "cmd": "command_101"
    }
    }
    }
    }
    ***************************************

    NOTE! Extra delta received as it was not reported(cleared) by device 

    ************** MESSAGE RECEIVED ***********
    TOPIC: nrfsim-608892161600763900000/shadow/update/delta
    MESSAGE: {
    "state": {
    "nrfcloud_mqtt_topic_prefix": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/",
    "pairing": {
    "state": "paired",
    "topics": {
    "d2c": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/m/d/nrfsim-608892161600763900000/d2c",
    "c2d": "prod/2e1ed31b-0c2e-45fb-918e-bdc66a68b002/m/d/nrfsim-608892161600763900000/+/r"
    }
    }
    }
    }
    *******************************************

    In other words, you cannot compare the device's received delta only to the "desired" in server. In server side you need to check both "reported" and "desired". If both contains the same value ("cmd":"command_101") it is not going to be as delta.   

    For example, after sending a new command to device my shadow looks like this:

    But device receives only:

    as the config part does not have delta

Children
  • Hi  

    Thanks for the reply. Question: was your device/simulator offline when you sent this:

    NOTE! Update of the config CMD in server side

    My experiments show what you describe, but only when the device is online, not when it is offline.

    When the device is offline while the config CMD is updated on the server side and then the device comes online then the updates to config CMD seem to be lost - they are not sent as further delta updates nor included in the full shadow.

    Can it be that your client already reports back the command and shadow send bending delta again? 

    I considered that, but the device is definitely not reporting back because I just tested commenting out the reporting code -- same behavior.

    This continues to confuse me. My expectation is that -- as long as the device has not reported (cleared) the delta -- then the delta will remain present in the desired section to any shadow message to the device. 

    Best,
    -- Terrence

  •   

    I think I finally have some insight. I think the nRF Cloud incorrectly trims away parts of the desired section, whereas AWS doesn't.

    Here is what I did:

    1. The previous reported state of the device is {"cmd": "sendReport": "7"}

    2. The device is offline

    3. I send the desired update to the server {"cmd": "sendReport": "8"}

    4. When the device comes online it gets the shadow via ${deviceId}/shadow/get/accepted.

    The shadow shows config {"cmd": "sendReport": "7"} but is missing desired {"cmd": "sendReport": "8"}:

    ************** MESSAGE RECEIVED ***********

    TOPIC: devsim4/shadow/get/accepted

    MESSAGE: {

      "desired": {

        "pairing": {

          "state": "paired",

          "topics": {

            "d2c": "...",

            "c2d": "..."

          }

        },

        "nrfcloud_mqtt_topic_prefix": "..."

      },

      "reported": {

        "control": {

          "alertsEn": true,

          "logLvl": 3

        }

      },

      "config": {

        "cmd": {

          "sendReport": "7"

        }

      }

    }

    *******************************************

    4. I restart the device and it now it gets the shadow via $aws/things/${deviceId}/shadow/get/accepted.

    The shadow now contains the desired {"cmd": "sendReport": "8"}:

    ************** MESSAGE RECEIVED ***********

    TOPIC: $aws/things/devsim4/shadow/get/accepted

    MESSAGE: {

      "state": {

        "desired": {

          "nrfcloud_mqtt_topic_prefix": "...",

          "pairing": {

            "state": "paired",

            "topics": {

              "d2c": "...",

              "c2d": "..."

            }

          },

          "config": {

            "cmd": {

              "sendReport": "8"

            }

          }

        },

        "reported": {

          "connection": {

            "status": "connected",

            "keepalive": 30

          },

          "control": {

            "alertsEn": true,

            "logLvl": 3

          },

          "config": {

            "cmd": {

              "sendReport": "7"

            }

          },

    I did nothing else, the device did not report any changes and I did not resend any commands. 

    It looks like the nRF cloud ${deviceId}/shadow/get/accepted trims the desired delta, regardless of whether the device received it (i.e. was offline) or reported (cleared) it. 

    This does not look right. Why is the config section of desired trimmed away by nRF Cloud? What is the correct way to do this?

    Thanks,
    -- Terrence

     

  • Hello,

    I'll report this to the RnD.

    I tried to simulate the device being offline just by stopping the simulator after the device had been associated. At least the device did not get update/delta but received correct info with get/accepted.

    I'll check from our testing engineers that do we have this kind of test case in our setup. I suspecting that no or there is similar wrong approach as what I used. 

  • Hello  

    You are right, the nRF Cloud trims the config from the shadow. 

    There is now a BETA feature in production for you to try out. Unfortunately I have not been able to test this yet, so only "copy and pasting" instructions for you.

    There are now new topics:

    subscribe "{deviceId}/shadow/get/accepted/tf" to receive the transformed subset of the devices full shadow (response to `.../get/tf`). See (3)

    Publish to "{deviceId}/shadow/get/tf" to request a subset of the devices full shadow, defined by a transform.

    Transformation example

    The required 't' property defines the transform string or JSON object.

    The optional 'l' property defines the payload size limit (500 bytes in this example):

    ```
    {'t':'state.desired.config', 'l': 500}
    ```

    The transformation is used in REST side and you can check https://docs.nordicsemi.com/bundle/nrf-cloud/page/APIs/REST/Tutorials/Transforms.html 

    This should enable you to get only the parts you need with the get/tf. 

Related