Is RFM9x LoRa Radio Module considered as LoRaWAN end-node?

Hello. I made the RFM96 LoRa Radio Module by adafruit communicate to each other. The next step I want to do is connect to it to a gateway, which is either the RAK7371 Developer Base or RAK2287 Concentrator Module. After connecting these gateways to The Things Network (TTN), I noticed that it doesn't get or reflect the data from the RFM96 module in the gateway. I didn't see the data I sent using the module.

After doing researches, I may have found out what might be the reason -- RFM96 LoRa Radio Module is not considered as LoRaWAN end-node as needed by the gateways.

I would like to confirm to you and hoping that someone can shed light on this matter.

Thank you!

I have used an couple of Adafruit Feather 32u4 lora boards (which use the RFM96) to connect to LoraWAN V3 without problems.
I assume you have managed to register your gateway and Lora device with the Things Network V3
if you run an OTTA program on the LoRa Radio Module you should see traffic on the gateway and the device web pages

Hi

yes , RFM9x are 'agnostic' transceiver modules , they can deal with any kind of LoRa frames (p2p, LoRaWAN...)
Many users build LoRaWAN their nodes swith these modules.
Ok for TTN V3 with a good node's configuration on node's side and server's side.

it doesn't get or reflect the data from the RFM96 module in the gateway

Standard gateways ignore datas content , (postman does'nt open letters....) they just 'relay' frames from nodes to TTN server and, when necessary, from from TTN server to nodes.
My Gateway (TTN one) shows for each frame seen : DEvAddress or DEvEUI, SF, BW, RSSI....

Hello. Thank you! Appreciated your insights @horace I already connected my RFM96 transceiver module to the TTN. I also see that it was accepted by my gateway. However, I can't see the data I sent using the module.

I followed the tutorial I saw in the internet by a youtuber named Mobilefish.com while he discussed and made a series about Lora/LoraWAN.

However upon loading the code the arduino, I saw this in the serial comm.

I edited the code based on what he discussed, by any chance, could give me some insights about this? Thank you so much!

Thank you so much for your insight! Appreciated it @al1fch

I already registered my end node to the TTN. However, I didn't see the data from the RFM96 module. I followed this video from mobilefisher.com found in this link LoRa/LoRaWAN Tutorial. I edited the code based on the specification of my registered RFM96. By any chance, do you have any ideas I could take to address this? Thank you so much!

No time here to spend watching a probably obsolete vidéo .....

Does this video deals witn TTS (TTN version#3) or TTN V2 ?

already registered my end node to the TTN...

is your Gateway registered in TTS backend ? (TTN V3) ?
If so do you see some activity listed in 'Live data' ?
if so is there some activity from your device's address ?

However upon loading the code the arduino, I saw this in the serial comm.
Failure shown for frame sending

put here a full output with text copy and paste, not a picture

is your Gateway registered in TTS backend ? (TTN V3) ?
If so do you see some activity listed in 'Live data' ?
if so is there some activity from your device's address ?

Yes Sir. I already registered my gateway to the TTN. Here's a screenshot of my gateway and end node with live data.

Here's what I always get after loading my codes.
Starting
Packet queued
1884: EV_JOINING
2162: EV_TXSTART
FAILURE
C:\Users\AdrianAristotlePDell\Documents\Arduino\libraries\MCCI_LoRaWAN_LMIC_library\src\lmic\radio.c:1065

As per hardware connection:
RFM96 Arduino Uno
Vin 5V
GND GND
CLK Pin 13
MISO Pin 12
MOSI Pin 11
CS Pin 10
RST Pin 5
G0 (data interrupt) Pin 2

Here's the my modified code:

#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>

//
// For normal use, we require that you edit the sketch to replace FILLMEIN
// with values assigned by the TTN console. However, for regression tests,
// we want to be able to compile these scripts. The regression tests define
// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
// working but innocuous value.
//
#ifdef COMPILE_REGRESSION_TEST
# define FILLMEIN 0
#else
# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"
# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)
#endif

// This EUI must be in little-endian format, so least-significant-byte
// first. When copying an EUI from ttnctl output, this means to reverse
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
// 0x70.
static const u1_t PROGMEM APPEUI[8]={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}

// This should also be in little endian format, see above.
static const u1_t PROGMEM DEVEUI[8]={0xA9, 0x1B, 0x05, 0xD0, 0x7E, 0xD5, 0xB3, 0x70};
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}

// This key should be in big endian format (or, since it is not really a
// number but a block of memory, endianness does not really apply). In
// practice, a key taken from ttnctl can be copied as-is.
static const u1_t PROGMEM APPKEY[16] = {0x7A, 0xF2, 0xD7, 0x27, 0x1B, 0x99, 0x36, 0x43, 0xBE, 0x3A, 0xFB, 0x84, 0x38, 0x17, 0x8B, 0x46};
void os_getDevKey (u1_t* buf) {  memcpy_P(buf, APPKEY, 16);}

static uint8_t mydata[] = "Hello, world!";
static osjob_t sendjob;

// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 60;

// Pin mapping
const lmic_pinmap lmic_pins = {
    .nss = 10,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 5,
    .dio = {2, 3, LMIC_UNUSED_PIN},
};

void printHex2(unsigned v) {
    v &= 0xff;
    if (v < 16)
        Serial.print('0');
    Serial.print(v, HEX);
}

void onEvent (ev_t ev) {
    Serial.print(os_getTime());
    Serial.print(": ");
    switch(ev) {
        case EV_SCAN_TIMEOUT:
            Serial.println(F("EV_SCAN_TIMEOUT"));
            break;
        case EV_BEACON_FOUND:
            Serial.println(F("EV_BEACON_FOUND"));
            break;
        case EV_BEACON_MISSED:
            Serial.println(F("EV_BEACON_MISSED"));
            break;
        case EV_BEACON_TRACKED:
            Serial.println(F("EV_BEACON_TRACKED"));
            break;
        case EV_JOINING:
            Serial.println(F("EV_JOINING"));
            break;
        case EV_JOINED:
            Serial.println(F("EV_JOINED"));
            {
              u4_t netid = 0;
              devaddr_t devaddr = 0;
              u1_t nwkKey[16];
              u1_t artKey[16];
              LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
              Serial.print("netid: ");
              Serial.println(netid, DEC);
              Serial.print("devaddr: ");
              Serial.println(devaddr, HEX);
              Serial.print("AppSKey: ");
              for (size_t i=0; i<sizeof(artKey); ++i) {
                if (i != 0)
                  Serial.print("-");
                printHex2(artKey[i]);
              }
              Serial.println("");
              Serial.print("NwkSKey: ");
              for (size_t i=0; i<sizeof(nwkKey); ++i) {
                      if (i != 0)
                              Serial.print("-");
                      printHex2(nwkKey[i]);
              }
              Serial.println();
            }
            // Disable link check validation (automatically enabled
            // during join, but because slow data rates change max TX
	    // size, we don't use it in this example.
            LMIC_setLinkCheckMode(0);
            break;
        /*
        || This event is defined but not used in the code. No
        || point in wasting codespace on it.
        ||
        || case EV_RFU1:
        ||     Serial.println(F("EV_RFU1"));
        ||     break;
        */
        case EV_JOIN_FAILED:
            Serial.println(F("EV_JOIN_FAILED"));
            break;
        case EV_REJOIN_FAILED:
            Serial.println(F("EV_REJOIN_FAILED"));
            break;
        case EV_TXCOMPLETE:
            Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
            if (LMIC.txrxFlags & TXRX_ACK)
              Serial.println(F("Received ack"));
            if (LMIC.dataLen) {
              Serial.print(F("Received "));
              Serial.print(LMIC.dataLen);
              Serial.println(F(" bytes of payload"));
            }
            // Schedule next transmission
            os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
            break;
        case EV_LOST_TSYNC:
            Serial.println(F("EV_LOST_TSYNC"));
            break;
        case EV_RESET:
            Serial.println(F("EV_RESET"));
            break;
        case EV_RXCOMPLETE:
            // data received in ping slot
            Serial.println(F("EV_RXCOMPLETE"));
            break;
        case EV_LINK_DEAD:
            Serial.println(F("EV_LINK_DEAD"));
            break;
        case EV_LINK_ALIVE:
            Serial.println(F("EV_LINK_ALIVE"));
            break;
        /*
        || This event is defined but not used in the code. No
        || point in wasting codespace on it.
        ||
        || case EV_SCAN_FOUND:
        ||    Serial.println(F("EV_SCAN_FOUND"));
        ||    break;
        */
        case EV_TXSTART:
            Serial.println(F("EV_TXSTART"));
            break;
        case EV_TXCANCELED:
            Serial.println(F("EV_TXCANCELED"));
            break;
        case EV_RXSTART:
            /* do not print anything -- it wrecks timing */
            break;
        case EV_JOIN_TXCOMPLETE:
            Serial.println(F("EV_JOIN_TXCOMPLETE: no JoinAccept"));
            break;

        default:
            Serial.print(F("Unknown event: "));
            Serial.println((unsigned) ev);
            break;
    }
}

void do_send(osjob_t* j){
    // Check if there is not a current TX/RX job running
    if (LMIC.opmode & OP_TXRXPEND) {
        Serial.println(F("OP_TXRXPEND, not sending"));
    } else {
        // Prepare upstream data transmission at the next possible time.
        LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
        Serial.println(F("Packet queued"));
    }
    // Next TX is scheduled after TX_COMPLETE event.
}

void setup() {
    Serial.begin(9600);
    Serial.println(F("Starting"));

    #ifdef VCC_ENABLE
    // For Pinoccio Scout boards
    pinMode(VCC_ENABLE, OUTPUT);
    digitalWrite(VCC_ENABLE, HIGH);
    delay(1000);
    #endif

    // LMIC init
    os_init();
    // Reset the MAC state. Session and pending data transfers will be discarded.
    LMIC_reset();

    // Start job (sending automatically starts OTAA too)
    do_send(&sendjob);
}

void loop() {
    os_runloop_once();
}

Thank you so much for your assistance.

after transmitting the OTTA join request you appear to be transmitting before you have joined the network (the network indicates a join accept but I assume you transmitted before it arrived at the device)
it should something look like

Starting
CFG_eu868
Packet queued
0
664566: EV_JOINING
1378101: EV_JOINED
1698830: EV_TXCOMPLETE (includes waiting for RX windows)
Received 
3 bytes of payload
Data Received:  0xFFFFFF99 0xFFFFFF88 0x77

cannot see any problems with the code

are you sure you have the pin mapping correct? which patricular Adafruit RFM96 LoRa Radio Module are you using?

Edit: your code will not even even compile with the LMIC library I have
have you tried File>Examples>IBM LMIC Framework > ttn-otaa

after transmitting the OTTA join request you appear to be transmitting before you have joined the network (the network indicates a join accept but I assume you transmitted before it arrived at the device)

same feeling, downlink configuration problem ?

C:\Users\AdrianAristotlePDell\Documents\Arduino\libraries\MCCI_LoRaWAN_LMIC_library\src\lmic\radio.c:1065

look for 1065 line of radio.c file in your LMIC installed library (deveral verifons around...)

Ohh I see. Thank you @horace

I believe that what really happened. I opened the serial monitor 1st then I reloaded my end node application in TTN. May I humbly ask what the sequence should be? Or the steps to take after modifying the code?

Also, this never happens to me, it's always FAILED after EV_JOINED

Starting
CFG_eu868
Packet queued
0
664566: EV_JOINING
1378101: EV_JOINED
1698830: EV_TXCOMPLETE (includes waiting for RX windows)
Received
3 bytes of payload
Data Received: 0xFFFFFF99 0xFFFFFF88 0x77

Edit: your code will not even even compile with the LMIC library I have
have you tried File>Examples>IBM LMIC Framework > ttn-otaa

Yes, this is basically what I've used. I just modified the name.

Thank you @al1fch

When I looked at the line 1065, this is it:
ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP );
This is the whole code where it actually located.

static void startrx (u1_t rxmode) {
    ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP );
    if(getSf(LMIC.rps) == FSK) { // FSK modem
        rxfsk(rxmode);
    } else { // LoRa modem
        rxlora(rxmode);
    }

I don't know what's wrong with it. Apologies I'm just a newbie here huhuhu.

as I mentioned in post #8 your code would not compile on my version of lmic.h which was from IBM_LMIC_framework - it failed at statement

              LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);

with error: 'LMIC_getSessionKeys' was not declared in this scope

I deleted IBM_LMIC_framework and installed MCCI_LoRaWAN_LMIC_library - your code now compiled and downloaded into the Adafruit Feather 32u4 but corrupted something - after pressing RESET the COM port disappeared after about 8 seconds - in the end I managed to download blink and it woked
reinstalled IBM_LMIC_framework and now that won't join - no idea what is going on

edit: what particular Adafruit lora board are you using and what frequenmcy?

managed to sort out problem with programming the Feather 32u4 was something to do with incorrec t COM port drivers. Uninstalled all COM ports including hidden and the 32u4 then programed OK with new COM port assignments.
The recommended MCCI_LoRaWAN_LMIC_library library is now working on the 32u4 Lora - followed region-configuration to setup CFG_eu868 region and then arduino-code guide
as with the IBM_LMIC_framework library it still requires a call to LMIC_setClockError()

    LMIC_reset();
    LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100);  // **** does not work without this

a run displays join, uplink and downlink

MCCI_LoRaWAN_LMIC Starting
Packet queued
362778: EV_JOINING
525775: EV_TXSTART
848683: EV_JOINED
netid: 19
devaddr: xxxxx
AppSKey: xxxxxxx
NwkSKey: xxxxxxxx
850123: EV_TXSTART
1171689: EV_TXCOMPLETE (includes waiting for RX windows)
Received 8 bytes of payload
 0x1 0x2 0x3 0x4 0x5 0x6 0x67 0x77

Hi @horace I'm using Adafruit's 915MHz RFM96 LoRa Radio Module (see attached pic)
image

And for reference po I got my code from MCCI-Catena Github repo and used the ttn-otaa code. Is this what you used also when you tried it?

Thank you for your assistance.

the MCCI LoRaWAN LMIC Library is the one I used although I loaded it using the library manager, e.g.
LMIC_library

I have a number of Lora boards but not the Adafruit RFM96 - however, the MCCI LoRaWAN LMIC Library should work with RFM96 lora boards

as I am in the EU868 frequency region I had to modify file lmic_project_config.h - you are in the 915MHz region so should need no modification

the LoraWAN code I used was File>Examples>MCCI LoRaWAN LMIC Library>ttn-otta

Then using values from The Things V3 LoraWAN server set up the APPKEY, DEVEUI and AAPEUI uploaded the code and it connected OK as described in post #13
this is the program I used

// Adafruit feather 32u4 Lora connecting to region EU868
//
// using MCCI_LoRaWAN_LMIC_library for EU868 modify
// C:\Users\bb\Documents\Arduino\libraries\MCCI_LoRaWAN_LMIC_library\project_config\lmic_project_config.h
// #define CFG_eu868 1  // set for EU868

/*******************************************************************************
 * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
 * Copyright (c) 2018 Terry Moore, MCCI
 *
 * Permission is hereby granted, free of charge, to anyone
 * obtaining a copy of this document and accompanying files,
 * to do whatever they want with them without any restriction,
 * including, but not limited to, copying, modification and redistribution.
 * NO WARRANTY OF ANY KIND IS PROVIDED.
 *
 * This example sends a valid LoRaWAN packet with payload "Hello,
 * world!", using frequency and encryption settings matching those of
 * the The Things Network.
 *
 * This uses OTAA (Over-the-air activation), where where a DevEUI and
 * application key is configured, which are used in an over-the-air
 * activation procedure where a DevAddr and session keys are
 * assigned/generated for use with all further communication.
 *
 * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
 * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
 * violated by this sketch when left running for longer)!

 * To use this sketch, first register your application and device with
 * the things network, to set or generate an AppEUI, DevEUI and AppKey.
 * Multiple devices can use the same AppEUI, but each device has its own
 * DevEUI and AppKey.
 *
 * Do not forget to define the radio type correctly in
 * arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt.
 *
 *******************************************************************************/

#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>

// This EUI must be in little-endian format, so least-significant-byte
// first. When copying an EUI from ttnctl output, this means to reverse
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
// 0x70.
static const u1_t PROGMEM APPEUI[8]={xxxxxxx };
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}

// This should also be in little endian format, see above.
static const u1_t PROGMEM DEVEUI[8]={ 0xxxxxxxx};
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}

// This key should be in big endian format (or, since it is not really a
// number but a block of memory, endianness does not really apply). In
// practice, a key taken from ttnctl can be copied as-is.
// The key shown here is the semtech default key.
static const u1_t PROGMEM APPKEY[16] = {xxxxxxxxxxxx };
void os_getDevKey (u1_t* buf) {  memcpy_P(buf, APPKEY, 16);}

static uint8_t mydata[] = "Hello, world!";
static osjob_t sendjob;

// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 60;

// Pin mapping for Adafruit Feather 32u4 LoRa, etc.
// Just like Feather M0 LoRa, but uses SPI at 1MHz; and that's only
// because MCCI doesn't have a test board; probably higher frequencies
// will work.
// /!\ By default Feather 32u4's pin 6 and DIO1 are not connected. Please 
// ensure they are connected.
const lmic_pinmap lmic_pins = {
    .nss = 8,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 4,
    .dio = {7, 6, LMIC_UNUSED_PIN},
    .rxtx_rx_active = 0,
    .rssi_cal = 8,              // LBT cal for the Adafruit Feather 32U4 LoRa, in dB
    .spi_freq = 1000000,
};



void printHex2(unsigned v) {
    v &= 0xff;
    if (v < 16)
        Serial.print('0');
    Serial.print(v, HEX);
}

void onEvent (ev_t ev) {
    Serial.print(os_getTime());
    Serial.print(": ");
    switch(ev) {
        case EV_SCAN_TIMEOUT:
            Serial.println(F("EV_SCAN_TIMEOUT"));
            break;
        case EV_BEACON_FOUND:
            Serial.println(F("EV_BEACON_FOUND"));
            break;
        case EV_BEACON_MISSED:
            Serial.println(F("EV_BEACON_MISSED"));
            break;
        case EV_BEACON_TRACKED:
            Serial.println(F("EV_BEACON_TRACKED"));
            break;
        case EV_JOINING:
            Serial.println(F("EV_JOINING"));
            break;
        case EV_JOINED:
            Serial.println(F("EV_JOINED"));
            {
              u4_t netid = 0;
              devaddr_t devaddr = 0;
              u1_t nwkKey[16];
              u1_t artKey[16];
              LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
              Serial.print("netid: ");
              Serial.println(netid, DEC);
              Serial.print("devaddr: ");
              Serial.println(devaddr, HEX);
              Serial.print("AppSKey: ");
              for (size_t i=0; i<sizeof(artKey); ++i) {
                if (i != 0)
                  Serial.print("-");
                printHex2(artKey[i]);
              }
              Serial.println("");
              Serial.print("NwkSKey: ");
              for (size_t i=0; i<sizeof(nwkKey); ++i) {
                      if (i != 0)
                              Serial.print("-");
                      printHex2(nwkKey[i]);
              }
              Serial.println();
            }
            // Disable link check validation (automatically enabled
            // during join, but because slow data rates change max TX
	    // size, we don't use it in this example.
            LMIC_setLinkCheckMode(0);
            break;
        /*
        || This event is defined but not used in the code. No
        || point in wasting codespace on it.
        ||
        || case EV_RFU1:
        ||     Serial.println(F("EV_RFU1"));
        ||     break;
        */
        case EV_JOIN_FAILED:
            Serial.println(F("EV_JOIN_FAILED"));
            break;
        case EV_REJOIN_FAILED:
            Serial.println(F("EV_REJOIN_FAILED"));
            break;
        case EV_TXCOMPLETE:
            Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
            if (LMIC.txrxFlags & TXRX_ACK)
              Serial.println(F("Received ack"));
            if (LMIC.dataLen) {
              char text[50]={0};
              Serial.print(F("Received "));
              Serial.print(LMIC.dataLen);
              Serial.println(F(" bytes of payload"));
              for(int i=0;i< LMIC.dataLen;i++)
                 { Serial.print(" 0x"); Serial.print(text[i]=(LMIC.frame+LMIC.dataBeg)[i],  HEX);}
              Serial.println();
              Serial.println(text);
           }
            // Schedule next transmission
            os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
            break;
        case EV_LOST_TSYNC:
            Serial.println(F("EV_LOST_TSYNC"));
            break;
        case EV_RESET:
            Serial.println(F("EV_RESET"));
            break;
        case EV_RXCOMPLETE:
            // data received in ping slot
            Serial.println(F("EV_RXCOMPLETE"));
            break;
        case EV_LINK_DEAD:
            Serial.println(F("EV_LINK_DEAD"));
            break;
        case EV_LINK_ALIVE:
            Serial.println(F("EV_LINK_ALIVE"));
            break;
        /*
        || This event is defined but not used in the code. No
        || point in wasting codespace on it.
        ||
        || case EV_SCAN_FOUND:
        ||    Serial.println(F("EV_SCAN_FOUND"));
        ||    break;
        */
        case EV_TXSTART:
            Serial.println(F("EV_TXSTART"));
            break;
        case EV_TXCANCELED:
            Serial.println(F("EV_TXCANCELED"));
            break;
        case EV_RXSTART:
            /* do not print anything -- it wrecks timing */
            break;
        case EV_JOIN_TXCOMPLETE:
            Serial.println(F("EV_JOIN_TXCOMPLETE: no JoinAccept"));
            break;

        default:
            Serial.print(F("Unknown event: "));
            Serial.println((unsigned) ev);
            break;
    }
}

void do_send(osjob_t* j){
    // Check if there is not a current TX/RX job running
    if (LMIC.opmode & OP_TXRXPEND) {
        Serial.println(F("OP_TXRXPEND, not sending"));
    } else {
        // Prepare upstream data transmission at the next possible time.
        LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
        Serial.println(F("Packet queued"));
    }
    // Next TX is scheduled after TX_COMPLETE event.
}

void setup() {
    delay(5000);
    while (! Serial) ;
    Serial.begin(115200);

    Serial.println(F("MCCI_LoRaWAN_LMIC Starting"));

    #ifdef VCC_ENABLE
    // For Pinoccio Scout boards
    pinMode(VCC_ENABLE, OUTPUT);
    digitalWrite(VCC_ENABLE, HIGH);
    delay(1000);
    #endif

    // LMIC init
    os_init();
    // Reset the MAC state. Session and pending data transfers will be discarded.
    LMIC_reset();
    LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100);  // **** does not work without this


    // Start job (sending automatically starts OTAA too)
    do_send(&sendjob);
}

void loop() {
    os_runloop_once();
}

looking again at the screen shots of post #7 your RFM96 board is communicating with the gateway OK and the Thing LoraWAN V3 server wihich receives the Join request and transmits an Join Accepted - it appears this is not being received by the RFM96 board

what antenna are you using?
are you sure the gateway you are using is fully LoraWAN V3 compatible?
it may be worth trying File>Examples>MCCI LoRaWAN LMIC Library>ttn-abp

the LoraWAN code I used was File>Examples>MCCI LoRaWAN LMIC Library>ttn-otta

Then using values from The Things V3 LoraWAN server set up the APPKEY, DEVEUI and AAPEUI uploaded the code and it connected OK as described in post #13
this is the program I used

This was exactly what I did in my set up. And we used similar code as well.

But for this part of your code:

const lmic_pinmap lmic_pins = {
    .nss = 8,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 4,
    .dio = {7, 6, LMIC_UNUSED_PIN},
    .rxtx_rx_active = 0,
    .rssi_cal = 8,              // LBT cal for the Adafruit Feather 32U4 LoRa, in dB
    .spi_freq = 1000000,

My code is like this:

const lmic_pinmap lmic_pins = {
    .nss = 10,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 5,
    .dio = {2, 3, LMIC_UNUSED_PIN},
};

Do I need to add this part in my code?

     .rxtx_rx_active = 0,
    .rssi_cal = 8,              // LBT cal for the Adafruit Feather 32U4 LoRa, in dB
    .spi_freq = 1000000,

Also, when I tried File>Examples>MCCI LoRaWAN LMIC Library>ttn-abp, the result were the following:
This was what I got in Arduino IDE:
image
But when I checked my registered gateway in the TTN, I noticed that it receives (I don't know if 'receives' is the right term or just 'communicates') data.

With regard to the Live Data being shown of my registered end node in the TTN, there's no registered Live Data during the duration of my testing

By the way Sir, here are some information when I registered my end node to the TTN:
LoRaWAN version: LoRaWAN Specification 1.0.2
Regional Parameters version: RP001 Regional Parameters 1.0.2
Is this correct???

what antenna are you using?

Actually just a piece of wire measuring 3inches or 7.8 cm as discussed in this link by Adafruit.

are you sure the gateway you are using is fully LoraWAN V3 compatible?

I believe Sir yes. I'm using RAK2287 Concentrator Module.

Again. thank you so much for assisting me and for your answers again for the following questions.

when you set up the RAK7371 what "server address" did you use?
I also use LoRaWAN version: LoRaWAN Specification 1.0.2
your pin settings match the hardware connection in post #7

const lmic_pinmap lmic_pins = {
    .nss = 10,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 5,
    .dio = {2, 3, LMIC_UNUSED_PIN},
};

don't think the other settings are critical
not sure what is going wrong - the last time I had the problem of The Things server telling me "Forward join-accept message" and the Adafruit Feather 23u4 not getting the "EV_JOINED" event I had the wrong antenna connected.
Think we need help from someone who has used this particular module in your region.

when you set up the RAK7371 what "server address" did you use?

The server address is "nam1.cloud.thethings.network" since my gateway's frequency is US915, tho I'm currently in Asia.

However, every time I sent a data, though it is always
Starting
8651: EV_TXSTART
Packet queued
FAILURE
when I counter checked it in the TTN, it reflects. But I don't know if my "Hello, World" message is also reflected since I don't know where to find it in the TTN.

the "hello world" message appears under Live data, e.g.

from the screen dump in post #7 its looks like the Things server is receiving the join-request from your device and transmitting join-accept message but your device never receives it

Oww I see. So the problem might be with the RFM96? Can I say that the connection between them is not yet established? So, the RFM96 should first accept the 'join-accept' message before I can say that their connection is established?