Can't connect to the TTN

Hello all,
I’m using a Waveshare ESP32-S3 connected to a Seeed Grove Wio-E5 and I’m trying to connect to my End Device, but I can’t seem to make it work. I’m located in Europe (berlin) and there are 2 gateways near me, the nearer is about 1.5km from me.

I’ve tried using the TTN Library <TheThingsNetwork.h> but as soon as the code comes to this line:
ttn.showStatus();

or this line:
ttn.join(appEui, appKey);

My board starts crashing. So my first question:

  1. To connect to the TTN, do I explicitly need to use the TTN Library?

I’ve tried using other libraries, including the code which is provided by Seeed. As soon as I send the AT Commands to join, I’m unable to join (and nothing is shown in the live data in the TTN Console).

This is the code I’m using to test sending a Hello World to the TTN:

#include <Arduino.h>
#include <SoftwareSerial.h>
#include <Wire.h>

// Define RX and TX pins
#define RX_PIN 38  // RX pin
#define TX_PIN 37  // TX pin

void sendATCommand(const char* command, int waitTime);
void setupLoRaWAN();
void sendHelloWorld();

void setup() {
  Serial.begin(115200);  // Initialize Serial for debugging
  Serial2.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN);  // Initialize Serial2 for Wio-E5 communication

  delay(1000);  // Give the module time to initialize

  Serial.println("Starting LoRaWAN setup...");

  // Configure the LoRaWAN parameters and join the network
  setupLoRaWAN();

  // Send a Hello World message
  sendHelloWorld();
}

void loop() {
  // Nothing needed in the loop for this example
}

// Function to send AT commands and wait for a response
void sendATCommand(const char* command, int waitTime) {
  Serial2.println(command);  // Send command to Wio-E5
  Serial.print("Sent: ");
  Serial.println(command);

  delay(waitTime);

  while (Serial2.available()) {
    String response = Serial2.readString();
    Serial.print("Received: ");
    Serial.println(response);
  }
}

// Function to configure the LoRaWAN parameters and join the network
void setupLoRaWAN() {
  //sendATCommand("AT+ID=DevEui,\" **REDACTED** \"", 1000);  // Set Device EUI
  //sendATCommand("AT+ID=AppEui,\" **REDACTED** \"", 1000);  // Set Application EUI
  sendATCommand("AT+KEY=APPKEY,\" **REDACTED** \"", 1000);  // Set Application Key
  sendATCommand("AT+MODE=LWOTAA", 1000);  // Set mode to OTAA (Over the Air Activation)
  sendATCommand("AT+CLASS=A", 1000);  // Set the device class to Class A
  sendATCommand("AT+DR=EU868", 1000);  // Set the data rate and frequency plan (EU868 in this case)
  sendATCommand("AT+JOIN", 10000);  // Join the network

  // Wait for the network to join
  Serial.println("Waiting for network join...");
  delay(10000);
}

// Function to send a Hello World message to the TTN
void sendHelloWorld() {
  // Convert "Hello World" to HEX: 48656C6C6F20576F726C64
  sendATCommand("AT+CMSGHEX=\"48656C6C6F20576F726C64\"", 5000);  // Send Hello World in HEX
  Serial.println("Hello World message sent!");
}

I’ve also checked some other libraries (which are made for the ESP32), but they don’t offer the direct change of the RX / TX pins. In their cases, I need to manually add MISO / MOSI pins for example, which in the case of the Grove LoraWAN module, it’s not needed.

This is the code from the Seeed Grove wiki, which is provided as a tutorial to connect to the TTN:

#include <Arduino.h>
#include <U8x8lib.h>

#define RX_PIN 38  // RX pin
#define TX_PIN 37  // TX pin

U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/*reset=*/U8X8_PIN_NONE);

static char recv_buf[512];
static bool is_exist = false;
static bool is_join = false;
static int led = 0;

static int at_send_check_response(char *p_ack, int timeout_ms, char *p_cmd, ...)
{
    int ch;
    int num = 0;
    int index = 0;
    int startMillis = 0;
    va_list args;
    memset(recv_buf, 0, sizeof(recv_buf));
    va_start(args, p_cmd);
    Serial1.printf(p_cmd, args);
    Serial.printf(p_cmd, args);
    va_end(args);
    delay(200);
    startMillis = millis();

    if (p_ack == NULL)
    {
        return 0;
    }

    do
    {
        while (Serial1.available() > 0)
        {
            ch = Serial1.read();
            recv_buf[index++] = ch;
            Serial.print((char)ch);
            delay(2);
        }

        if (strstr(recv_buf, p_ack) != NULL)
        {
            return 1;
        }

    } while (millis() - startMillis < timeout_ms);
    return 0;
}

static void recv_prase(char *p_msg)
{
    if (p_msg == NULL)
    {
        return;
    }
    char *p_start = NULL;
    int data = 0;
    int rssi = 0;
    int snr = 0;

    p_start = strstr(p_msg, "RX");
    if (p_start && (1 == sscanf(p_start, "RX: \"%d\"\r\n", &data)))
    {
        Serial.println(data);
        u8x8.setCursor(2, 4);
        u8x8.print("led :");
        led = !!data;
        u8x8.print(led);
        if (led)
        {
            digitalWrite(LED_BUILTIN, LOW);
        }
        else
        {
            digitalWrite(LED_BUILTIN, HIGH);
        }
    }

    p_start = strstr(p_msg, "RSSI");
    if (p_start && (1 == sscanf(p_start, "RSSI %d,", &rssi)))
    {
        u8x8.setCursor(0, 6);
        u8x8.print("                ");
        u8x8.setCursor(2, 6);
        u8x8.print("rssi:");
        u8x8.print(rssi);
    }
    p_start = strstr(p_msg, "SNR");
    if (p_start && (1 == sscanf(p_start, "SNR %d", &snr)))
    {
        u8x8.setCursor(0, 7);
        u8x8.print("                ");
        u8x8.setCursor(2, 7);
        u8x8.print("snr :");
        u8x8.print(snr);
    }
}

void setup(void)
{
    u8x8.begin();
    u8x8.setFlipMode(1);
    u8x8.setFont(u8x8_font_chroma48medium8_r);

    Serial.begin(115200);
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, HIGH);

    Serial1.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN);  // Initialize Serial1 with RX and TX pins
    Serial.print("E5 LORAWAN TEST\r\n");
    u8x8.setCursor(0, 0);

    if (at_send_check_response("+AT: OK", 100, "AT\r\n"))
    {
        is_exist = true;
        at_send_check_response("+ID: AppEui", 1000, "AT+ID\r\n");
        at_send_check_response("+MODE: LWOTAA", 1000, "AT+MODE=LWOTAA\r\n");
        at_send_check_response("+DR: EU868", 1000, "AT+DR=EU868\r\n");
        at_send_check_response("+CH: NUM", 1000, "AT+CH=NUM,0-2\r\n");
        at_send_check_response("+KEY: APPKEY", 1000, "AT+KEY=APPKEY,\" **REDACTED** \"\r\n");
        at_send_check_response("+CLASS: C", 1000, "AT+CLASS=A\r\n");
        at_send_check_response("+PORT: 8", 1000, "AT+PORT=8\r\n");
        delay(200);
        u8x8.setCursor(5, 0);
        u8x8.print("LoRaWAN");
        is_join = true;
    }
    else
    {
        is_exist = false;
        Serial.print("No E5 module found.\r\n");
        u8x8.setCursor(0, 1);
        u8x8.print("unfound E5 !");
    }
}

void loop(void)
{
    if (is_exist)
    {
        int ret = 0;
        if (is_join)
        {
            ret = at_send_check_response("+JOIN: Network joined", 12000, "AT+JOIN\r\n");
            if (ret)
            {
                is_join = false;
            }
            else
            {
                at_send_check_response("+ID: AppEui", 1000, "AT+ID\r\n");
                Serial.print("JOIN failed!\r\n\r\n");
                delay(5000);
            }
        }
        else
        {
            char cmd[128];
            sprintf(cmd, "AT+CMSGHEX=\"48656C6C6F20576F726C64\"\r\n");  // "Hello World" in HEX
            ret = at_send_check_response("Done", 5000, cmd);
            if (ret)
            {
                recv_prase(recv_buf);
            }
            else
            {
                Serial.print("Send failed!\r\n\r\n");
            }
            delay(5000);
        }
    }
    else
    {
        delay(1000);
    }
}

These are my logs:

AT+JOIN
+JOIN: Start
+JOIN: NORMAL
+JOIN: Join failed
+JOIN: Done
AT+ID
+ID: DevAddr, **REDACTED**
+ID: DevEui, **REDACTED**
+ID: AppEui, **REDACTED**
JOIN failed!

The JoinEui, DevEui, and AppEui are correct, and are the same for the board and the End Device Registration

I’d appreciate any help. Thank you!