No ACKs are received by LoPy4 LoRaWAN end node (although they are visible on nanogateway and TTN). How to change downlink payload and port?

  • Hi,

    I have two LoPy4 pycom devices. One is used as a nano gateway and another as a LoRaWAN end node. Both devices are running the official example code from the pycom lib folder on github. Nano gateway is based on while lorawan node is based on

    Have registered both devices on TTN successfully and can see uplink packets from LoRaWAN node on the TTN application server.

    To enable downlink ACKs I have added additional line in

    s.setsockopt(socket.SOL_LORA, socket.SO_CONFIRMED, True)

    Although I can see downlink messages on the TTN page and on my nano gateway (as it schedules downlink messages), my LoPy4 end node can't see those messages. I assume that's because these ACKs are coming to port 0 and don't contain any payload except typical LoRaWAN metadata.

    Please could you tell me if it's possible to configure the format of downlink messages so they could contain some useful ACK payload and come to different port (e.g. downlink ACK containing 'OK' and coming to port 2). Or, if possible, could you please provide some examples of pycom code where downlink ACKs are received correctly on the LoRaWAN pycom end node. I was looking at many topics on TTN and pycom forums, but couldn't find the answer for my problem unfortunately.

    Many thanks for your time and help!

    P.S. when I generate downlink messages manually from TTN application page, my LoPy4 LoRaWAN end node receive it successfully.

  • @vs Don't know the details about Chirpstack nor exactly what setup you have, but does Chirpstack show you the downlinks being sent? Does it state in what RX windows they are sent (RX1 or RX2), at which frequency, data rate, and actual time?

    In general, you should consider most LoRaWAN traffic to be best effort. It gets there if it gets there whenever it gets there. LoRaWAN is very suitable for non-time-sensitive applications such as metering or reporting. Not so much command-and-control.

    Nevertheless, downlinks exist in confirmed flavours exactly like uplinks. Though as downlinks are only sent in response to an uplink, it may take a while for retransmissions to be sent.

  • @jcaron I am just trying to make my downlinks being received on the LoPy4, as I am still struggling to get them work unfortunately. Although I can generate and receive downlinks on TTN and that works fine, I can't do similar things on ChirpStack for some reason. I would like to use Chirpstack as it provides the option to deploy a private lorawan server. Again, I am currently just experimenting with LoPy4 devices and don't have any concrete scenario in mind, besides just basic uplink/downlink functionality. To think about further use cases, I first need to be sure that downlinks can be seen by the LoPy4 end device (as downlink is a scarce resource so I want to be sure that it is received by the end node correctly).

  • @vs Nope, and that would probably break LoRaWAN compatibility. But what are you trying to achieve that would need that? You should view the whole LoRaWAN part (LoRaWAN stack on the LoPY + TTN) as a black box: you send data on one side, it is delivered to the other, and vice versa, and there should be everything you need on both sides to achieve that if you want to use regular, standard LoRaWAN tech & networks.

    I think you have something in mind that you are not sharing with us, it makes it difficult to try to guess where you are trying to get... Am I right?

  • @jcaron many thanks for your detailed explanation. Currently I am playing with both TTN and ChirpStack solutions to receive downlinks. Are there any pycom built-in methods for changing the LoRaWAN packet metadata (e.g. FCtrl fields etc.)? Couldn't find such on the pycom website and the socket class for LoRaWAN don't allow to do that I guess..

  • @vs The end-device receives the ACK, and uses it to decide that it does not need to re-transmit the confirmed packet, and discards it.

    The purpose for sending confirmed packets is to make sure they are received by the network:

    • A regular unconfirmed packet is sent on a best effort basis only. It is sent once, and the device hopes it gets there, but can't be sure. Fire and forget.

    • A confirmed packet will be automatically retransmitted by the node until it receives an ACK from the network that tells it "got it!", or a retry count is exceeded. So if a packet was lost en route (due to collisions or other forms of interference), it will be sent again, and hopefully received that time.

    You can receive downlinks after any uplink, confirmed or not. In normal conditions, a confirmed packet will always receive a downlink (just to carry that ACK), but that downlink frame can also contain a downlink payload if you sent one from the network to that device, or conversely, you can have a downlink with a payload even for a non-confirmed uplink.

    All downlinks (ACKs and/or payload) follow the same rules, being sent in RX1 or RX2, etc.

    If you want to send data from your application server to your node, you need to send a downlink, but this is completely independent from confirmed packets.

    How you do this depends on the network and the application protocol you use. Don't know the exact details for TTN, but I suppose this is documented.

    NB: Don't forget that downlinks (including ACKs for confirmed packets) are a very scarce resource, and TTN have very low limits on their use. As much as possible, regular operations should not need them.

  • @vs TTN itself just provides the network. What you want to achieve is a kind of application layer. For that you have to use an integration service. TTN offers standard one like Cayenne. But you can build your own. The integration service receives uplink messages from TTN and supplies downlink messages to TTN. see:

  • @jcaron ah I see, thanks! Any chance that I could see an ACK packet on my pycom device? Even with no payload, just to depict metadata. I guess the end-device should receive it in any case, otherwise what's the purpose for sending confirmed packets?

    I am currently just checking the downlink logic. The example could be that e.g. LoPy4 regularly sends some sensor data to TTN and at some point it should receive a downlink message from TTN with some useful payload (e.g. some custom control commands like reset the device when sensor readings are above the threshold etc.).

  • @vs you can’t. A confirmed packet ACK is just a bit of info (the frame counter for the relevant uplink frame IIRC) in the MAC part of the downlink. There’s no data, no port, no payload.

    If you want to send data to the node, just use a downlink, it will be sent in the response to an uplink in exactly the same way (but as payload).

    I’m still not sure what you are trying to achieve? Do you need to make sure your uplink has gone through? Do you need to send other data to the node?

  • @jcaron thank you for your reply and sorry if my question was a bit unclear.
    I would like to see a downlink message on my LoRaWAN LoPy4 node after it sends a packet to TTN which requires confirmation. From what I understand the confirmed uplink option is added by the following line (to the

     s.setsockopt(socket.SOL_LORA, socket.SO_CONFIRMED, True) 

    However, I can't see a downlink packet on my LoPy4 node even though I can see that the downlink message was sent from TTN (I can see it on the application page with traffic data) and also my nanogateway seems to see that downlink packet. TTN says that the downlink packet was generated to port 0 and has no payload. So I guess that's why my LoPy4 end node tells me that there are no downlink packets. So my question would be: how to program my LoPy4 in the way that once it sends a message which requires confirmation, the TTN generates downlink with some payload and send it back to LoPy4 on port 1 (rather than just send an empty message to port 0).


  • @vs I’m not sure I understand your issue. Confirmed packet ACKs are MAC features, so they are handled in the firmware, and are not visible from your application, they contain no data anyway.

    If you send downlink data it will be sent exactly the same way, but you’ll have a payload and you can receive it from your app, which you seem to have done.

    What’s wrong with that one? What are you actually trying to achieve?

    Also remember that downlinks are a scarce resource, they are quite limited, and unless you have very very few uplinks, you can’t have them all confirmed.

Log in to reply

Pycom on Twitter