MKR NB 1500 downlink - howto?

Hi, I have an Arduino MKR NB1500 with CoAP up and running, it has worked ok for some months now.
I followed the tutorial: Arduino MKR NB1500 - CoAP | Start IoT

Now, I need to extend the solution by downlink functionality, and I tried the description from the Device Communication IoTGW side, found here:

My working solution uses the uplink transform as described in mkr-nb1500 tutorial, i.e.:
return JSON.parse(payload.toString(“utf-8”));

In the IoTGW document, a different uplink transform is described, and the downlink transform is also described.
I have adjusted it to fit my system and my transforms look now as follows:

// Uplink
var parser = new Parser()
.endianess(“little”)
.string(“vlt”, { zeroTerminated:true })
.string(“tmp”, { zeroTerminated:true })
.string(“hum”, { zeroTerminated:true })
.string(“latlng”, { zeroTerminated:true });

return parser.parse(payload);

// Downlink
payload = [
state.vlt, 0,
state.tmp, 0,
state.humty, 0,
state.latlng, 0
];

return {
transport: “coap-pull”,
coapPath: “/”,
port: 5683,
payload: payload
};

At the device side, I have modified the message sent (buffer) as follows:

// uint32_t buf_size = snprintf(buffer, 100, “{“vlt”:%.2f,“tmp”:%.2f,“hum”:%.2f,“latlng”:“59.67, 9.65”}”, 12.8, tmp, hum);
uint32_t buf_size = snprintf(buffer, 100, “%.2f %.2f %.2f 59.67,9.65”, 12.8, tmp, hum);
buffer[5] = 0; // null termination of the string
buffer[11] = 0; // null termination of the string
buffer[17] = 0; // null termination of the string

I have tested variation of the above description, but I am not able to send data to MIC
(if I changes back to the JSON.parse transform it work as before)

I must admit both the MIC and the CoAP are a new domain for me, maybe it is some basics I not have yet understood…
If anybody has some tips or clues to guide me, that would be much appreciated.

Gunnar Holm

Hi @gunnar.holm - first up, welcome to the forum! Great to have you here!

You’re not doing anything wrong, unfortunately downlink messages are a little tricky to get working (it’s a limitation we are aware of, and working on, however most devices use uplink only)

My first question is to make sure you are receiving data packets to the Arduino when you do a downlink message?

Thank you, @robert_colvin and thank you for the response.
In fact, I’m not sure I receive data and neither if data is sent from the MIC…

Here is what I tried:
To get any uplink messages to the MIC, I re-inserted the json uplink transform (return JSON.parse(payload.toString(“utf-8”)):wink:
The downlink transform is still as described previously.

When the MIC now has connection with my device, I uses the ‘Set’ function of a Dashboard widget to set (what I imagine) can be a reference value for the corresponding state of the device.

In the arduino sketch there is an coap.get() function.
In addition I have added a callback function that responds upon messages received.
Its like this:
// CoAP client response callback
void callback_response(CoapPacket &packet, IPAddress ip, int port) {
Serial.println("[Coap Response got]");
Serial.println(packet.payloadlen);

char p[packet.payloadlen + 1];
memcpy(p, packet.payload, packet.payloadlen);
p[packet.payloadlen] = 0; //NULL;

Serial.println(p);
}

The callback responds upon the coap.get() function, but always the packet length is 0 and the packet contains nothing.

One question is – does the widget ‘Set’ function send messages to the device? (Or is there other ways that I have overseen?).

hi @gunnar.holm - apologies for the delay in replying.

from what you show above, it looks like you are on the right track. if a widget is settable in the dashboard, it should send a message down to the device. What can happen though is that the downlink queue can get full as uplink messages have an acknowledge, when this happens, messages queued for downlink can be discarded (an issue we are aware of). So what you can try is to do a get after each uplink to ensure the queue is empty.

let me know how that goes! I have been looking to improve our tutorial in this, so I’ll try and find a suitable device

hi again @gunnar.holm

owing to the issues you have found with downlink, we took a discussion yesterday to look at improving downlink support at some point. Unfortunately though, for the time being, we will not be supporting downlink in IoT GW. We will not remove anything, so it will remain as it is today, however we will not be officially supporting it. Having said that. i’ll do my best to support here within the limitations

hi @robert_colvin , thank you for updating on this.

Infact I came a bit further, I managed to get uplink data with the non-json uplink parser - it started working when I added zero-termination also at the end of the buffer:

image

I observe the message sent from MIC in the MIC development tool when I set the Thing state “tmp” to 22.22 from a widget :

    "topic": "thing-update/StartIoT/xxxxx.com/00002040",
    "payload": {
      "state": {
        "desired": {
          "tmp": 22.22
        }
      }
    },
    "timestamp": 1652306627728

It seems promising, but the widget keeps the desired-value information, and I do not receive any data packages on the Arduino. (I guess the MIC hold the desired-value intication until ack’ed by the Arduino. But no message received - no ack…)

I also tried to activate the coap-server on the Arduino, and to use the coap-push as transport. But no success…
The queue-issue I have also tried to handle by multiple gets (infact also tested with get only - no sending). No difference.

The coapPath I have changed from ‘/foo’ to ‘/’ (I cannot see ‘foo’ is used in the MKR NB1500 Coap example)

Im sorry to hear the downlink will not be supported. I hope it will be possible to manage anyway, but if I could ask - can MQTT be a better option to achieve downlink functionality? or maybe another ‘thing’ like the Nordic Thingy:91?

hi @gunnar.holm apologies for the long delay in replying to this, not sure what happened!

  1. You are correct, MIC is waiting for you to ack the desired value to update the widget. to do this you need to set the desired state to null on the next update. This is (sort of) documented here (for MQTT, but the widget operates the same way with CoAP): on this page there is a code sample Using the Thing API - Managed IoT Cloud where it states:
// If the change is successful you should communicate this back to
  // Managed Iot Cloud using the reported state and clearing desired.

  const state = {
    desired: null,
    reported: {
      counter: msg.state.counter
    }
  }

doing so will clear the widget

  1. you are correct, /foo is not supported in our CoAP endpoint - that is a document error (thanks for finding that!) I have used CoAP Pull with limited success (hence the removal of support).

  2. At this time, we will keep the code, and if i get the time (since its no longer supported) I will keep working with it and see what can be done, but no guarantees! MQTT is a much better option for downlink support, the main issue with that is the overhead that TLS requires; though the NB1500 can handle this using the MQTT stack built into the sara modem, we have a tutorial for that as well. If you have the ability to use a device with a bigger CPU / Memory that can handle TLS, that would be recommended. MQTT also has a bigger impact on power usage if you are thinking about a battery device.

once again, my apologies for the delay :slight_smile:

Thank you for the feedback @robert_colvin :slight_smile:

you’re welcome @gunnar.holm - please keep us posted on your progress and if you have anything further. When I have the time available I will take a look at the downlink process