Attempt to implement a personal downlink mode

  • Hi all,

    I bought some Lopys and the ic880a concentrator for a Raspberry Pi.

    Well, I see that the TTN works like a charm, but I wanted to create a personal network avoiding it using LORAWAN with ABP auth.

    With poly packet forwarder there are some useful examples(util_ack and util_txt_test) to implement in C a server that listen the gateway and follows the Semtech protocol.

    I don't have any problems with the uplink path.I can decipher the Lopy's data in order to be used in a web service. But i cannot receive nothing in the Lopy when I try to implement a script for the downlink mode. Let's say for the moment is just manual and not automatic.

    Debugging the HAL and AUX and logging the incomming PULL_RESP data and the txpk object, I could capture the data when I use the TTN network. However, if i try to use the same data with the util_tx_test, I can send though the HAL, but I dont receive it in the Lopy. I aproximate a timestamp and in order to be validated by the concentrator I reset the poly packet forwarder script. So it captures the PULL_RESP data and after 50 seconds it is sent though the HAL.

    For example, this a message from TTN

    "txpk": {
    "imme": false,
    "tmst": 16837923,
    "freq": 868.1,
    "rfch": 0,
    "powe": 14,
    "modu": "LORA",
    "datr": "SF7BW125",
    "codr": "4/5",
    "ipol": true,
    "size": 15,
    "data": "YEoXASYAAAABfhUpFU3H"

    With loragw_hal.c the way to interpretate the pattern is the following one:
    0..2 = TX PLL frequency
    3..6 = 0x0100e747= the timestamp in hex(same as 16837923)
    9 = 0x97 = 0x07 | 0x80 | 0x10 = DR_LORA_SF7 with CR_LORA_4_5 and CRC enabled
    10 = 0x0f = data size, 15 bytes
    8, 14, 15 = 0x00 = not used
    16..30 = 0x604A170126000000017E1529154DC7 = the 15 bytes payload (same as YEoXASYAAAABfhUpFU3H)

    And here is the data when I use the modified script of util_tx_test:
    "txpk": {
    "imme": false,
    "tmst": 65427707,
    "freq": 868.0999756,
    "rfch": 0,
    "powe": 14,
    "modu": "LORA",
    "datr": "SF7BW125",
    "codr": "4/5",
    "ipol": true,
    "size": 15,
    "data": "YEoXASYAAAABfhUpFU3H"

    As you can see, apart of a different timestamp, it is sent the same info. But in the Lopy in the first case I receive a b'\xab\xbb' and nothing in the second case.

    By the way, I tend to reset the Lopy before each trial, and of course this is not the first attempt.

    If I use the node plugin lora-packet I am able to decipher the data in a Node.js script with the same Dev Addr,App and NWK Skeys that have the Lopy.

    Here is the code for the Lopy
    from network import WLAN,LoRa
    from machine import Pin
    import time,pycom,socket,binascii,struct

    def Pulsado(button):
    global pulsado

    lora = LoRa(mode=LoRa.LORAWAN, sf=7)
    file = open('/flash/abp/keys.txt', 'r')

    for j in range(3):

    dev_addr = struct.unpack(">l", binascii.unhexlify(keys[0].replace(' ','')))[0]
    nwk_swkey = binascii.unhexlify(keys[1].replace(' ',''))
    app_swkey = binascii.unhexlify(keys[2].replace(' ',''))
    lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey), timeout=0)

    pycom.rgbled(0x7f0000) # red
    while not lora.has_joined():

    s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
    s.setsockopt(socket.SOL_LORA, socket.SO_DR, 5)
    s.setsockopt(socket.SOL_LORA, socket.SO_CONFIRMED,False)
    button=Pin("G17",Pin.IN, pull=Pin.PULL_UP)
    irq=button.callback(trigger=button.IRQ_FALLING, handler=Pulsado)

    while pulsado==0:
    pycom.rgbled(0x007f00) # green
    #s.send(bytes([0x50, 0x02, 0x03]))
    rx = s.recv(256)
    if rx:
    pycom.rgbled(0xff00ff) # fuchsia
    pycom.rgbled(0x7f7f00) # yellow


    I know that if I use the property imme as true, it will be sent immediately, but it will be consider as a Class C and the Lopy is a Class A. Maybe can I modify the script(and reflashing the firmware) that checks if the incomming data is ok in the Lopy to debug if it really receives something?

    Kind regards,


  • @jmarcelino said in Attempt to implement a personal downlink mode:

    and then maybe dive into the whole thing Specification 1R0.pdf

    This version is outdated, current is 1.0.2

  • @jmarcelino Well, in the end I could implement it. The rare thing is that I am only able to receive a PULL_RESP in the poly packet forwarder after it is sent a PULL_DATA and not a PUSH_DATA. Seems that the gateway is also listening some time frames and not constantly.

  • @jmarcelino Hi and thanks for the reply.

    Should I send the PULL_RESP message after a PULL_DATA or a PUSH_DATA?In the util_tx_test is after a PULL_DATA, but as you know it, the gateway sents it periodically. However, it seems reasonable to be sent after a PUSH_DATA because of the RX windows.

    The script is stored in local with the gateway's script. In most cases, the messages though UDP are received in less than 1 ms(sometimes between 1 and 2 ms). So after all, should I add exactly 1s or should I substract the time between the node sends data, it pass though the gateway to the 'server', and it replies back(some ms)?I will try to add 1s on the timestamp, which is in us to see what happends.

  • @Pajarraco
    Using the LoPy in LoRaWAN mode you have to respect the receive windows (when the radio is actively waiting to receive) according to LoRaWAN spec. The first window Rx1 as you found is at 1s after transmission. For Class A outside of the two receive windows the node cannot receive anything, the radio is off to save power.

    I recommend reading the gentle introduction to the LoRaWAN spec at

    and then maybe dive into the whole thing Specification 1R0.pdf

    The tmst isn't just used as a timestamp but as a reference (based on the time the uplink was made) for when the gateway should transmit the packet to match the node's receive window.

    Hope this helps - let me tell you have a very interesting project in your hands :-)

  • Well, seems that the result should be to add to the timestamp 1s after a uplink is sent. So I will try to program a script in node, because i would find a little hard to do it in C.

Pycom on Twitter