Connecting RAK 4631 to TTN

I am trying to setup the device now on the TTN with the scheduler code. The problem is that for one when I copy the dev eui and node app into the ino file it looks like this
uint8_t nodeDeviceEUI[8] = {70B3D57ED0046933}; uint8_t nodeAppEUI[8] = {0x67, 0x81, 0xF4, 0x08, 0x69, 0xAA, 0xB4, 0xx0}; uint8_t nodeAppKey[16] = {4792D5B35CCF20D9C22BFE3BBD42C0AC};
Also App EUI is has that fill with 0’s button? I am confused on that, does it want me to pull the app EUI from the ino file and put it in there? On helium it generates all 3 keys in the proper format to copy and paste. I guess the format I need is MSB. What do I need to do?

Hello @a1projects Welcome to RAKwireless forum.
If you click on generate button then TTN will generate a new value. If you prefer to use a previously chosen value, you need to type the hex value in the field to the left of the generate button. To put the value in your sketch you need to include 0x and a comma to separate each hex byte. For example: uint8_t nodeDeviceEUI[8] = {70B3D57ED0046933} .Then you nodeDeviceEUI variable is:
uint8_t nodeDeviceEUI[8] = { 0x70,0xB3,0xD5,0x7E,0xD0,0x04,0x69,0x33 };

1 Like

Consider migrating to TTN v3. In this version there is a button “Toggle array formatting” that changes the format and already includes 0x and the comma.

1 Like

Ah ok I see that now! What about for the AppEUI, what do I put there? It has an all 0’s button but where do I get the number from

@a1projects For DevEUI you can read the QR code of your RAK. For AppEUI let TTN generate a new value.

It just auto fills it with 0s for AppEUI image

@a1projects I’ve already used all zero and had no problem. But in the fields where there are zeros you can put some hex value from 0 to FF

Does anything else in the code need to be changed? I am running it and this is all that shows up in my output Screen Shot 2021-10-12 at 10.03.08 PM

Here is my code minus my keys

   @file Environment_Monitoring.ino
   @brief This sketch demonstrate how to get environment data from BME680
      and send the data to lora gateway.
   @version 0.1
   @date 2020-07-28
   @copyright Copyright (c) 2020

#include <Arduino.h>
#include <LoRaWan-RAK4630.h> // Click to install library: http://librarymanager/ALL#SX126x-Arduino
#include <SPI.h>

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME680.h> // Click to install library: http://librarymanager/All#Adafruit_BME680
#include <U8g2lib.h>		   // Click to install library: http://librarymanager/ALL#u8g2

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);
Adafruit_BME680 bme;

// 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_3									  /*LoRaMac datarates definition, from DR_0 to DR_5*/
#define LORAWAN_TX_POWER TX_POWER_0								  /*LoRaMac tx power definition, from TX_POWER_0 to TX_POWER_15*/
#define JOINREQ_NBTRIALS 15										  /**< Number of trials for the join request. */
DeviceClass_t g_CurrentClass = CLASS_A;					/* class definition*/
LoRaMacRegion_t g_CurrentRegion = LORAMAC_REGION_US915;    /* Region:EU868*/
lmh_confirm g_CurrentConfirm = LMH_CONFIRMED_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);
void lorawan_join_fail(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_fail

//OTAA keys !!!! KEYS ARE MSB !!!!

// 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 60000											  /**< 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()
  // Initialize the built in LED
  digitalWrite(LED_BUILTIN, LOW);

  // Initialize Serial for debug output

  time_t serial_timeout = millis();
  // On nRF52840 the USB serial is not available immediately
  while (!Serial)
    if ((millis() - serial_timeout) < 5000)
      digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));

  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");
    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");

  // Initialize LoRa chip.

  /* bme680 init */


  //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);

  // Start Join procedure
  u8g2.clearBuffer();					// clear the internal memory
  u8g2.setFont(u8g2_font_ncenB10_tr); // choose a suitable font

  u8g2.drawStr(20, 39, "Joining ...");
  u8g2.sendBuffer(); // transfer internal memory to the display

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

/**@brief LoRa function for failed Join event
void lorawan_join_fail(void)
  Serial.println("OTAA join failed!");

/**@brief LoRa function for handling HasJoined event.
void lorawan_has_joined_handler(void)
  Serial.println("OTAA Mode, Network Joined!");
  u8g2.clearBuffer();					// clear the internal memory
  u8g2.setFont(u8g2_font_ncenB10_tr); // choose a suitable font

  u8g2.drawStr(20, 39, "Joined");
  u8g2.sendBuffer(); // transfer internal memory to the display

  lmh_error_status ret = lmh_class_request(g_CurrentClass);
  if (ret == LMH_SUCCESS)
    TimerSetValue(&appTimer, LORAWAN_APP_INTERVAL);

/**@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
  if (!bme.performReading()) {

  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;

void init_bme680(void)

  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME680 sensor, check wiring!");

  // Set up oversampling and filter initialization
  bme.setGasHeater(320, 150); // 320*C for 150 ms

String data = "";

void bme680_get()
  char oled_data[32] = {0};
  Serial.print("result: ");
  uint32_t i = 0;
  memset(m_lora_app_data.buffer, 0, LORAWAN_APP_DATA_BUFF_SIZE);
  m_lora_app_data.port = gAppPort;

  double temp = bme.temperature;
  double pres = bme.pressure / 100.0;
  double hum = bme.humidity;
  uint32_t gas = bme.gas_resistance;

  data = "Tem:" + String(temp) + "C " + "Hum:" + String(hum) + "% " + "Pres:" + String(pres) + "KPa " + "Gas:" + String(gas) + "Ohms";

  // display bme680 sensor data on OLED
  u8g2.clearBuffer();					// clear the internal memory
  u8g2.setFont(u8g2_font_ncenB10_tr); // choose a suitable font

  memset(oled_data, 0, sizeof(oled_data));
  sprintf(oled_data, "T=%.2fC", temp);
  u8g2.drawStr(3, 15, oled_data);

  memset(oled_data, 0, sizeof(oled_data));
  snprintf(oled_data, 64, "RH=%.2f%%", hum);
  u8g2.drawStr(3, 30, oled_data);

  memset(oled_data, 0, sizeof(oled_data));
  sprintf(oled_data, "P=%.2fhPa", pres);
  u8g2.drawStr(3, 45, oled_data);

  memset(oled_data, 0, sizeof(oled_data));
  sprintf(oled_data, "G=%dOhms", gas);
  u8g2.drawStr(3, 60, oled_data);

  u8g2.sendBuffer(); // transfer internal memory to the display

  uint16_t t = temp * 100;
  uint16_t h = hum * 100;
  uint32_t pre = pres * 100;

  //result: T=28.25C, RH=50.00%, P=958.57hPa, G=100406 Ohms
  m_lora_app_data.buffer[i++] = 0x01;
  m_lora_app_data.buffer[i++] = (uint8_t)(t >> 8);
  m_lora_app_data.buffer[i++] = (uint8_t)t;
  m_lora_app_data.buffer[i++] = (uint8_t)(h >> 8);
  m_lora_app_data.buffer[i++] = (uint8_t)h;
  m_lora_app_data.buffer[i++] = (uint8_t)((pre & 0xFF000000) >> 24);
  m_lora_app_data.buffer[i++] = (uint8_t)((pre & 0x00FF0000) >> 16);
  m_lora_app_data.buffer[i++] = (uint8_t)((pre & 0x0000FF00) >> 8);
  m_lora_app_data.buffer[i++] = (uint8_t)(pre & 0x000000FF);
  m_lora_app_data.buffer[i++] = (uint8_t)((gas & 0xFF000000) >> 24);
  m_lora_app_data.buffer[i++] = (uint8_t)((gas & 0x00FF0000) >> 16);
  m_lora_app_data.buffer[i++] = (uint8_t)((gas & 0x0000FF00) >> 8);
  m_lora_app_data.buffer[i++] = (uint8_t)(gas & 0x000000FF);
  m_lora_app_data.buffsize = i;

No need to change the code. Here is the output of example code as it is given with the BSP:

After a while it should say either
OTAA join failed!
OTAA Mode, Network Joined!

Here is the log in case the connection to the network fails:

11:32:54.635 -> =====================================
11:32:54.635 -> Welcome to RAK4630 LoRaWan!!!
11:32:54.635 -> Type: OTAA
11:32:54.635 -> Region: EU868
11:32:54.635 -> =====================================
11:33:41.418 -> OTAA join failed!

As you can see, it takes around 1 minute until the failed message appears.
And here is a successful join:

11:37:42.676 -> =====================================
11:37:42.676 -> Welcome to RAK4630 LoRaWan!!!
11:37:42.676 -> Type: OTAA
11:37:42.676 -> =====================================
11:37:48.969 -> OTAA Mode, Network Joined!
11:38:09.971 -> Sending frame now...
11:38:10.322 -> result: Tem:30.00C Hum:50.46% Pres:1003.76KPa Gas:0Ohms
11:38:10.370 -> lmh_send ok count 1
11:38:29.953 -> Sending frame now...
11:38:30.323 -> result: Tem:30.00C Hum:50.45% Pres:1003.78KPa Gas:1359Ohms
11:38:30.367 -> lmh_send ok count 2

It takes only 6 seconds to join and then it starts sending the packets.

In TTN, can you see if your device is connecting and the join is accepted?

@a1projects , also the very basic thing to check is if your gateway is online in TTN V3 already?

If I were you, if you just want to test the LoRaWAN device, just use the built-in Chirpstak Network Server. It is local and wont require any internet connection :+1:

The gateway is online. Ok but I need to see if the wind sensor is working. The device is offline though, it never connected. Maybe I am confused on how to setup the keys/EUI’s

Is it ok to have all 00s for the app EUI or is that the problem?

@a1projects TTN v3 for US region

I waited over a minute it’s not saying joined or failed…

The code is looking for a RAK1906 module, but it is not there. It assumes an error and does not continue.

Do you have RAK1906 installed? If not, look for something like init_bme680() and remove it.

Yes the environment sensor is there, After re adjusting that error went away but now it just doesn’t say anything

Tried switching back to helium (which it does connect there sometimes and sometimes it looks like this) and it’s doing the same thing, no join and no failed. The light starts blinking green and stays just red after

Just to confirm this does mean that my gateway is connected correctly right?

Yes @a1projects the gateway is online.

When I have a device that can’t connect to the LoRaWAN Network Server, I try to upload a basic LoRaWAN End Device sketch first. Can you try if the basic example will work?

If is still doesn’t work, can you send me the specific sketch so I can have a look?

This is the basic sketch I am referring to:

Update, I figured out that using Mac OS was actually bricking the devices to connect when programming through it for both TTN and Helium. You guys might want to look into that!! So I just ran a test run for Helium and it connects but all the packets fail after 2 successful ones

shtc3 init
Beginning sensor. Result = =====================================
Welcome to RAK4630 LoRaWan!!!
Type: OTAA
Region: US915
OTAA Mode, Network Joined!
Sending frame now...
result: Tem:27.31C Hum:57.15% Pres:1000.49KPa Gas:4816Ohms
lmh_send ok count 1
Sending frame now...
result: Tem:27.08C Hum:57.27% Pres:1000.50KPa Gas:5332Ohms
lmh_send fail count 1
Sending frame now...
result: Tem:26.91C Hum:57.20% Pres:1000.50KPa Gas:5865Ohms
lmh_send fail count 2

I am about to give TTN a whirl