RAK4631 doesn't connect on AU915 network from Helium

Issue :
Hi, I’m struggling with connection using OTAA example code on Rak4631 module on a longfi network from Helium on region AU915 (Brazil) but no matter what I do it keeps returning OTAA Join Failed after the set number of tryies.

Setup :
AU915 sub band 2 (already tried sub band 5, 4 and 8 as hotspots nearby where transmitting their beacon on those bands accordingly to the helium explorer webapp) without success.

Docs :
Using code from: longfi-platformio/main.cpp at master · helium/longfi-platformio · GitHub

Details :
Here is what aways happens

14:35:55.844 > <BRD> SyncWord = 2414
14:35:55.844 > <BRD> LoRa Task started
14:35:55.844 > =====================================
14:35:55.844 > Welcome to RAK4630 LoRaWan!!!
14:35:55.844 > Type: OTAA
14:35:55.844 > Region: AU915
14:35:55.844 > =====================================
14:35:55.845 > <LMH> OTAA 
14:35:55.845 > DevEui=60-81-F9-E1-A5-E8-C8-1B
14:35:55.845 > DevAdd=00000000
14:35:55.845 > AppEui=60-81-F9-5E-95-44-90-82
14:35:55.845 > AppKey=XX-XX-XX (here I have defined based on Helium Console keys as MSB)
14:35:55.918 > <LMH> Selected subband 2
14:35:56.918 > <LMH> Selected subband 2
14:35:57.918 > Starting Join
14:35:57.940 > Return from join
14:35:58.311 > <LM> OnRadioTxDone
14:35:58.313 > <LM> OnRadioTxDone => RX Windows #1 4991 #2 6002
14:35:58.313 > <LM> OnRadioTxDone => TX was Join Request
14:36:03.311 > <RADIO> RX window timeout = 3000
14:36:07.274 > <RADIO> RadioIrqProcess => IRQ_RX_TX_TIMEOUT
14:36:07.274 > <LM> OnRadioRxTimeout
14:36:07.436 > <LM> Join network failed 1 time(s)
14:36:07.436 > 
14:36:35.435 > <LM> OnRadioTxDone
14:36:35.436 > <LM> OnRadioTxDone => RX Windows #1 4991 #2 6002
14:36:35.437 > <LM> OnRadioTxDone => TX was Join Request
14:36:40.435 > <RADIO> RX window timeout = 3000
14:36:44.395 > <RADIO> RadioIrqProcess => IRQ_RX_TX_TIMEOUT
14:36:44.395 > <LM> OnRadioRxTimeout
14:36:44.560 > <LM> Join network failed 2 time(s)
14:36:44.560 > 
14:37:12.558 > <LM> OnRadioTxDone
14:37:12.559 > <LM> OnRadioTxDone => RX Windows #1 4991 #2 6002
14:37:12.560 > <LM> OnRadioTxDone => TX was Join Request
14:37:17.558 > <RADIO> RX window timeout = 3000
14:37:21.519 > <RADIO> RadioIrqProcess => IRQ_RX_TX_TIMEOUT
14:37:21.519 > <LM> OnRadioRxTimeout
14:37:21.683 > <LM> Join network failed 3 time(s)
14:37:21.683 > 
14:37:21.683 > <LM> LoRaMacState = idle
14:37:21.683 > OTAA join failed!
14:37:21.683 > Check your EUI's and Keys's!
14:37:21.683 > Check if a Gateway is in range!

Code :

 * file LoRaWAN_OTAA_ABP.ino
 * author rakwireless.com
 * brief LoRaWan node example with OTAA/ABP registration
 * version 0.1
 * date 2020-08-21
 * copyright Copyright (c) 2020
 * note RAK4631 GPIO mapping to nRF52840 GPIO ports
   RAK4631    <->  nRF52840
   WB_IO1     <->  P0.17 (GPIO 17)
   WB_IO2     <->  P1.02 (GPIO 34)
   WB_IO3     <->  P0.21 (GPIO 21)
   WB_IO4     <->  P0.04 (GPIO 4)
   WB_IO5     <->  P0.09 (GPIO 9)
   WB_IO6     <->  P0.10 (GPIO 10)
   WB_SW1     <->  P0.01 (GPIO 1)
   WB_A0      <->  P0.04/AIN2 (AnalogIn A2)
   WB_A1      <->  P0.31/AIN7 (AnalogIn A7)
#include <Arduino.h>
#include <LoRaWan-RAK4630.h>
#include <SPI.h>

// RAK4630 supply two LED
#define LED_BUILTIN 35

#ifndef LED_BUILTIN2
#define LED_BUILTIN2 36

bool doOTAA = true;   // OTAA is used by default.
#define SCHED_MAX_EVENT_DATA_SIZE APP_TIMER_SCHED_EVENT_DATA_SIZE /**< Maximum size of scheduler events. */
#define SCHED_QUEUE_SIZE 60										  /**< Maximum number of events in the scheduler queue. */
#define LORAWAN_DATERATE DR_0									  /*LoRaMac datarates definition, from DR_0 to DR_5*/
#define LORAWAN_TX_POWER TX_POWER_5							/*LoRaMac tx power definition, from TX_POWER_0 to TX_POWER_15*/
#define JOINREQ_NBTRIALS 3										  /**< Number of trials for the join request. */
DeviceClass_t g_CurrentClass = CLASS_A;					/* class definition*/
LoRaMacRegion_t g_CurrentRegion = LORAMAC_REGION_AU915;    /* Region:EU868*/
lmh_confirm g_CurrentConfirm = LMH_UNCONFIRMED_MSG;				  /* confirm/unconfirm packet definition*/
uint8_t gAppPort = LORAWAN_APP_PORT;							        /* data port*/

/**brief Structure containing LoRaWan parameters, needed for lmh_init()

// Foward declaration
static void lorawan_has_joined_handler(void);
static void lorawan_join_failed_handler(void);
static void lorawan_rx_handler(lmh_app_data_t *app_data);
static void lorawan_confirm_class_handler(DeviceClass_t Class);
static void send_lora_frame(void);

/**brief Structure containing LoRaWan callback functions, needed for lmh_init()
static lmh_callback_t g_lora_callbacks = {BoardGetBatteryLevel, BoardGetUniqueId, BoardGetRandomSeed,
                                        lorawan_rx_handler, lorawan_has_joined_handler, lorawan_confirm_class_handler, lorawan_join_failed_handler
//OTAA keys !!!! KEYS ARE MSB !!!!
uint8_t nodeDeviceEUI[8] = {0x60, 0x81, 0xF9, 0xE1, 0xA5, 0xE8, 0xC8, 0x1B};
uint8_t nodeAppEUI[8] = {0x60, 0x81, 0xF9, 0x5E, 0x95, 0x44, 0x90, 0x82};
uint8_t nodeAppKey[16] = {0x12, 0xAC, 0x1F, 0xA5, 0x42, 0xF1, 0x85, 0x3A, 0x99, 0xB7, 0xEB, 0xD5, 0xEE, 0xB4, 0x7A, 0xC8};

// ABP keys
uint32_t nodeDevAddr = 0x260116F8;
uint8_t nodeNwsKey[16] = {0x7E, 0xAC, 0xE2, 0x55, 0xB8, 0xA5, 0xE2, 0x69, 0x91, 0x51, 0x96, 0x06, 0x47, 0x56, 0x9D, 0x23};
uint8_t nodeAppsKey[16] = {0xFB, 0xAC, 0xB6, 0x47, 0xF3, 0x58, 0x45, 0xC7, 0x50, 0x7D, 0xBF, 0x16, 0x8B, 0xA8, 0xC1, 0x7C};

// Private defination
#define LORAWAN_APP_DATA_BUFF_SIZE 64                     /**< buffer size of the data to be transmitted. */
#define LORAWAN_APP_INTERVAL 20000                        /**< Defines for user timer, the application data transmission interval. 20s, value in [ms]. */
static uint8_t m_lora_app_data_buffer[LORAWAN_APP_DATA_BUFF_SIZE];            //< Lora user application data buffer.
static lmh_app_data_t m_lora_app_data = {m_lora_app_data_buffer, 0, 0, 0, 0}; //< Lora user application data structure.

TimerEvent_t appTimer;
static uint32_t timers_init(void);
static uint32_t count = 0;
static uint32_t count_fail = 0;

void setup()
  digitalWrite(LED_BUILTIN, LOW);

  // Initialize Serial for debug output
  time_t timeout = millis();
  while (!Serial)
    if ((millis() - timeout) < 5000)

  // Initialize LoRa chip.

  Serial.println("Welcome to RAK4630 LoRaWan!!!");
  if (doOTAA)
    Serial.println("Type: OTAA");
    Serial.println("Type: ABP");

  switch (g_CurrentRegion)
    case LORAMAC_REGION_AS923:
      Serial.println("Region: AS923");
    case LORAMAC_REGION_AU915:
      Serial.println("Region: AU915");
    case LORAMAC_REGION_CN470:
      Serial.println("Region: CN470");
    Serial.println("Region: CN779");
    case LORAMAC_REGION_EU433:
      Serial.println("Region: EU433");
    case LORAMAC_REGION_IN865:
      Serial.println("Region: IN865");
    case LORAMAC_REGION_EU868:
      Serial.println("Region: EU868");
    case LORAMAC_REGION_KR920:
      Serial.println("Region: KR920");
    case LORAMAC_REGION_US915:
      Serial.println("Region: US915");
    Serial.println("Region: RU864");
  case LORAMAC_REGION_AS923_2:
    Serial.println("Region: AS923-2");
  case LORAMAC_REGION_AS923_3:
    Serial.println("Region: AS923-3");
  case LORAMAC_REGION_AS923_4:
    Serial.println("Region: AS923-4");
  //creat a user timer to send data to server period
  uint32_t err_code;
  err_code = timers_init();
  if (err_code != 0)
    Serial.printf("timers_init failed - %d\n", err_code);

  // Setup the EUIs and Keys
  if (doOTAA)

  // Initialize LoRaWan
  err_code = lmh_init(&g_lora_callbacks, g_lora_param_init, doOTAA, g_CurrentClass, g_CurrentRegion);
  if (err_code != 0)
    Serial.printf("lmh_init failed - %d\n", err_code);

  /* Helium Network LoraWan sub channel 2 is recommended, default channels 0-7*/
  if (!lmh_setSubBandChannels(2))
      Serial.println("lmh_setSubBandChannels() failed");


	// Start Join procedure
	Serial.println("Starting Join");
	Serial.println("Return from join");

void loop()
  // Put your application tasks here, like reading of sensors,
  // Controlling actuators and/or other functions. 

/**brief LoRa function for handling HasJoined event.
void lorawan_has_joined_handler(void)
  if(doOTAA == true)
  Serial.println("OTAA Mode, Network Joined!");
    Serial.println("ABP Mode");

  lmh_error_status ret = lmh_class_request(g_CurrentClass);
  if (ret == LMH_SUCCESS)
    TimerSetValue(&appTimer, LORAWAN_APP_INTERVAL);
/**brief LoRa function for handling OTAA join failed
static void lorawan_join_failed_handler(void)
  Serial.println("OTAA join failed!");
  Serial.println("Check your EUI's and Keys's!");
  Serial.println("Check if a Gateway is in range!");
/**brief Function for handling LoRaWan received data from Gateway
 * param[in] app_data  Pointer to rx data
void lorawan_rx_handler(lmh_app_data_t *app_data)
  Serial.printf("LoRa Packet received on port %d, size:%d, rssi:%d, snr:%d, data:%s\n",
          app_data->port, app_data->buffsize, app_data->rssi, app_data->snr, app_data->buffer);

void lorawan_confirm_class_handler(DeviceClass_t Class)
  Serial.printf("switch to class %c done\n", "ABC"[Class]);
  // Informs the server that switch has occurred ASAP
  m_lora_app_data.buffsize = 0;
  m_lora_app_data.port = gAppPort;
  lmh_send(&m_lora_app_data, g_CurrentConfirm);

void send_lora_frame(void)
  if (lmh_join_status_get() != LMH_SET)
    //Not joined, try again later

  uint32_t i = 0;
  memset(m_lora_app_data.buffer, 0, LORAWAN_APP_DATA_BUFF_SIZE);
  m_lora_app_data.port = gAppPort;
  m_lora_app_data.buffer[i++] = 'H';
  m_lora_app_data.buffer[i++] = 'e';
  m_lora_app_data.buffer[i++] = 'l';
  m_lora_app_data.buffer[i++] = 'l';
  m_lora_app_data.buffer[i++] = 'o';
  m_lora_app_data.buffer[i++] = '!';
  m_lora_app_data.buffsize = i;

  lmh_error_status error = lmh_send(&m_lora_app_data, g_CurrentConfirm);
  if (error == LMH_SUCCESS)
    Serial.printf("lmh_send ok count %d\n", count);
    Serial.printf("lmh_send fail count %d\n", count_fail);

/**brief Function for handling user timerout event.
void tx_lora_periodic_handler(void)
  TimerSetValue(&appTimer, LORAWAN_APP_INTERVAL);
  Serial.println("Sending frame now...");

/**brief Function for the Timer initialization.
 * details Initializes the timer module. This creates and starts application timers.
uint32_t timers_init(void)
  TimerInit(&appTimer, tx_lora_periodic_handler);
  return 0;

Platformio configuration:

; PlatformIO Project Configuration File


; Build options: build flags, source filter

; Upload options: custom upload port, speed and extra flags

; Library options: dependencies, extra library storages

; Advanced options: extra scripting



default_envs = wiscore_rak4631_v2


build_flags =




platform = nordicnrf52

board = wiscore_rak4631

framework = arduino

lib_deps = beegee-tokyo/SX126x-Arduino@^2.0.11

Am I forgetting to configure something? I have already tried to enable duty cycle wondering if 3000ms is a RX window too short (as I saw in other topics it may cause a delay on RxTimeout of 65s but independently of LORAWAN_DUTYCYCLE_ON or LORAWAN_DUTYCYCLE_OFF the timeout is the same on region AU915. Thanks in advance for any help.

Welcome to the forum @nilmonto

Unfortunately the example code in the Helium documents is very old and does not work anymore.

Please try our own example code for the RAK4631 => LoRaWAN_OTAA_ABP

All your settings are correct (including subband 2), so you can transfer them to our example code.

It worked!! Thanks, but I’m curious about something: my device only communicated after my own hotspot (a bobcat300) was properly online even though there are plenty other hotspots nearby supposedly on the same frequency once Helium force them to be on the right region profile and here in Brazil is AU915MHz. Is that possible to them to be faking their radio signals in order to earn cryptocurrency and not really being here in my city? Just wondering how could a little device such a RAK4631 communicate only through my hotspot (the package that is sent to my mqtt server comes with the information about which hotspot routed the message through along with network parameters such as rssi, snr, frequency etc) using a printed antenna and other hotspots with better antennas could not :thinking:

I am in a similar situation, there are several hotspots around my place but my devices connect only to my own hotspot. And I was doing tests with a Helium Mapper and the results are very disappointing. My region is AS923-3. The device is sending its location package every 30 seconds.

Only where you see black dots, the device (RAK4631 as well with a 2dBi antenna) could send packets to the console

However there are multiple hotspots around that should have been able to receive the packets. (my route is in red on the map

I think there are fake miners around (setup with the wrong position) to get better mining results.

That’s very annoying indeed :roll_eyes: hope this could be prevented or at least had some tool to put these devices under investigation once they put all coverage - the main purpose of this network - at risk.

After some research I’ve found that this odious practice is also called spoofing position and by now there are no effective resource on the algorithm to prevent or banish these devices from network, only some hard to understand reduce on transmit scale when there are wrong signal strength related to the hotspots distance when witnessing or responding to a challenge beacon.

Not sure if I understood right but when this happens the reduced scale hotspot may ‘contaminate’ their neighbors with this reduction and lower the earns of the nearby - and probably also spoofed positioned - hotspots (still reading docs to better understand).

Anyway, hope when the earns on the network be more for data transfer this practice be discourage - maybe we can even see a drop on the cost of hotspots once the spoofers will not have where to place their devices and the earns for PoC will be too low to keep they running.

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.