NTN Support and Application Feasibility on nRF9151

Inquiry: NTN Support and Application Feasibility on nRF9151

I would like to seek clarification regarding Non-Terrestrial Network (NTN) support on the nRF9151.

There have been discussions and references suggesting NTN capability on the nRF9151, and I would like to better understand the current level of support and practical limitations.

Specifically, I am interested in the following:

1. Resource Availability

  • Which nRF Connect SDK versions include sample applications or support for NTN on nRF9151?

  • Are there example projects, reference code, or documentation demonstrating NTN connectivity in practice?

  • If official samples are not yet available, is there a roadmap or expected timeline for when they might be provided?

  • What type of antenna is required or recommended for NTN operation on the nRF9151?

2. Application Feasibility

We are currently using the nRF9151 with a Hologram eSIM in India, and our solution is working reliably:

  • connectivity

  • Cellular IoT stack

  • Data transmission to a CoAP server

Given this setup:

  • Is a similar application architecture feasible over NTN?

  • Can CoAP-based data transmission be supported over NTN in a comparable manner?

3. SDK Support and Examples

  • I could not find any nRF Connect SDK samples, documentation, or application notes specifically related to NTN on nRF9151

  • Are there any example applications, experimental features, or recommended references available?

  • If not, could you please advise on the current status and roadmap for NTN support on nRF9151?

Any guidance, documentation links, or clarification would be greatly appreciated.

Thank you for your support.

Parents
  • Hi Priyesh,
    To fill out some more information not already answered by Achim:

    Which nRF Connect SDK versions include sample applications or support for NTN on nRF9151?

    The NCS version you need is v3.2.0. 

    We recommend starting with either of this two options:

    • The Serial Modem Application (modem only apporach, AT commands)
    • Asset Tracker Template configured for satellite connectivity.

    Note: To ensure NTN support is correctly implemented, please refer to our dedicated NTN use case doc and NTN and TN use case doc, as the standard template is not configured for NTN by default.

    If official samples are not yet available, is there a roadmap or expected timeline for when they might be provided?

    Please reach out to the regional sales manager for your area as we cannot talk about roadmap on DevZone.

    What type of antenna is required or recommended for NTN operation on the nRF9151?

    We recommend reaching out to our antenna solution partners. They are equipped to provide detailed guidance on the most suitable antenna for your specific application.

    NTN NB-IoT require optimal link conditions, and the choice of antenna depends on multiple factors, including whether the application supports both LTE and NTN or NTN alone, as well as the physical constraints of the product.

    For applications targeting both LTE and NTN, it is essential to select a wideband antenna that can meet the performance and form factor requirements of both technologies. The antenna must support a broad frequency range to satisfy the performance criteria of LTE networks and the relevant satellite operators.

    The nRF9151 SMA DK includes Taoglas (LTE/NTN/NR+) and Kyocera (GNSS) antennas.

    If not, could you please advise on the current status and roadmap for NTN support on nRF9151?

    The alpha version of the NTN modem FW is now public and can be downloaded here. This firmware is only for nRF9151 LACA A1 SiP.

    Regards,
    Benjamin

  • Hi Benjamin,

    Thanks for the info.

    Quick question:


    Can we connect to a CoAP server over NTN like we do on cellular, or is NTN only for location updates?
    Is custom payload upload supported beyond basic asset tracking?

    Regards ,
    Priyesh

  • Benjamin,

    Thanks for confirming CoAP/custom payloads work over NTN.

    Achim mentioned adding NTN support using AT commands. Is there a library or code example for this, or is it a manual AT command configuration?

    And also we need a special eSIM/SIM for NTN, different from standard cellular NB-IoT?

    Appreciate the guidance.

    Regards,
    Priyesh

  • Achim mentioned adding NTN support using AT commands.

    That was referring to NCS 3.1.0. With NCS 3.2.0 that's included in the nrf/lib/lte_link_control.

  • Priyesh Shahi said:
    And also we need a special eSIM/SIM for NTN, different from standard cellular NB-IoT?

    No, the nRF9151 supports switching between terrestrial and non-terrestrial radio access. You could use the same SIM card if that is what you are asking. However, you need a network operator that supports NTN.

    Regards,
    Benjamin

  • Thanks for the clarification on the SIM and network operator requirements.

    regards,
    Priyesh

  • I am working with an nRF9151 SMA DK using nRF Connect SDK v3.2.1 and testing NTN connectivity. When using the precompiled Eval HEX and issuing AT commands, the device is able to register on the NTN network, successfully ping external hosts, and establish UDP communication with a server. This indicates that basic NTN registration and IP connectivity are functioning as expected.

    However, while using AT commands, I was not able to construct a valid CoAP payload in hexadecimal format suitable for transmission over NTN. To address this, I switched to the CoAP client sample from the Nordic DevAcademy exercise and modified it to enable NTN, configure the required NTN settings, and provide device location using ntn_location_set() together with an NTN event handler.

    The modified CoAP application builds and flashes successfully, and there are no compile-time or obvious runtime errors. When running this application in LTE mode, the CoAP sample behaves as expected and is able to connect to the server and exchange data. When switching the same application to NTN mode, however, the device does not establish the connection.

    Given that raw UDP communication over NTN works via AT commands and the same CoAP sample works correctly over LTE, I would like to understand whether there are additional requirements or limitations when using CoAP over NTN on the nRF9151. Specifically, are there any required socket, CoAP, or modem configurations that differ between LTE and NTN, or any known constraints that would prevent the DevAcademy CoAP sample from working over NTN without further modification?

    is there anything that I should add in this code? I am using modem firmware from sma dk eval web page.

    code 

    /*
     * Copyright (c) 2019 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    #include <stdio.h>
    #include <ncs_version.h>
    
    #include <zephyr/kernel.h>
    #include <zephyr/net/socket.h>
    
    #include <zephyr/logging/log.h>
    #include <modem/nrf_modem_lib.h>
    #include <modem/lte_lc.h>
    
    #include <zephyr/random/random.h>
    #include <zephyr/net/coap.h>
    
    #include <modem/ntn.h>
    
    #define SEND_REQ_THREAD_STACK_SIZE 1024
    #define SEND_REQ_THREA_PRIORITY 5
    
    /* Define the macros for the CoAP version and message length */
    #define APP_COAP_VERSION 1
    #define APP_COAP_MAX_MSG_LEN 1280
    
    K_SEM_DEFINE(con_sem, 0, 1);
    
    /* Declare the buffer coap_buf to receive the response. */
    static uint8_t coap_buf[APP_COAP_MAX_MSG_LEN];
    
    /* Define the CoAP message token next_token */
    static uint16_t next_token;
    
    static int sock;
    static struct sockaddr_storage server;
    
    static uint8_t test_counter = 0;
    
    K_SEM_DEFINE(lte_connected, 0, 1);
    
    LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
    
    /**@brief Resolves the configured hostname. */
    static int server_resolve(void)
    {
            int err;
            struct addrinfo *result;
            struct addrinfo hints = {
                .ai_family = AF_INET,
                .ai_socktype = SOCK_DGRAM};
            char ipv4_addr[NET_IPV4_ADDR_LEN];
    
            err = getaddrinfo(CONFIG_COAP_SERVER_HOSTNAME, NULL, &hints, &result);
            if (err != 0)
            {
                    LOG_ERR("ERROR: getaddrinfo failed %d\n", err);
                    return -EIO;
            }
    
            if (result == NULL)
            {
                    LOG_ERR("ERROR: Address not found\n");
                    return -ENOENT;
            }
    
            /* IPv4 Address. */
            struct sockaddr_in *server4 = ((struct sockaddr_in *)&server);
    
            server4->sin_addr.s_addr =
                ((struct sockaddr_in *)result->ai_addr)->sin_addr.s_addr;
            server4->sin_family = AF_INET;
            server4->sin_port = htons(CONFIG_COAP_SERVER_PORT);
    
            inet_ntop(AF_INET, &server4->sin_addr.s_addr, ipv4_addr,
                      sizeof(ipv4_addr));
            LOG_INF("IPv4 Address found %s\n", ipv4_addr);
    
            /* Free the address. */
            freeaddrinfo(result);
    
            return 0;
    }
    
    /**@brief Initialize the CoAP client */
    static int client_init(void)
    {
            int err;
    
            sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
            if (sock < 0)
            {
                    LOG_ERR("Failed to create CoAP socket: %d.\n", errno);
                    return -errno;
            }
    
            err = connect(sock, (struct sockaddr *)&server,
                          sizeof(struct sockaddr_in));
            if (err < 0)
            {
                    LOG_ERR("Connect failed : %d\n", errno);
                    return -errno;
            }
    
            LOG_INF("Successfully connected to server");
    
            /* Generate a random token after the socket is connected */
            next_token = sys_rand32_get();
    
            k_sem_give(&con_sem);
    
            return 0;
    }
    
    static void lte_handler(const struct lte_lc_evt *const evt)
    {
            switch (evt->type)
            {
            case LTE_LC_EVT_NW_REG_STATUS:
                    if ((evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_HOME) &&
                        (evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_ROAMING))
                    {
                            break;
                    }
                    LOG_INF("Network registration status: %s",
                            evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_HOME ? "Connected - home network" : "Connected - roaming");
                    k_sem_give(&lte_connected);
                    break;
            case LTE_LC_EVT_RRC_UPDATE:
                    LOG_INF("RRC mode: %s", evt->rrc_mode == LTE_LC_RRC_MODE_CONNECTED ? "Connected" : "Idle");
                    break;
            default:
                    break;
            }
    }
    
    static int modem_configure(void)
    {
            int err;
    
            LOG_INF("Initializing modem library");
            err = nrf_modem_lib_init();
            if (err)
            {
                    LOG_ERR("Failed to initialize the modem library, error: %d", err);
                    return err;
            }
            lte_lc_system_mode_set(LTE_LC_SYSTEM_MODE_NTN_NBIOT, LTE_LC_SYSTEM_MODE_PREFER_AUTO);
    
            LOG_INF("Connecting to LTE network");
            err = lte_lc_connect_async(lte_handler);
            if (err)
            {
                    LOG_ERR("Error in lte_lc_connect_async, error: %d", err);
                    return err;
            }
    
            k_sem_take(&lte_connected, K_FOREVER);
            LOG_INF("Connected to LTE network");
    
            return 0;
    }
    
    /**@brief Send CoAP POST request. */
    static int client_post_send(void)
    {
            int err;
            struct coap_packet request;
    
            next_token = sys_rand32_get();
    
            /* Initialize the CoAP packet and append the resource path */
            err = coap_packet_init(&request, coap_buf, sizeof(coap_buf),
                                   APP_COAP_VERSION, COAP_TYPE_CON,
                                   sizeof(next_token), (uint8_t *)&next_token,
                                   COAP_METHOD_POST, coap_next_id());
            if (err < 0)
            {
                    LOG_ERR("Failed to create CoAP request, %d\n", err);
                    return err;
            }
    
            err = coap_packet_append_option(&request, COAP_OPTION_URI_HOST,
                                            (uint8_t *)CONFIG_COAP_SERVER_HOSTNAME,
                                            strlen(CONFIG_COAP_SERVER_HOSTNAME));
            if (err < 0)
            {
                    LOG_ERR("Failed to encode CoAP option, %d\n", err);
                    return err;
            }
    
            err = coap_packet_append_option(&request, COAP_OPTION_URI_PATH,
                                            (uint8_t *)CONFIG_COAP_TX_RESOURCE,
                                            strlen(CONFIG_COAP_TX_RESOURCE));
            if (err < 0)
            {
                    LOG_ERR("Failed to encode CoAP option, %d\n", err);
                    return err;
            }
    
            /* Add payload marker */
            err = coap_packet_append_payload_marker(&request);
            if (err < 0)
            {
                    LOG_ERR("Failed to append payload marker, %d\n", err);
                    return err;
            }
    
            /* Format and append test_counter as payload */
            char payload[64];
    
            int len = snprintf(payload, sizeof(payload), "0000000000000000000000000000000");
            if (len < 0 || len >= sizeof(payload))
            {
                    LOG_ERR("Failed to format counter payload");
                    return -EINVAL;
            }
    
            err = coap_packet_append_payload(&request, (uint8_t *)payload, len);
            if (err < 0)
            {
                    LOG_ERR("Failed to append payload, %d\n", err);
                    return err;
            }
    
            err = send(sock, request.data, request.offset, 0);
            if (err < 0)
            {
                    LOG_ERR("Failed to send CoAP request, %d\n", errno);
                    return -errno;
            }
    
            LOG_INF("CoAP POST request sent: Token 0x%04x\n", next_token);
    
            return 0;
    }
    
    /**@brief Handles responses from the remote CoAP server. */
    static int client_handle_response(uint8_t *buf, int received)
    {
            struct coap_packet reply;
            uint8_t token[8];
            uint16_t token_len;
            const uint8_t *payload;
            uint16_t payload_len;
            uint8_t temp_buf[128];
            /* Parse the received CoAP packet */
            int err = coap_packet_parse(&reply, buf, received, NULL, 0);
            if (err < 0)
            {
                    LOG_ERR("Malformed response received: %d\n", err);
                    return err;
            }
    
            /* Confirm the token in the response matches the token sent */
            token_len = coap_header_get_token(&reply, token);
            if ((token_len != sizeof(next_token)) ||
                (memcmp(&next_token, token, sizeof(next_token)) != 0))
            {
                    LOG_ERR("Invalid token received: 0x%02x%02x\n",
                            token[1], token[0]);
                    return 0;
            }
    
            /* Retrieve the payload and confirm it's nonzero */
            payload = coap_packet_get_payload(&reply, &payload_len);
    
            if (payload_len > 0)
            {
                    snprintf(temp_buf, MIN(payload_len + 1, sizeof(temp_buf)), "%s", payload);
            }
            else
            {
                    strcpy(temp_buf, "EMPTY");
            }
    
            /* Log the header code, token and payload of the response */
            LOG_INF("CoAP response: Code 0x%x, Token 0x%02x%02x, Payload: %s\n",
                    coap_header_get_code(&reply), token[1], token[0], (char *)temp_buf);
    
            return 0;
    }
    
    /* Example location structure – exact type is defined by the NTN API
     * (not fully shown in the provided docs). Adjust to the real type.
     */
    struct app_location
    {
            double lat;
            double lon;
            float accuracy_m;
    };
    
    /* Stub: get location from external GNSS or other source */
    static int app_get_location(struct app_location *loc)
    {
            /* TODO: Implement real location acquisition */
            loc->lat = 36.331449;
            loc->lon = -119.292130;
            loc->accuracy_m = 200.0f;
            return 0;
    }
    
    /* Convert app_location to NTN library location type and call ntn_location_set().
     * The docs state that validity time must be longer than the interval between updates.[[NTN usage](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/libraries/modem/ntn.html#usage)]
     */
    static int app_ntn_provide_location(uint32_t validity_time_s)
    {
            struct app_location loc;
            int err = app_get_location(&loc);
            if (err)
            {
                    LOG_ERR("Failed to get location: %d", err);
                    return err;
            }
    
            err = ntn_location_set(loc.lat, loc.lon, 12, validity_time_s);
            if (err)
            {
                    LOG_ERR("ntn_location_set failed: %d", err);
            }
            else
            {
                    LOG_INF("NTN location updated, valid for %u s", validity_time_s);
            }
    
            return err;
    }
    
    /* NTN event handler – called when modem needs location updates.[[NTN usage](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/libraries/modem/ntn.html#usage)] */
    static void app_ntn_event_handler(const struct ntn_evt *evt)
    {
            switch (evt->type)
            {
            case NTN_EVT_LOCATION_REQUEST:
                    LOG_INF("NTN location request");
    
                    /* Decide update interval based on max device speed, as suggested in docs.
                     * Example: 200 m accuracy, 120 km/h => update at least every 6 s.[[NTN usage](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/libraries/modem/ntn.html#usage)]
                     */
                    app_ntn_provide_location(60000); /* validity_time_s >= update interval */
                    break;
    
            default:
                    LOG_DBG("Unhandled NTN event type: %d", evt->type);
                    break;
            }
    }
    
    int main(void)
    {
            int err;
            int received;
    
            /* Register NTN handler (safe even if you start in TN mode) */
            ntn_register_handler(app_ntn_event_handler);
    
            err = modem_configure();
            if (err)
            {
                    LOG_ERR("Failed to configure the modem");
                    return 0;
            }
    
            if (server_resolve() != 0)
            {
                    LOG_INF("Failed to resolve server name");
                    return 0;
            }
    
            if (client_init() != 0)
            {
                    LOG_INF("Failed to initialize client");
                    return 0;
            }
    
            while (1)
            {
                    /* Receive response from the CoAP server */
                    received = recv(sock, coap_buf, sizeof(coap_buf), 0);
    
                    if (received < 0)
                    {
                            LOG_ERR("Socket error: %d, exit\n", errno);
                            break;
                    }
                    else if (received == 0)
                    {
                            LOG_INF("Empty datagram\n");
                            continue;
                    }
    
                    /* Parse the received CoAP packet */
                    err = client_handle_response(coap_buf, received);
                    if (err < 0)
                    {
                            LOG_ERR("Invalid response, exit\n");
                            break;
                    }
            }
    
            (void)close(sock);
    
            return 0;
    }
    
    void send_req_thread(void *arg1, void *arg2, void *arg3)
    {
            k_sem_take(&con_sem, K_FOREVER);
            while (1)
            {
                    client_post_send();
                    k_sleep(K_SECONDS(10));
            }
    }
    
    K_THREAD_DEFINE(send_req, SEND_REQ_THREAD_STACK_SIZE, send_req_thread, NULL, NULL, NULL,
                    SEND_REQ_THREA_PRIORITY, 0, 0);

Reply
  • I am working with an nRF9151 SMA DK using nRF Connect SDK v3.2.1 and testing NTN connectivity. When using the precompiled Eval HEX and issuing AT commands, the device is able to register on the NTN network, successfully ping external hosts, and establish UDP communication with a server. This indicates that basic NTN registration and IP connectivity are functioning as expected.

    However, while using AT commands, I was not able to construct a valid CoAP payload in hexadecimal format suitable for transmission over NTN. To address this, I switched to the CoAP client sample from the Nordic DevAcademy exercise and modified it to enable NTN, configure the required NTN settings, and provide device location using ntn_location_set() together with an NTN event handler.

    The modified CoAP application builds and flashes successfully, and there are no compile-time or obvious runtime errors. When running this application in LTE mode, the CoAP sample behaves as expected and is able to connect to the server and exchange data. When switching the same application to NTN mode, however, the device does not establish the connection.

    Given that raw UDP communication over NTN works via AT commands and the same CoAP sample works correctly over LTE, I would like to understand whether there are additional requirements or limitations when using CoAP over NTN on the nRF9151. Specifically, are there any required socket, CoAP, or modem configurations that differ between LTE and NTN, or any known constraints that would prevent the DevAcademy CoAP sample from working over NTN without further modification?

    is there anything that I should add in this code? I am using modem firmware from sma dk eval web page.

    code 

    /*
     * Copyright (c) 2019 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    #include <stdio.h>
    #include <ncs_version.h>
    
    #include <zephyr/kernel.h>
    #include <zephyr/net/socket.h>
    
    #include <zephyr/logging/log.h>
    #include <modem/nrf_modem_lib.h>
    #include <modem/lte_lc.h>
    
    #include <zephyr/random/random.h>
    #include <zephyr/net/coap.h>
    
    #include <modem/ntn.h>
    
    #define SEND_REQ_THREAD_STACK_SIZE 1024
    #define SEND_REQ_THREA_PRIORITY 5
    
    /* Define the macros for the CoAP version and message length */
    #define APP_COAP_VERSION 1
    #define APP_COAP_MAX_MSG_LEN 1280
    
    K_SEM_DEFINE(con_sem, 0, 1);
    
    /* Declare the buffer coap_buf to receive the response. */
    static uint8_t coap_buf[APP_COAP_MAX_MSG_LEN];
    
    /* Define the CoAP message token next_token */
    static uint16_t next_token;
    
    static int sock;
    static struct sockaddr_storage server;
    
    static uint8_t test_counter = 0;
    
    K_SEM_DEFINE(lte_connected, 0, 1);
    
    LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
    
    /**@brief Resolves the configured hostname. */
    static int server_resolve(void)
    {
            int err;
            struct addrinfo *result;
            struct addrinfo hints = {
                .ai_family = AF_INET,
                .ai_socktype = SOCK_DGRAM};
            char ipv4_addr[NET_IPV4_ADDR_LEN];
    
            err = getaddrinfo(CONFIG_COAP_SERVER_HOSTNAME, NULL, &hints, &result);
            if (err != 0)
            {
                    LOG_ERR("ERROR: getaddrinfo failed %d\n", err);
                    return -EIO;
            }
    
            if (result == NULL)
            {
                    LOG_ERR("ERROR: Address not found\n");
                    return -ENOENT;
            }
    
            /* IPv4 Address. */
            struct sockaddr_in *server4 = ((struct sockaddr_in *)&server);
    
            server4->sin_addr.s_addr =
                ((struct sockaddr_in *)result->ai_addr)->sin_addr.s_addr;
            server4->sin_family = AF_INET;
            server4->sin_port = htons(CONFIG_COAP_SERVER_PORT);
    
            inet_ntop(AF_INET, &server4->sin_addr.s_addr, ipv4_addr,
                      sizeof(ipv4_addr));
            LOG_INF("IPv4 Address found %s\n", ipv4_addr);
    
            /* Free the address. */
            freeaddrinfo(result);
    
            return 0;
    }
    
    /**@brief Initialize the CoAP client */
    static int client_init(void)
    {
            int err;
    
            sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
            if (sock < 0)
            {
                    LOG_ERR("Failed to create CoAP socket: %d.\n", errno);
                    return -errno;
            }
    
            err = connect(sock, (struct sockaddr *)&server,
                          sizeof(struct sockaddr_in));
            if (err < 0)
            {
                    LOG_ERR("Connect failed : %d\n", errno);
                    return -errno;
            }
    
            LOG_INF("Successfully connected to server");
    
            /* Generate a random token after the socket is connected */
            next_token = sys_rand32_get();
    
            k_sem_give(&con_sem);
    
            return 0;
    }
    
    static void lte_handler(const struct lte_lc_evt *const evt)
    {
            switch (evt->type)
            {
            case LTE_LC_EVT_NW_REG_STATUS:
                    if ((evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_HOME) &&
                        (evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_ROAMING))
                    {
                            break;
                    }
                    LOG_INF("Network registration status: %s",
                            evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_HOME ? "Connected - home network" : "Connected - roaming");
                    k_sem_give(&lte_connected);
                    break;
            case LTE_LC_EVT_RRC_UPDATE:
                    LOG_INF("RRC mode: %s", evt->rrc_mode == LTE_LC_RRC_MODE_CONNECTED ? "Connected" : "Idle");
                    break;
            default:
                    break;
            }
    }
    
    static int modem_configure(void)
    {
            int err;
    
            LOG_INF("Initializing modem library");
            err = nrf_modem_lib_init();
            if (err)
            {
                    LOG_ERR("Failed to initialize the modem library, error: %d", err);
                    return err;
            }
            lte_lc_system_mode_set(LTE_LC_SYSTEM_MODE_NTN_NBIOT, LTE_LC_SYSTEM_MODE_PREFER_AUTO);
    
            LOG_INF("Connecting to LTE network");
            err = lte_lc_connect_async(lte_handler);
            if (err)
            {
                    LOG_ERR("Error in lte_lc_connect_async, error: %d", err);
                    return err;
            }
    
            k_sem_take(&lte_connected, K_FOREVER);
            LOG_INF("Connected to LTE network");
    
            return 0;
    }
    
    /**@brief Send CoAP POST request. */
    static int client_post_send(void)
    {
            int err;
            struct coap_packet request;
    
            next_token = sys_rand32_get();
    
            /* Initialize the CoAP packet and append the resource path */
            err = coap_packet_init(&request, coap_buf, sizeof(coap_buf),
                                   APP_COAP_VERSION, COAP_TYPE_CON,
                                   sizeof(next_token), (uint8_t *)&next_token,
                                   COAP_METHOD_POST, coap_next_id());
            if (err < 0)
            {
                    LOG_ERR("Failed to create CoAP request, %d\n", err);
                    return err;
            }
    
            err = coap_packet_append_option(&request, COAP_OPTION_URI_HOST,
                                            (uint8_t *)CONFIG_COAP_SERVER_HOSTNAME,
                                            strlen(CONFIG_COAP_SERVER_HOSTNAME));
            if (err < 0)
            {
                    LOG_ERR("Failed to encode CoAP option, %d\n", err);
                    return err;
            }
    
            err = coap_packet_append_option(&request, COAP_OPTION_URI_PATH,
                                            (uint8_t *)CONFIG_COAP_TX_RESOURCE,
                                            strlen(CONFIG_COAP_TX_RESOURCE));
            if (err < 0)
            {
                    LOG_ERR("Failed to encode CoAP option, %d\n", err);
                    return err;
            }
    
            /* Add payload marker */
            err = coap_packet_append_payload_marker(&request);
            if (err < 0)
            {
                    LOG_ERR("Failed to append payload marker, %d\n", err);
                    return err;
            }
    
            /* Format and append test_counter as payload */
            char payload[64];
    
            int len = snprintf(payload, sizeof(payload), "0000000000000000000000000000000");
            if (len < 0 || len >= sizeof(payload))
            {
                    LOG_ERR("Failed to format counter payload");
                    return -EINVAL;
            }
    
            err = coap_packet_append_payload(&request, (uint8_t *)payload, len);
            if (err < 0)
            {
                    LOG_ERR("Failed to append payload, %d\n", err);
                    return err;
            }
    
            err = send(sock, request.data, request.offset, 0);
            if (err < 0)
            {
                    LOG_ERR("Failed to send CoAP request, %d\n", errno);
                    return -errno;
            }
    
            LOG_INF("CoAP POST request sent: Token 0x%04x\n", next_token);
    
            return 0;
    }
    
    /**@brief Handles responses from the remote CoAP server. */
    static int client_handle_response(uint8_t *buf, int received)
    {
            struct coap_packet reply;
            uint8_t token[8];
            uint16_t token_len;
            const uint8_t *payload;
            uint16_t payload_len;
            uint8_t temp_buf[128];
            /* Parse the received CoAP packet */
            int err = coap_packet_parse(&reply, buf, received, NULL, 0);
            if (err < 0)
            {
                    LOG_ERR("Malformed response received: %d\n", err);
                    return err;
            }
    
            /* Confirm the token in the response matches the token sent */
            token_len = coap_header_get_token(&reply, token);
            if ((token_len != sizeof(next_token)) ||
                (memcmp(&next_token, token, sizeof(next_token)) != 0))
            {
                    LOG_ERR("Invalid token received: 0x%02x%02x\n",
                            token[1], token[0]);
                    return 0;
            }
    
            /* Retrieve the payload and confirm it's nonzero */
            payload = coap_packet_get_payload(&reply, &payload_len);
    
            if (payload_len > 0)
            {
                    snprintf(temp_buf, MIN(payload_len + 1, sizeof(temp_buf)), "%s", payload);
            }
            else
            {
                    strcpy(temp_buf, "EMPTY");
            }
    
            /* Log the header code, token and payload of the response */
            LOG_INF("CoAP response: Code 0x%x, Token 0x%02x%02x, Payload: %s\n",
                    coap_header_get_code(&reply), token[1], token[0], (char *)temp_buf);
    
            return 0;
    }
    
    /* Example location structure – exact type is defined by the NTN API
     * (not fully shown in the provided docs). Adjust to the real type.
     */
    struct app_location
    {
            double lat;
            double lon;
            float accuracy_m;
    };
    
    /* Stub: get location from external GNSS or other source */
    static int app_get_location(struct app_location *loc)
    {
            /* TODO: Implement real location acquisition */
            loc->lat = 36.331449;
            loc->lon = -119.292130;
            loc->accuracy_m = 200.0f;
            return 0;
    }
    
    /* Convert app_location to NTN library location type and call ntn_location_set().
     * The docs state that validity time must be longer than the interval between updates.[[NTN usage](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/libraries/modem/ntn.html#usage)]
     */
    static int app_ntn_provide_location(uint32_t validity_time_s)
    {
            struct app_location loc;
            int err = app_get_location(&loc);
            if (err)
            {
                    LOG_ERR("Failed to get location: %d", err);
                    return err;
            }
    
            err = ntn_location_set(loc.lat, loc.lon, 12, validity_time_s);
            if (err)
            {
                    LOG_ERR("ntn_location_set failed: %d", err);
            }
            else
            {
                    LOG_INF("NTN location updated, valid for %u s", validity_time_s);
            }
    
            return err;
    }
    
    /* NTN event handler – called when modem needs location updates.[[NTN usage](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/libraries/modem/ntn.html#usage)] */
    static void app_ntn_event_handler(const struct ntn_evt *evt)
    {
            switch (evt->type)
            {
            case NTN_EVT_LOCATION_REQUEST:
                    LOG_INF("NTN location request");
    
                    /* Decide update interval based on max device speed, as suggested in docs.
                     * Example: 200 m accuracy, 120 km/h => update at least every 6 s.[[NTN usage](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/libraries/modem/ntn.html#usage)]
                     */
                    app_ntn_provide_location(60000); /* validity_time_s >= update interval */
                    break;
    
            default:
                    LOG_DBG("Unhandled NTN event type: %d", evt->type);
                    break;
            }
    }
    
    int main(void)
    {
            int err;
            int received;
    
            /* Register NTN handler (safe even if you start in TN mode) */
            ntn_register_handler(app_ntn_event_handler);
    
            err = modem_configure();
            if (err)
            {
                    LOG_ERR("Failed to configure the modem");
                    return 0;
            }
    
            if (server_resolve() != 0)
            {
                    LOG_INF("Failed to resolve server name");
                    return 0;
            }
    
            if (client_init() != 0)
            {
                    LOG_INF("Failed to initialize client");
                    return 0;
            }
    
            while (1)
            {
                    /* Receive response from the CoAP server */
                    received = recv(sock, coap_buf, sizeof(coap_buf), 0);
    
                    if (received < 0)
                    {
                            LOG_ERR("Socket error: %d, exit\n", errno);
                            break;
                    }
                    else if (received == 0)
                    {
                            LOG_INF("Empty datagram\n");
                            continue;
                    }
    
                    /* Parse the received CoAP packet */
                    err = client_handle_response(coap_buf, received);
                    if (err < 0)
                    {
                            LOG_ERR("Invalid response, exit\n");
                            break;
                    }
            }
    
            (void)close(sock);
    
            return 0;
    }
    
    void send_req_thread(void *arg1, void *arg2, void *arg3)
    {
            k_sem_take(&con_sem, K_FOREVER);
            while (1)
            {
                    client_post_send();
                    k_sleep(K_SECONDS(10));
            }
    }
    
    K_THREAD_DEFINE(send_req, SEND_REQ_THREAD_STACK_SIZE, send_req_thread, NULL, NULL, NULL,
                    SEND_REQ_THREA_PRIORITY, 0, 0);

Children
  • CoAP in general assumes a default timeout, which is based on constrained networks.

    But NTN is even "more constraint". In my experience, at least the first message exchange (request/response) takes up to 30s (sometimes even 50s) after the device is RRC active and registered in the network. Follow-up message may be faster (10-15s) but there will be also cases, where more time is needed. I use in the meantime 50s ACK timeout.

  •  I will address CoAP timing later. In this case, the issue appears earlier, as the device does not seem to reach a connected state in NTN mode. The modem keeps requesting location updates, but the application does not progress beyond this, unlike in LTE mode where the same sample works. Therefore, this seems related to NTN attachment or modem state rather than CoAP behavior.

    is there anything that I should change so that it connects to network.

  • is there anything that I should change so that it connects to network.

    I'm not sure, but I don't see, that you select band 256 and 255, do you?

    AT%XBANDLOCK=2,,\"255,256\"

  • > at
    OK
    > at+cfun=0
    OK
    > at%xfactoryreset=0
    OK
    > at%xsystemmode=0,0,0,0,1
    OK
    > at%xbandlock=2,,"255,256"
    OK
    > at%location=2,"36.331449","-119.292130","0",0,0
    OK
    > at+cgdcont=0,"IP","data.mono"
    OK
    > at%mdmev=2
    OK
    > at+cscon=3
    OK
    > at+cnec=24
    OK
    > at+cereg=5
    OK
    > at+cfun=1
    OK
    %MDMEV: SEARCH STATUS 1
    +CEREG: 91,,,14
    +CEREG: 4
    %MDMEV: SEARCH STATUS 2
    > at#xping="8.8.8.8",32,30000,4,1000
    "EAI_SYSTEM"
    ERROR
    
    > at
    OK
    > at+cfun=0
    OK
    > at%xfactoryreset=0
    OK
    > at%xsystemmode=0,0,0,0,1
    OK
    > at%xbandlock=2,,"255,256"
    OK
    > at%location=2,"36.331449","-119.292130","0",10,0
    OK
    > at+cgdcont=0,"ip","data.mono"
    OK
    > at%mdmev=2
    OK
    > at+cscon=3
    OK
    > at+cereg=5
    OK
    > at+cnec=24
    OK
    > at+cfun=1
    OK
    %MDMEV: SEARCH STATUS 1
    +CEREG: 91,,,14
    +CEREG: 4
    %MDMEV: SEARCH STATUS 2
    

    earlier i was able to ping with these commands, but now even that is failing.

  • I also tried with at monitor sample, where i modified commands.

    /*
     * Copyright (c) 2021 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    #include <string.h>
    #include <zephyr/kernel.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <nrf_modem_at.h>
    #include <modem/nrf_modem_lib.h>
    #include <modem/at_monitor.h>
    #include <modem/modem_info.h>
    
    AT_MONITOR(cmd_rsp, ANY, cmd_mon, ACTIVE);
    
    uint8_t response[128] = {0};
    
    static void cmd_mon(const char *notif)
    {
    
    	printk("%s\n", notif);
    }
    
    int main(void)
    {
    	int err;
    
    	printk("AT Monitor sample started\n");
    
    	err = nrf_modem_lib_init();
    	if (err)
    	{
    		printk("Modem library initialization failed, error: %d\n", err);
    		return 0;
    	}
    
    	err = nrf_modem_at_cmd(response, sizeof(response), "AT+CFUN=0");
    	if (err)
    	{
    		printk("Failed to read AT+CFUN=0, err %d\n", err);
    		return 0;
    	}
    
    	printk("%s\n", response);
    	k_msleep(1000);
    	err = nrf_modem_at_cmd(response, sizeof(response), "AT%%XFACTORYRESET=0");
    	if (err)
    	{
    		printk("Failed to read xfactoryreset, err %d\n", err);
    		return 0;
    	}
    
    	printk("%s\n", response);
    	k_msleep(1000);
    	err = nrf_modem_at_cmd(response, sizeof(response), "AT%%XSYSTEMMODE=0,0,0,0,1");
    	if (err)
    	{
    		printk("Failed to read XSYSTEMMODE, err %d\n", err);
    		return 0;
    	}
    
    	printk("%s\n", response);
    	k_msleep(1000);
    	err = nrf_modem_at_cmd(response, sizeof(response), "AT%%XBANDLOCK=2,,\"255,256\"");
    	if (err)
    	{
    		printk("Failed to read XBANDLOCK, err %d\n", err);
    		return 0;
    	}
    
    	printk("%s\n", response);
    	k_msleep(1000);
    	err = nrf_modem_at_cmd(response, sizeof(response), "AT%%LOCATION=2,\"36.33144938\",\"-119.292130\",\"0\",0,0");
    	if (err)
    	{
    		printk("Failed to read LOCATION, err %d\n", err);
    		return 0;
    	}
    
    	printk("%s\n", response);
    	k_msleep(1000);
    	err = nrf_modem_at_cmd(response, sizeof(response), "AT+CGDCONT=0,\"IP\",\"data.mono\"");
    	if (err)
    	{
    		printk("Failed to read CGDCONT, err %d\n", err);
    		return 0;
    	}
    
    	printk("%s\n", response);
    	k_msleep(1000);
    	err = nrf_modem_at_cmd(response, sizeof(response), "AT%MDMEV=2");
    	if (err)
    	{
    		printk("Failed to read MDMEV, err %d\n", err);
    		return 0;
    	}
    
    	printk("%s\n", response);
    	k_msleep(1000);
    	err = nrf_modem_at_cmd(response, sizeof(response), "AT+CSCON=3");
    	if (err)
    	{
    		printk("Failed to read CSCON, err %d\n", err);
    		return 0;
    	}
    
    	printk("%s\n", response);
    	k_msleep(1000);
    
    	err = nrf_modem_at_cmd(response, sizeof(response), "AT+CNEC=24");
    	if (err)
    	{
    		printk("Failed to read CEREG, err %d\n", err);
    		return 0;
    	}
    
    	printk("%s\n", response);
    	k_msleep(1000);
    
    	err = nrf_modem_at_cmd(response, sizeof(response), "AT+CEREG=5");
    	if (err)
    	{
    		printk("Failed to read CEREG, err %d\n", err);
    		return 0;
    	}
    
    	printk("%s\n", response);
    	k_msleep(1000);
    
    	err = nrf_modem_at_cmd(response, sizeof(response), "AT+CFUN=1");
    	if (err)
    	{
    		printk("Failed to read CEREG, err %d\n", err);
    		return 0;
    	}
    
    	printk("%s\n", response);
    	k_msleep(200000);
    
    	// err = nrf_modem_at_cmd(response, sizeof(response), "AT#XPING=\"8.8.8.8\",32,30000,4,1000");
    	// if (err)
    	// {
    	// 	printk("Failed to read XPING, err %d\n", err);
    	// }
    
    	// printk("%s\n", response);
    
    	while (true)
    	{
    		/* code */
    		k_msleep(10000);
    		err = nrf_modem_at_cmd(response, sizeof(response), "AT%%XMONITOR");
    		if (err)
    		{
    			printk("Failed to read CEREG, err %d\n", err);
    			return 0;
    		}
    
    		printk("%s\n", response);
    	}
    }
    

Related