How I got reliable TTN connectivity in Australia

  • For the last few weeks I have been working with a small collection of FiPy and LoPy development units to do some data collection and upload over LoRa to The Things Network (TTN). During this period I have experienced numerous issues getting reliable communication here in Adelaide, Australia. I believe I have now resolved all of my issue and I thought I would share with the rest of the community how I managed it, in part because I would not have got here without all the bits of the puzzle posted by others on the forum. Also partly because I'd like some confirmation that I havn;t got something wrong somewhere.

    I'l start with my conclusion: The Pycom LoRa Region implementation is not currently compatible with TTN channel setup for Australia AU915. I'm using the 1.20.1r1

    The LoRaWAN Regional parameters V1.1rB available here, details the channel parameters for a number of regions including AU915-928MHz ISM Band. This details the uplink and downlink channels as follows:

    • Upstream – 64 channels numbered 0 to 63 utilizing LoRa 125 kHz BW varying from DR0 to DR5, using coding rate 4/5, starting at 915.2 MHz and incrementing linearly by 200 kHz to 927.8 MHz
    • Upstream – 8 channels numbered 64 to 71 utilizing LoRa 500 kHz BW at DR6 starting at 915.9 MHz and incrementing linearly by 1.6 MHz to 927.1 MHz
    • Downstream – 8 channels numbered 0 to 7 utilizing LoRa 500 kHz BW at DR8 to DR13) starting at 923.3 MHz and incrementing linearly by 600 kHz to 927.5 MHz

    TTN channel plan for Australia available here , details the following channel plan:

    • 916.8 - SF7BW125 to SF10BW125
    • 917.0 - SF7BW125 to SF10BW125
    • 917.2 - SF7BW125 to SF10BW125
    • 917.4 - SF7BW125 to SF10BW125
    • 917.6 - SF7BW125 to SF10BW125
    • 917.8 - SF7BW125 to SF10BW125
    • 918.0 - SF7BW125 to SF10BW125
    • 918.2 - SF7BW125 to SF10BW125
    • 917.5 - SF8BW500


    • 923.3 - SF7BW500 to SF12BW500
    • 923.9 - SF7BW500 to SF12BW500
    • 924.5 - SF7BW500 to SF12BW500
    • 925.1 - SF7BW500 to SF12BW500
    • 925.7 - SF7BW500 to SF12BW500
    • 926.3 - SF7BW500 to SF12BW500
    • 926.9 - SF7BW500 to SF12BW500
    • 927.5 - SF7BW500 to SF12BW500

    Now for AU915 the data rate mappings are:
    SF7BW125 = DR5,
    SF10BW125 = DR2
    SF8BW500 = DR6
    SF7BW500 = DR13
    SF12BW500 = DR8

    Given this information it is clear that while TTN downlink channel set matches with the LoRaWAN regional parameters, TTN uplink channel set is a subset of those in the LoRaWAN regional parameters.

    To conform to the TTN plan we need to change the default uplink channel set for AU915.

    The Pycom LoRa API provide an interface for adding and removing channels (Note: although I cannot find documentation of this, it appears to specifically relate to the Transmit channels only). That's OK, I only need to change the uplink channels for my LoRa device.
    So we can remove the exiting channels specifications and add new ones like this:

    # Set  AU ISM 915 channel plan for TTN Australia
    # remove all the channels
    for i in range(0, 72):
    # Add the 8 125MHz uplink channels for TTN configuration
    for channel in range(8, 16):
      lora.add_channel(channel, frequency=915200000+channel*200000,
                       dr_min=2, dr_max=5)
    # Add the 500MHz uplink channels for TTN configuration
    lora.add_channel(65, frequency=917500000, dr_min=6, dr_max=6)

    First Problem:

    Here we discover that the Pycom implementation does not support adding a channel for the AU915 region, with a minimum data rate that is not DR_0 (0). To correct this, I have patched the firmware in lib/lora/mac/region/RegionAU915.c

    @@ -840,17 +840,17 @@ LoRaMacStatus_t RegionAU915ChannelManualAdd( ChannelAddParams_t* channelAdd )
         if( id >= AU915_MAX_NB_CHANNELS )
    -    // Validate the datarate range for min: must be DR_0
    -    if( channelAdd->NewChannel->DrRange.Fields.Min != DR_0 )
    +    // Validate the datarate range for min: must be <= max
    +    if( channelAdd->NewChannel->DrRange.Fields.Max > AU915_TX_MAX_DATARATE )
             drInvalid = true;
         // Validate the datarate range for max: must be <= TX_MAX_DATARATE
    -    if( channelAdd->NewChannel->DrRange.Fields.Max > AU915_TX_MAX_DATARATE )
    +    if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max )
             drInvalid = true;
         // Check frequency

    OK, so I have my channels set correctly. Now I need to join the network. I'm using OTAA not ABP. OTAA appears to be the default for TTN. It also means I don't need any device specific keys in my python code. So I can use the same code on all my devices. Also the join accept message confirms the downlink channels are working correctly.

    lora.join(activation=LoRa.OTAA, auth=(app_eui, app_key), timeout=0)

    Second Problem:

    At this point the Pycom device hangs.

    The LoRaWAN regional parameters says that:

    • If using the over-the-air activation procedure, the end-device SHALL broadcast the JoinReq message alternatively on a random 125 kHz channel amongst the 64 channels defined using DR2 and a random 500 kHz channel amongst the 8 channels defined using DR6.

    Looking into the firmware, esp32/mods/modlora.c:lora_join() uses a default data rate of DR_6 and has the following check on the data rate parameter provided

            case LORAMAC_REGION_AU915:
                if (dr != DR_0 && dr != DR_6) {
                    goto dr_error;

    This allows DR_0 as the only other data rate that can be used. OK so they got the allowed data rates wrong. That can be fixed with another small patch changing DR_0 to DR_2. However, technically the data rate should be dependent on the channel randomly selected and not specified by the user.

    So this is where things start to get more complicated.

    Digging a little further following the call/process flow, we find another function lib/lora/mac/region/RegionAU915.c:RegionAU915AlternateDr() which appears to alternate data rates between DR_6 and DR_0 for JOIN messages.(There's that DR_0 again!)
    Not only that, it also re-enables the deleted channels from 64 to 71. Why??

    But TTN plan does not support a channel with data rate DR_0. So what happens when the firmware switches to DR_0?
    In lib/lora/mac/LoRaMac.c:ScheduleTx() there is this bit of code:

        // Select channel
        while( RegionNextChannel( LoRaMacRegion, &nextChan, &Channel, &dutyCycleTimeOff, &AggregatedTimeOff ) == false )
            // Set the default datarate
            LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate;
            // Update datarate in the function parameters
            nextChan.Datarate = LoRaMacParams.ChannelsDatarate;

    So RegionNextChannel() returns false when it cannot find a channel that support the specified data rate. If this happen this loop forces the data rate to the default and repeats. And guess what the default data rate is for AU915? That's right, it's DR_0, which is not supported by TTN channel plan. As there is no escaping this loop, the code live-locks.

    Fortunately a small patch to RegionAU915AlternateDr() to use DR_2 instead of DR_0 and it all comes good. The endless loop above is however, is still a potential problem that ought to be fixed.

    My Pycom devices can now Join and communicate reliably.

    I must qualify, that this works fine with the LoRa gateway (MatchX 1702) that I set up, using the TTN AU global config available here. I recently tried a unit at home and wasn't getting the join accept response. Not sure why. Could be a problem with the gateway I was connecting to.

    I hope the information I have provided here may be useful to others in Australia.

  • I can't get a join.accept from an AS923 gateway in Australia either. My join requests appear on the TTN console but the join.accept never arrives. I'm wondering if the lopy4 (release='1.18.2.r1) receive frequencies are wrong for AS923 too? Anybody successfully connected a lopy4 to an AS923 gateway?

  • @achalmers68 I'm trying to retrace your steps for a US915 config. Where exactly are those .c files located that you patched?
    Update: I found them at

Log in to reply

Pycom on Twitter