zephyr - lorawan_send() not working on thread

I have been testing this for some time and I cannot figure out why lorawan_send() on zephyr does not work when called from a thread, while calling it from main() works perfectly.

The OTAA join completes successfully and returns no error. If I move the lorawan_send() call directly into main(), the packet is sent correctly. However, when I call it from a separate thread, lorawan_send() fails.

Below is the code I am using.


main.c
#include <zephyr/device.h>
#include <zephyr/lorawan/lorawan.h>
#include <zephyr/kernel.h>

/* Credentials replaced with dummy values (real ones are correct) */
#define LORAWAN_DEV_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
#define LORAWAN_JOIN_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
#define LORAWAN_APP_KEY { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }

#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(lorawan_class_a);

char data[] = {'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'};

static void dl_callback(uint8_t port, uint8_t flags, int16_t rssi, int8_t snr, uint8_t len,
            const uint8_t *hex_data)
{
    LOG_INF("Port %d, Pending %d, RSSI %ddB, SNR %ddBm, Time %d", port,
        flags & LORAWAN_DATA_PENDING, rssi, snr, !!(flags & LORAWAN_TIME_UPDATED));
    if (hex_data) {
        LOG_HEXDUMP_INF(hex_data, len, "Payload: ");
    }
}

static void lorwan_datarate_changed(enum lorawan_datarate dr)
{
    uint8_t unused, max_size;

    lorawan_get_payload_sizes(&unused, &max_size);
    LOG_INF("New Datarate: DR_%d, Max Payload %d", dr, max_size);
}

void lora_send_thread() {
    k_thread_suspend(k_current_get());

    while(true) {
        int ret = lorawan_send(2, data, sizeof(data), LORAWAN_MSG_CONFIRMED);

        if (ret < 0) {
            LOG_ERR("lorawan_send failed: %d", ret);
            return;
        }

        LOG_INF("Data sent!");
        k_sleep(K_SECONDS(20));
    }
}

K_THREAD_DEFINE(lora_send_tid, 10240, lora_send_thread, NULL, NULL, NULL, 0, 0, 0);

int main(void) {
    const struct device *lora_dev;
    struct lorawan_join_config join_cfg;
    uint8_t dev_eui[] = LORAWAN_DEV_EUI;
    uint8_t join_eui[] = LORAWAN_JOIN_EUI;
    uint8_t app_key[] = LORAWAN_APP_KEY;
    int ret;

    struct lorawan_downlink_cb downlink_cb = {
        .port = LW_RECV_PORT_ANY,
        .cb = dl_callback
    };

    lora_dev = DEVICE_DT_GET(DT_ALIAS(lora0));
    if (!device_is_ready(lora_dev)) {
        LOG_ERR("%s: device not ready.", lora_dev->name);
        return 0;
    }

    ret = lorawan_start();
    if (ret < 0) {
        LOG_ERR("lorawan_start failed: %d", ret);
        return 0;
    }

    lorawan_register_downlink_callback(&downlink_cb);
    lorawan_register_dr_changed_callback(lorwan_datarate_changed);

    join_cfg.mode = LORAWAN_ACT_OTAA;
    join_cfg.dev_eui = dev_eui;
    join_cfg.otaa.join_eui = join_eui;
    join_cfg.otaa.app_key = app_key;
    join_cfg.otaa.nwk_key = app_key;
    join_cfg.otaa.dev_nonce = 0u;

    LOG_INF("Joining network over OTAA");
    ret = lorawan_join(&join_cfg);
    if (ret < 0) {
        LOG_ERR("lorawan_join_network failed: %d", ret);
        return 0;
    }

    LOG_INF("Sending data...");
   
    k_thread_resume(lora_send_tid);
}
proj.conf
CONFIG_LOG=y
CONFIG_LORA=y
CONFIG_LORAWAN=y
CONFIG_LORAMAC_REGION_EU868=y=y
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
I tried changing priorities, even using cooperative threads but nothing worked. If you have any insights on how to fix it I would be grateful
Parents
  • Hi,

    The proper way of stopping and releasing a thread is to use semaphores.

    However, I believe that your code logic is not the best for your purpose. One of the following logics would work better:

    message queue and LoRa thread:

    • LoRa thread initialize the LoRa peripheral when thread starts.
    • use a semaphore to wait if the peripheral is not yet initialized.
    • use a message queue in the LoRa thread loop to execute to the send actions.

    Work queue and main thread (no LoRa thread):

    • the main thread initialize the LoRa peripheral when thread starts.
    • use work queue to execute the send actions.
    • use a semaphore inside the work function to wait if the peripheral is not yet initialized.

    These are just recommendation. I haven't tested them, and they might not work, but I believe you should steer more into this kind of logic.

    Also, I believe this is a typo, but you have "=y" twice here in your proj.conf:

    CONFIG_LORAMAC_REGION_EU868=y=y

    Tell me if any of this helped.

    Best regards,

    Simon D-M

Reply
  • Hi,

    The proper way of stopping and releasing a thread is to use semaphores.

    However, I believe that your code logic is not the best for your purpose. One of the following logics would work better:

    message queue and LoRa thread:

    • LoRa thread initialize the LoRa peripheral when thread starts.
    • use a semaphore to wait if the peripheral is not yet initialized.
    • use a message queue in the LoRa thread loop to execute to the send actions.

    Work queue and main thread (no LoRa thread):

    • the main thread initialize the LoRa peripheral when thread starts.
    • use work queue to execute the send actions.
    • use a semaphore inside the work function to wait if the peripheral is not yet initialized.

    These are just recommendation. I haven't tested them, and they might not work, but I believe you should steer more into this kind of logic.

    Also, I believe this is a typo, but you have "=y" twice here in your proj.conf:

    CONFIG_LORAMAC_REGION_EU868=y=y

    Tell me if any of this helped.

    Best regards,

    Simon D-M

Children
  • Hi,

    Thank you for your response. I wanted to separate the main thread from the LoRa thread, so I followed your first approach and did some tests. I concluded that the main reason it was not working before was that I was initializing LoRa in one thread and sending messages from another. I still don’t fully understand why, but it seems to be working properly now.

    I also started using semaphores to start the thread and msgq for message passing, but I tried it without them as well and it worked fine. That’s why I concluded that the issue was related to the initialization.

    Thank you for your help.

  • Hi,

    Glad that you were able to do what you wanted Slight smile

    I don't really know why it didn't work in the main thread as I currently don't have the required hardware to test the program. But I'm wondering if that may be because the thread where the LoRa was initialized was terminated thus releasing some of the memory used by the LoRa API.

    Also, can I close this ticket ?

    Best regards,

    Simon D-M

  • Hi,

    In the meantime I’ve done some additional tests and I was able to identify the root cause of the hard faults that were occurring when everything was initialized from the main thread.

    The issue was related to the downlink callback registration being done in the main thread. Since that thread terminates, the callback pointer became inaccessible, which ultimately caused the hard faults. I actually ran into the same behavior again later on, which confirmed the cause.

    The best solution was to declare the callback (or the related structure) globally so that its lifetime is not tied to a thread that may terminate or a function. After making this change, the problem was resolved and everything is now working as expected.

    Thank you again for your help.

    Yes, you can close the ticket.

    Best regards

Related