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

Reply
  • 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

Children
No Data
Related