How to implement downlink messages



  • I had some trouble getting downlink messages to work reliably.

    Some things that I did not find in the documentation:

    • When a downlink is received, is there a way to know what port it was sent to?
    • Is a downlink receive event triggered if no socket is bound to that port?
    • How do confirmed downlinks work? Is the downlink confirmed automatically? When does that happen, when the packet is received, or when the callback is called, or when the data is read from the socket?
    • Does it matter whether sockets on downlink ports are blocking or non-blocking?

    In my case sometimes I received the downlinks (just after sending a packet myself), sometimes I didn't. And I don't really know what the criteria are, or how this is supposed to work.

    How do downlinks work? A real-world example with LoRaWAN downlinks would be really useful.



  • @jmarcelino wow, thanks a lot for that find. explains the headaches I've been having :)



  • @jmarcelino Thanks. I was trying to get downlink messages for about a week now without success.
    Changing port to 1 helped.



  • @dbrgn
    I came across this one today and found there is a problem in the current implementation, only downlinks to ports 1 and 2 are being accepted.

    https://github.com/pycom/pycom-micropython-sigfox/issues/46

    May explain your problems.



  • @jcaron said in How to implement downlink messages:

    @dbrgn Just to be sure, you are aware of the 10 downlink messages per day limitation on TTN? Note that this includes confirmed uplink ACKs. Basically downlinks are unusable on LoRaWAN, at least in the ETSI region.

    Yes, but I think so far that's just a soft limit. I won't do that many downlinks in production, but I think it's fair if I occasionally go over the limit while testing as long as I don't exceed the 1% duty cycle, especially since both gateways in my area are provided by me :)

    @jmarcelino said in How to implement downlink messages:

    but what happens if you do uplinks and downlinks using the same port number instead of different as you're doing?

    I think I tried that too, without better results.

    It may be that you really only have one LoRa socket for the whole system so the uplink and downlink port numbers must match.

    If that would the case, the LoRaWAN stack would be pretty useless :) But I don't think it's the case. At least sending on different ports works properly.



  • @dbrgn Just to be sure, you are aware of the 10 downlink messages per day limitation on TTN? Note that this includes confirmed uplink ACKs. Basically downlinks are unusable on LoRaWAN, at least in the ETSI region.

    Not quite sure how this would appear in logs though.



  • @dbrgn said in How to implement downlink messages:

    I use relaxed frame counters for development.

    Ok I asked because "relaxed frame counters" means TTN disregards uplink counters when authenticating incoming messages but it doesn't mean the LoPy will disregard frame counters on downlinks: TTN's counter must always be higher than what the LoPy expects (up to a "max frame counter gap" ...). But yes that is happening and not the problem in this case.

    @dbrgn said in How to implement downlink messages:

    Maybe I'll get the possibility to test with a different network in the next days (not sure if I have coverage). But I haven't heard of downlink issues with TTN so far.

    Downlinks in general should work. What I meant is there are reported issues with the display of confirmed downlinks on TTN's console.

    All my LoPy are tied up at the moment so I can't test this, but what happens if you do uplinks and downlinks using the same port number instead of different as you're doing? It may be that you really only have one LoRa socket for the whole system so the uplink and downlink port numbers must match.



  • @jmarcelino said in How to implement downlink messages:

    OK since you're using ABP how are you handling the frame counters? Resetting them through the TTN console?

    I use relaxed frame counters for development.

    I see in your example your uplink counter seems to have started back from 1 when the first downlink counter there is 7.

    I think that makes sense, since the uplink frame counters are managed by the device (and reset when the device resets) while the downlinks are managed by the server, where the counter can be stored properly.

    To make sure that nothing is wrong with the configuration, I deleted and re-created the device in the TTN backend. Then I re-did the same example script with OTAA instead of ABP. Joining worked (so downlink transmission seems to work fine in principle). But the downlinks did not arrive.

    Maybe I'll get the possibility to test with a different network in the next days (not sure if I have coverage). But I haven't heard of downlink issues with TTN so far.



  • @dbrgn
    OK since you're using ABP how are you handling the frame counters? Resetting them through the TTN console?

    I see in your example your uplink counter seems to have started back from 1 when the first downlink counter there is 7.

    TTN's console display of confirmed downlinks is a bit buggy AFAIK so you might not see it in the browser.



  • Also, here's an excerpt from the gateway logs, after sending two downlinks:

    Jul 28 20:54:15 ttn-gw-test ttn-gateway[2881]: ### [DOWNSTREAM] ###
    Jul 28 20:54:15 ttn-gw-test ttn-gateway[2881]: # PULL_DATA sent: 3 (100.00% acknowledged)
    Jul 28 20:54:15 ttn-gw-test ttn-gateway[2881]: # PULL_RESP(onse) datagrams received: 2 (346 bytes)
    Jul 28 20:54:15 ttn-gw-test ttn-gateway[2881]: # RF packets sent to concentrator: 2 (28 bytes)
    Jul 28 20:54:15 ttn-gw-test ttn-gateway[2881]: # TX errors: 0
    

    I'm not sure I read this correctly, but I think that means that 2 downlink packets were sent over the air from the gateway to the node.



  • Thanks for your answers, @jmarcelino and @jcaron :)

    @dbrgn what happens if instead of listening for events you actually try to receive data on the socket right after sending?

    Good idea. Does not seem to work though :(

    Sending packet 3...
    Try to receive on port 150...
    Timeout
    A LoRa event occured: Lora packet sent
    Sending packet 4...
    Try to receive on port 150...
    Timeout
    A LoRa event occured: Lora packet sent

    This is the code I used:

    # Initialize incoming socket
    s_in = usocket.socket(usocket.AF_LORA, usocket.SOCK_RAW)
    s_in.setblocking(False)
    s_in.settimeout(1)
    s_in.bind(150)
    
    # Main loop
    counter = 0
    while True:
        print('Sending packet %d...' % counter)
        s_out.send(bytes([counter]))
        counter += 1
        print('Try to receive on port 150...')
        try:
            received = s_in.recv(64)
            print('Received: %r' % received)
        except:
            print('Timeout')
        utime.sleep_ms(10000)
    

    I feel your original post is spreading into too many topics

    You might be right. On the other hand, a single known-to-be-working example by Pycom could probably cover all those areas :)

    I see from your code your appear to be using a single channel gateway?

    No, it's an iC880A board with the poly packet forwarder that a lot of other people run in production. Joining via OTAA works, so it's definitely capable of doing downlinks.

    Can you post a screenshot of the TTN "Gateway Traffic" page for your gateway, showing the frequencies and transmission times?

    Great idea. Here's the screenshot of four uplinks and one downlink:

    screenshot

    I also tried to send a confirmed downlink. According to the gateway stats it was only sent down once, but I can't see any flag whether it was confirmed or not by the LoPy. Not sure how TTN handles the displaying of confirmed downlinks.



  • Hi @dbrgn,

    I feel your original post is spreading into too many topics: your problems with downlinks, downlink port numbers, confirmed downlinks and blocking vs non-blocking. Makes it hard for someone to help out and thus it becomes less likely to get an answer at all.

    I'd recommend limiting to one or two questions per post.

    Let's start with the difficulties with downlinks. You say you get some occasionally but not all. I see from your code your appear to be using a single channel gateway? Is this another LoPy running the nano gateway code?

    Can you post a screenshot of the TTN "Gateway Traffic" page for your gateway, showing the frequencies and transmission times?



  • @dbrgn what happens if instead of listening for events you actually try to receive data on the socket right after sending?



  • Some more experiments due to lack of documentation and feedback. The base is this script:

    import binascii
    from network import LoRa
    import usocket
    import ustruct
    import utime
    import pycom
    
    DEV_ADDR = ''
    NWK_SKEY = ''
    APP_SKEY = ''
    
    # Disable LoPy heartbeat
    pycom.heartbeat(False)
    
    # Join LoRaWAN network
    lr = LoRa(mode=LoRa.LORAWAN)
    auth = (
        ustruct.unpack('>l', binascii.unhexlify(DEV_ADDR.replace(' ', '')))[0],
        binascii.unhexlify(NWK_SKEY.replace(' ', '')),
        binascii.unhexlify(APP_SKEY.replace(' ', '')),
    )
    print('Joining')
    lr.join(activation=LoRa.ABP, auth=auth)
    while not lr.has_joined():
        utime.sleep_ms(1000)
        print('Not yet joined...')
    print('Joined')
    
    # Set callback
    def lora_callback(lora):
        print('A LoRa event occured: ', end='')
        events = lora.events()
        if events & LoRa.RX_PACKET_EVENT:
            print('Lora packet received')
        elif events & LoRa.TX_PACKET_EVENT:
            print('Lora packet sent')
        else:
            print('Other event: %s' % events)
    
    lr.callback(LoRa.RX_PACKET_EVENT | LoRa.TX_PACKET_EVENT, lora_callback)
    
    # Initialize socket
    s_out = usocket.socket(usocket.AF_LORA, usocket.SOCK_RAW)
    s_out.setsockopt(usocket.SOL_LORA, usocket.SO_DR, 5)
    s_out.setsockopt(usocket.SOL_LORA, usocket.SO_CONFIRMED, False)
    s_out.setblocking(False)
    s_out.bind(100)
    
    # Main loop
    counter = 0
    while True:
        print('Sending packet %d...' % counter)
        s_out.send(bytes([counter]))
        counter += 1
        utime.sleep_ms(10000)
    

    With this, a packet is sent out every 10 seconds on FPort 100. I scheduled a downlink to FPort 200, between packet 2 and 3:

    screenshot

    This is the log:

    Sending packet 0...
    A LoRa event occured: Lora packet sent
    Sending packet 1...
    A LoRa event occured: Lora packet sent
    Sending packet 2...
    A LoRa event occured: Lora packet sent
    Sending packet 3...
    A LoRa event occured: Lora packet sent

    As it can be seen from the screenshot and the log, the downlink was scheduled and delivered, but no event was raised on the LoPy. From that, I'd interpret that if there's no socket initialized on the port, nothing happens.

    So I sent the downlink on FPort 100 instead:

    screenshot

    The downlink was seemingly delivered, yet no message in the events callback.

    Ok, maybe there needs to be a socket that hasn't been used for transmitting yet? Let's try this:

    # Initialize incoming socket
    s_in = usocket.socket(usocket.AF_LORA, usocket.SOCK_RAW)
    s_in.bind(150)
    

    Now I'll send a downlink to port 150:

    screenshot

    Again, downlink sent, no event happened in the callback.

    What am I doing wrong?

    I'm on firmware version 1.7.8.b1. I'm connected to The Things Network. The gateway is 10 meters from here with very good signal strength.



    • gateway type

    iC880A concentrator board

    • network server type

    The Things Network

    Are you expecting help or just telling your story?

    No reason for that derogatory tone. I had some clear questions in my original post. Additionally, i was stating that there is currently no high level documentation or sample code on receiving / handling downlink messages. (Counterexample: Joining via OTAA/ABP is well documented with an introduction and a code example.)



  • You are not specifiying

    • gateway type

    • network server type

    • sample code to demonstrate usage

    Are you expecting help or just telling your story?



Pycom on Twitter