diff --git a/examples/zephyr/location/CMakeLists.txt b/examples/zephyr/location/CMakeLists.txt index 412514fe1..40ccc0fe0 100644 --- a/examples/zephyr/location/CMakeLists.txt +++ b/examples/zephyr/location/CMakeLists.txt @@ -8,3 +8,4 @@ project(location) target_sources(app PRIVATE src/main.c) target_sources_ifdef(CONFIG_GOLIOTH_CELLULAR_PLAYBACK app PRIVATE src/cellular_playback.c) target_sources_ifdef(CONFIG_GOLIOTH_WIFI_PLAYBACK app PRIVATE src/wifi_playback.c) +target_sources_ifdef(CONFIG_SOC_SERIES_NRF91X app PRIVATE src/cellular_nrf91.c) diff --git a/examples/zephyr/location/Kconfig b/examples/zephyr/location/Kconfig index 98df34512..ca1b4caba 100644 --- a/examples/zephyr/location/Kconfig +++ b/examples/zephyr/location/Kconfig @@ -2,6 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 configdefault GOLIOTH_LOCATION_CELLULAR + default y if SOC_SERIES_NRF91X default y if GOLIOTH_CELLULAR_PLAYBACK configdefault GOLIOTH_LOCATION_WIFI diff --git a/examples/zephyr/location/README.md b/examples/zephyr/location/README.md index 1ef427000..57ef70776 100644 --- a/examples/zephyr/location/README.md +++ b/examples/zephyr/location/README.md @@ -172,6 +172,16 @@ $ west build -b nrf52840dk/nrf52840 examples/zephyr/location $ west flash ``` +#### nRF9160 DK + +On your host computer open a terminal window, locate the source code of +this sample application (i.e., `examples/zephyr/location`) and type: + +```console +$ west build -b nrf9160dk/nrf9160/ns examples/zephyr/location +$ west flash +``` + ### Sample output This is the output from the serial console: diff --git a/examples/zephyr/location/sample.yaml b/examples/zephyr/location/sample.yaml index cbb891b96..356e41d8d 100644 --- a/examples/zephyr/location/sample.yaml +++ b/examples/zephyr/location/sample.yaml @@ -14,6 +14,8 @@ tests: - native_sim - native_sim/native/64 - nrf52840dk/nrf52840 + - nrf9151dk/nrf9151/ns + - nrf9160dk/nrf9160/ns sample.golioth.location.wifi_only: extra_configs: - CONFIG_GOLIOTH_CELLULAR_PLAYBACK=n diff --git a/examples/zephyr/location/socs/nrf9151_ns.conf b/examples/zephyr/location/socs/nrf9151_ns.conf new file mode 100644 index 000000000..bd3086af5 --- /dev/null +++ b/examples/zephyr/location/socs/nrf9151_ns.conf @@ -0,0 +1,41 @@ +# General config +CONFIG_HEAP_MEM_POOL_SIZE=4096 +CONFIG_NEWLIB_LIBC=y + +# Networking +CONFIG_NET_SOCKETS_OFFLOAD=y +CONFIG_NET_IPV6=y +CONFIG_NET_IPV6_NBR_CACHE=n +CONFIG_NET_IPV6_MLD=n + +# Increase native TLS socket implementation, so that it is chosen instead of +# offloaded nRF91 sockets +CONFIG_NET_SOCKETS_TLS_PRIORITY=35 + +# Modem library +CONFIG_NRF_MODEM_LIB=y + +# LTE connectivity with network connection manager +CONFIG_NRF_MODEM_LIB_NET_IF=y +CONFIG_NRF_MODEM_LIB_NET_IF_AUTO_START=y +CONFIG_NRF_MODEM_LIB_NET_IF_AUTO_CONNECT=y +CONFIG_NET_CONNECTION_MANAGER=y +CONFIG_NET_CONNECTION_MANAGER_MONITOR_STACK_SIZE=1024 + +# Increased sysworkq size, due to LTE connectivity +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 + +# Disable options y-selected by NCS for no good reason +CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED=n +CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED=n + +# MbedTLS configuration to support p-384 curve. These options +# enable using the MbedTLS built-in support for operations not +# supported by the default nRF Oberon crypto backend +CONFIG_NORDIC_SECURITY_BACKEND=n +CONFIG_MBEDTLS_LEGACY_CRYPTO_C=y + +# Generate MCUboot compatible images +CONFIG_BOOTLOADER_MCUBOOT=y + +CONFIG_LTE_LC_NEIGHBOR_CELL_MEAS_MODULE=y diff --git a/examples/zephyr/location/socs/nrf9160_ns.conf b/examples/zephyr/location/socs/nrf9160_ns.conf new file mode 100644 index 000000000..cdc20bd73 --- /dev/null +++ b/examples/zephyr/location/socs/nrf9160_ns.conf @@ -0,0 +1,38 @@ +# General config +CONFIG_HEAP_MEM_POOL_SIZE=4096 +CONFIG_NEWLIB_LIBC=y + +# Networking +CONFIG_NET_SOCKETS_OFFLOAD=y +CONFIG_NET_IPV6=y +CONFIG_NET_IPV6_NBR_CACHE=n +CONFIG_NET_IPV6_MLD=n + +# Increase native TLS socket implementation, so that it is chosen instead of +# offloaded nRF91 sockets +CONFIG_NET_SOCKETS_TLS_PRIORITY=35 + +# Modem library +CONFIG_NRF_MODEM_LIB=y + +# LTE connectivity with network connection manager +CONFIG_NRF_MODEM_LIB_NET_IF=y +CONFIG_NRF_MODEM_LIB_NET_IF_AUTO_START=y +CONFIG_NRF_MODEM_LIB_NET_IF_AUTO_CONNECT=y +CONFIG_NET_CONNECTION_MANAGER=y +CONFIG_NET_CONNECTION_MANAGER_MONITOR_STACK_SIZE=1024 + +# Increased sysworkq size, due to LTE connectivity +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 + +# Disable options y-selected by NCS for no good reason +CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED=n +CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED=n + +# MbedTLS configuration to support p-384 curve. These options +# enable using the MbedTLS built-in support for operations not +# supported by the default nRF Oberon crypto backend +CONFIG_NORDIC_SECURITY_BACKEND=n +CONFIG_MBEDTLS_LEGACY_CRYPTO_C=y + +CONFIG_LTE_LC_NEIGHBOR_CELL_MEAS_MODULE=y diff --git a/examples/zephyr/location/src/cellular_nrf91.c b/examples/zephyr/location/src/cellular_nrf91.c new file mode 100644 index 000000000..b87680e10 --- /dev/null +++ b/examples/zephyr/location/src/cellular_nrf91.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2025 Golioth, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_REGISTER(cellular_nrf91); + +#include + +#include "cellular.h" + +/* Indicates when individual ncellmeas operation is completed. This is internal to this file. */ +static K_SEM_DEFINE(scan_cellular_sem_ncellmeas_evt, 0, 1); + +static enum lte_lc_lte_mode lte_mode; +static struct lte_lc_ncell neighbor_cells[CONFIG_LTE_NEIGHBOR_CELLS_MAX]; +static struct lte_lc_cells_info scan_cellular_info = { + .neighbor_cells = neighbor_cells, +}; + +static const char *lte_mode_to_str(enum lte_lc_lte_mode mode) +{ + switch (mode) + { + case LTE_LC_LTE_MODE_LTEM: + return "LTEM"; + case LTE_LC_LTE_MODE_NBIOT: + return "NBIOT"; + case LTE_LC_LTE_MODE_NONE: + return "NONE"; + } + + return "unknown"; +} + +static void lte_ind_handler(const struct lte_lc_evt *const evt) +{ + switch (evt->type) + { + case LTE_LC_EVT_LTE_MODE_UPDATE: + LOG_INF("LTE mode: %s", lte_mode_to_str(evt->lte_mode)); + lte_mode = evt->lte_mode; + break; + case LTE_LC_EVT_CELL_UPDATE: + LOG_INF("LTE cell changed: Cell ID: %d, Tracking area: %d", + evt->cell.id, + evt->cell.tac); + break; + case LTE_LC_EVT_NEIGHBOR_CELL_MEAS: + LOG_INF("LTE current cell: MCC %d, MNC %d, Cell ID: %d, Tracking area: %d", + evt->cells_info.current_cell.mcc, + evt->cells_info.current_cell.mnc, + evt->cells_info.current_cell.id, + evt->cells_info.current_cell.tac); + + LOG_INF( + "Cell measurements results received: " + "ncells_count=%d, gci_cells_count=%d, current_cell.id=0x%08X", + evt->cells_info.ncells_count, + evt->cells_info.gci_cells_count, + evt->cells_info.current_cell.id); + + if (evt->cells_info.current_cell.id != LTE_LC_CELL_EUTRAN_ID_INVALID) + { + /* Copy current cell information. We are seeing this is not set for GCI + * search sometimes but we have it for the previous normal neighbor search. + */ + memcpy(&scan_cellular_info.current_cell, + &evt->cells_info.current_cell, + sizeof(struct lte_lc_cell)); + } + + /* Copy neighbor cell information if present */ + if (evt->cells_info.ncells_count > 0 && evt->cells_info.neighbor_cells) + { + memcpy(scan_cellular_info.neighbor_cells, + evt->cells_info.neighbor_cells, + sizeof(struct lte_lc_ncell) * evt->cells_info.ncells_count); + + scan_cellular_info.ncells_count = evt->cells_info.ncells_count; + } + else + { + LOG_DBG("No neighbor cell information from modem"); + } + + /* Copy surrounding cell information if present */ + if (evt->cells_info.gci_cells_count > 0 && evt->cells_info.gci_cells) + { + memcpy(scan_cellular_info.gci_cells, + evt->cells_info.gci_cells, + sizeof(struct lte_lc_cell) * evt->cells_info.gci_cells_count); + + scan_cellular_info.gci_cells_count = evt->cells_info.gci_cells_count; + } + else + { + LOG_DBG("No surrounding cell information from modem"); + } + + k_sem_give(&scan_cellular_sem_ncellmeas_evt); + break; + + default: + break; + } +} + +int cellular_info_get(struct golioth_cellular_info *infos, + size_t num_max_infos, + size_t *num_returned_infos) +{ + struct lte_lc_ncellmeas_params params = { + .search_type = LTE_LC_NEIGHBOR_SEARCH_TYPE_EXTENDED_LIGHT, + }; + struct lte_lc_cell *current = &scan_cellular_info.current_cell; + int err; + + err = lte_lc_neighbor_cell_measurement(¶ms); + if (err) + { + return err; + } + + err = k_sem_take(&scan_cellular_sem_ncellmeas_evt, K_FOREVER); + if (err) + { + /* Semaphore was reset so stop search procedure */ + err = 0; + } + + switch (lte_mode) + { + case LTE_LC_LTE_MODE_LTEM: + infos[0].type = GOLIOTH_CELLULAR_TYPE_LTECATM; + break; + case LTE_LC_LTE_MODE_NBIOT: + infos[0].type = GOLIOTH_CELLULAR_TYPE_NBIOT; + break; + default: + *num_returned_infos = 0; + return 0; + } + + infos[0].mcc = current->mcc; + infos[0].mnc = current->mnc; + infos[0].id = current->id; + + *num_returned_infos = 1; + + return 0; +} + +static int cellular_nrf91_init(void) +{ + lte_lc_register_handler(lte_ind_handler); + + return 0; +} + +SYS_INIT(cellular_nrf91_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);