diff --git a/applications/asset_tracker_v2/doc/location_module.rst b/applications/asset_tracker_v2/doc/location_module.rst index 35d0e273511..c88e715bea2 100644 --- a/applications/asset_tracker_v2/doc/location_module.rst +++ b/applications/asset_tracker_v2/doc/location_module.rst @@ -88,6 +88,7 @@ Wi-Fi positioning has the following limitations: The Wi-Fi configuration uses both flash and SRAM extensively. You can configure the number of scan results with the :kconfig:option:`CONFIG_LOCATION_METHOD_WIFI_SCANNING_RESULTS_MAX_CNT` Kconfig option to reduce SRAM consumption. + Align the :kconfig:option:`CONFIG_WIFI_MGMT_SCAN_MAX_BSS_CNT` Kconfig option with :kconfig:option:`CONFIG_LOCATION_METHOD_WIFI_SCANNING_RESULTS_MAX_CNT`. You can also change the value of the :kconfig:option:`CONFIG_HEAP_MEM_POOL_SIZE` Kconfig option. Module internals @@ -100,7 +101,19 @@ All incoming events from other modules are handled in the context of the Applica The :ref:`lib_location` library handles cellular and Wi-Fi positioning together when the location request method list has them next to each other. This means that LTE neighbor cell measurements and Wi-Fi scanning results are combined into the same :c:enum:`LOCATION_EVT_CLOUD_LOCATION_EXT_REQUEST` event. -The location module responds to the :ref:`lib_location` library with unknown location resolution, because it does not request the location back from cloud service. + +The location module sends the cellular and Wi-Fi positioning request forward with :c:enum:`LOCATION_MODULE_EVT_CLOUD_LOCATION_DATA_READY` event, +and waits for an event from the cloud module on the result of the positioning. +This result is forwarded to the :ref:`lib_location` library. +Depending on the cloud integration, positioning result from the cloud service might be requested or not. +The result must be indicated with one of the following events: + +* :c:enum:`CLOUD_EVT_CLOUD_LOCATION_RECEIVED`: Location has been resolved successfully. + The :ref:`lib_location` library does not continue to use other positioning methods and the location request completes quickly. +* :c:enum:`CLOUD_EVT_CLOUD_LOCATION_ERROR`: Location resolution failed. + The :ref:`lib_location` library performs a fallback to the next positioning method in the priority list. +* :c:enum:`CLOUD_EVT_CLOUD_LOCATION_UNKNOWN`: Location resolution result is unknown, that is, cloud integration is not capable of or configured to resolve it. + The :ref:`lib_location` library performs a fallback to the next positioning method. Configuration options ********************* diff --git a/applications/asset_tracker_v2/overlay-nrf7002ek-wifi-scan-only.conf b/applications/asset_tracker_v2/overlay-nrf7002ek-wifi-scan-only.conf index 0307832e43c..beefc264ffd 100644 --- a/applications/asset_tracker_v2/overlay-nrf7002ek-wifi-scan-only.conf +++ b/applications/asset_tracker_v2/overlay-nrf7002ek-wifi-scan-only.conf @@ -20,6 +20,8 @@ CONFIG_DK_LIBRARY=n # Actual configs for the Wi-Fi CONFIG_WIFI=y CONFIG_WIFI_NRF700X=y +# Align this with CONFIG_LOCATION_METHOD_WIFI_SCANNING_RESULTS_MAX_CNT +CONFIG_WIFI_MGMT_SCAN_MAX_BSS_CNT=30 # Wi-Fi location CONFIG_LOCATION_METHOD_WIFI=y @@ -27,6 +29,7 @@ CONFIG_LOCATION_REQUEST_DEFAULT_METHOD_FIRST_GNSS=y CONFIG_LOCATION_REQUEST_DEFAULT_METHOD_SECOND_WIFI=y CONFIG_LOCATION_REQUEST_DEFAULT_METHOD_THIRD_CELLULAR=y +# Align this with CONFIG_WIFI_MGMT_SCAN_MAX_BSS_CNT CONFIG_LOCATION_METHOD_WIFI_SCANNING_RESULTS_MAX_CNT=30 CONFIG_LOCATION_WORKQUEUE_STACK_SIZE=8192 # Needed to handle more scan results diff --git a/applications/asset_tracker_v2/src/cloud/aws_iot_integration.c b/applications/asset_tracker_v2/src/cloud/aws_iot_integration.c index 5d44534d26b..b4c2d15906e 100644 --- a/applications/asset_tracker_v2/src/cloud/aws_iot_integration.c +++ b/applications/asset_tracker_v2/src/cloud/aws_iot_integration.c @@ -507,6 +507,11 @@ int cloud_wrap_cloud_location_send(char *buf, size_t len, bool ack, uint32_t id) return 0; } +bool cloud_wrap_cloud_location_response_wait(void) +{ + return false; +} + int cloud_wrap_agps_request_send(char *buf, size_t len, bool ack, uint32_t id) { int err; diff --git a/applications/asset_tracker_v2/src/cloud/azure_iot_hub_integration.c b/applications/asset_tracker_v2/src/cloud/azure_iot_hub_integration.c index 16e21c8b547..52821e7513f 100644 --- a/applications/asset_tracker_v2/src/cloud/azure_iot_hub_integration.c +++ b/applications/asset_tracker_v2/src/cloud/azure_iot_hub_integration.c @@ -509,6 +509,11 @@ int cloud_wrap_cloud_location_send(char *buf, size_t len, bool ack, uint32_t id) return 0; } +bool cloud_wrap_cloud_location_response_wait(void) +{ + return false; +} + int cloud_wrap_agps_request_send(char *buf, size_t len, bool ack, uint32_t id) { int err; diff --git a/applications/asset_tracker_v2/src/cloud/cloud_codec/aws_iot/aws_iot_codec.c b/applications/asset_tracker_v2/src/cloud/cloud_codec/aws_iot/aws_iot_codec.c index 97c8c454cf3..8fb63c8bd12 100644 --- a/applications/asset_tracker_v2/src/cloud/cloud_codec/aws_iot/aws_iot_codec.c +++ b/applications/asset_tracker_v2/src/cloud/cloud_codec/aws_iot/aws_iot_codec.c @@ -75,26 +75,24 @@ int cloud_codec_encode_cloud_location( return -ENOMEM; } - if (!cloud_location->neighbor_cells_valid) { - err = -ENODATA; - goto exit; - } - - err = json_common_neighbor_cells_data_add(root_obj, &cloud_location->neighbor_cells, - JSON_COMMON_ADD_DATA_TO_OBJECT); - if (err) { - goto exit; + if (cloud_location->neighbor_cells_valid) { + err = json_common_neighbor_cells_data_add(root_obj, &cloud_location->neighbor_cells, + JSON_COMMON_ADD_DATA_TO_OBJECT); + if (err) { + goto exit; + } } #if defined(CONFIG_LOCATION_METHOD_WIFI) - err = json_common_wifi_ap_data_add(root_obj, &cloud_location->wifi_access_points, - JSON_COMMON_ADD_DATA_TO_OBJECT); - if (err) { - goto exit; + if (cloud_location->wifi_access_points_valid) { + err = json_common_wifi_ap_data_add(root_obj, &cloud_location->wifi_access_points, + JSON_COMMON_ADD_DATA_TO_OBJECT); + if (err) { + goto exit; + } } #endif - buffer = cJSON_PrintUnformatted(root_obj); if (buffer == NULL) { LOG_ERR("Failed to allocate memory for JSON string"); @@ -115,6 +113,12 @@ int cloud_codec_encode_cloud_location( return err; } +int cloud_codec_decode_cloud_location(const char *input, size_t input_len, + struct location_data *location) +{ + return -ENOTSUP; +} + int cloud_codec_encode_agps_request(struct cloud_codec_data *output, struct cloud_data_agps_request *agps_request) { diff --git a/applications/asset_tracker_v2/src/cloud/cloud_codec/azure_iot_hub/azure_iot_hub_codec.c b/applications/asset_tracker_v2/src/cloud/cloud_codec/azure_iot_hub/azure_iot_hub_codec.c index 85e34ca76df..6c657b43779 100644 --- a/applications/asset_tracker_v2/src/cloud/cloud_codec/azure_iot_hub/azure_iot_hub_codec.c +++ b/applications/asset_tracker_v2/src/cloud/cloud_codec/azure_iot_hub/azure_iot_hub_codec.c @@ -45,22 +45,21 @@ int cloud_codec_encode_cloud_location( return -ENOMEM; } - if (!cloud_location->neighbor_cells_valid) { - err = -ENODATA; - goto exit; - } - - err = json_common_neighbor_cells_data_add(root_obj, &cloud_location->neighbor_cells, - JSON_COMMON_ADD_DATA_TO_OBJECT); - if (err) { - goto exit; + if (cloud_location->neighbor_cells_valid) { + err = json_common_neighbor_cells_data_add(root_obj, &cloud_location->neighbor_cells, + JSON_COMMON_ADD_DATA_TO_OBJECT); + if (err) { + goto exit; + } } #if defined(CONFIG_LOCATION_METHOD_WIFI) - err = json_common_wifi_ap_data_add(root_obj, &cloud_location->wifi_access_points, - JSON_COMMON_ADD_DATA_TO_OBJECT); - if (err) { - goto exit; + if (cloud_location->wifi_access_points_valid) { + err = json_common_wifi_ap_data_add(root_obj, &cloud_location->wifi_access_points, + JSON_COMMON_ADD_DATA_TO_OBJECT); + if (err) { + goto exit; + } } #endif @@ -84,6 +83,13 @@ int cloud_codec_encode_cloud_location( return err; } + +int cloud_codec_decode_cloud_location(const char *input, size_t input_len, + struct location_data *location) +{ + return -ENOTSUP; +} + int cloud_codec_encode_agps_request(struct cloud_codec_data *output, struct cloud_data_agps_request *agps_request) { diff --git a/applications/asset_tracker_v2/src/cloud/cloud_codec/cloud_codec.h b/applications/asset_tracker_v2/src/cloud/cloud_codec/cloud_codec.h index 523cdfc6576..89d98daf08b 100644 --- a/applications/asset_tracker_v2/src/cloud/cloud_codec/cloud_codec.h +++ b/applications/asset_tracker_v2/src/cloud/cloud_codec/cloud_codec.h @@ -15,6 +15,9 @@ #include #include #include +#if defined(CONFIG_LOCATION) +#include +#endif #include #include @@ -319,6 +322,25 @@ int cloud_codec_encode_cloud_location( struct cloud_codec_data *output, struct cloud_data_cloud_location *cloud_location); +/* Foward declaration */ +struct location_data; + +/** + * @brief Decode received cloud location data. + * + * @param[in] input String buffer with encoded cloud location data. + * @param[in] input_len Length of input. + * @param[out] location Storage for the decoded cloud location data. + * + * @retval 0 on success. + * @retval -EFAULT if cloud returned with error to the location request. + * @retval -ENOMEM if codec could not allocate memory. + * @retval -ENOTSUP if the function is not supported by the backend. + * @return 0 on success or negative error value on failure. + */ +int cloud_codec_decode_cloud_location(const char *input, size_t input_len, + struct location_data *location); + /** * @brief Encode cloud codec A-GPS request. * diff --git a/applications/asset_tracker_v2/src/cloud/cloud_codec/lwm2m/lwm2m_codec.c b/applications/asset_tracker_v2/src/cloud/cloud_codec/lwm2m/lwm2m_codec.c index 5966eee47a5..41ea5c8b6db 100644 --- a/applications/asset_tracker_v2/src/cloud/cloud_codec/lwm2m/lwm2m_codec.c +++ b/applications/asset_tracker_v2/src/cloud/cloud_codec/lwm2m/lwm2m_codec.c @@ -86,7 +86,7 @@ int cloud_codec_init(struct cloud_data_cfg *cfg, cloud_codec_evt_handler_t event } int cloud_codec_encode_cloud_location(struct cloud_codec_data *output, - struct cloud_data_cloud_location *cloud_location) + struct cloud_data_cloud_location *cloud_location) { ARG_UNUSED(output); @@ -101,6 +101,12 @@ int cloud_codec_encode_cloud_location(struct cloud_codec_data *output, return 0; } +int cloud_codec_decode_cloud_location(const char *input, size_t input_len, + struct location_data *location) +{ + return -ENOTSUP; +} + int cloud_codec_encode_agps_request(struct cloud_codec_data *output, struct cloud_data_agps_request *agps_request) { diff --git a/applications/asset_tracker_v2/src/cloud/cloud_codec/nrf_cloud/nrf_cloud_codec.c b/applications/asset_tracker_v2/src/cloud/cloud_codec/nrf_cloud/nrf_cloud_codec.c index b12675fa5db..cb96a81eea7 100644 --- a/applications/asset_tracker_v2/src/cloud/cloud_codec/nrf_cloud/nrf_cloud_codec.c +++ b/applications/asset_tracker_v2/src/cloud/cloud_codec/nrf_cloud/nrf_cloud_codec.c @@ -768,6 +768,29 @@ int cloud_codec_encode_cloud_location( return -ENOTSUP; } +int cloud_codec_decode_cloud_location(const char *input, size_t input_len, + struct location_data *location) +{ + ARG_UNUSED(input_len); + + int err; + struct nrf_cloud_location_result result; + + err = nrf_cloud_location_process(input, &result); + if (err == 0) { +#if defined(CONFIG_LOCATION) + location->latitude = result.lat; + location->longitude = result.lon; + location->accuracy = result.unc; +#endif + /* Date and time are not filled because they are not used anyway */ + } else if (err == 1) { + /* Unexpected response from cloud is treated similarly to error from cloud */ + err = -EFAULT; + } + return err; +} + int cloud_codec_encode_agps_request(struct cloud_codec_data *output, struct cloud_data_agps_request *agps_request) { diff --git a/applications/asset_tracker_v2/src/cloud/cloud_wrapper.h b/applications/asset_tracker_v2/src/cloud/cloud_wrapper.h index 9aca0a59ec5..f093f22eed2 100644 --- a/applications/asset_tracker_v2/src/cloud/cloud_wrapper.h +++ b/applications/asset_tracker_v2/src/cloud/cloud_wrapper.h @@ -57,6 +57,10 @@ enum cloud_wrap_event_type { * Payload is of type @ref cloud_wrap_event_data. */ CLOUD_WRAP_EVT_PGPS_DATA_RECEIVED, + /** Data received from cloud integration layer. + * Payload is of type @ref cloud_wrap_event_data. + */ + CLOUD_WRAP_EVT_CLOUD_LOCATION_RESULT_RECEIVED, /** Reboot request received from cloud. */ CLOUD_WRAP_EVT_REBOOT_REQUEST, /** Request to connect to LTE. */ @@ -210,6 +214,14 @@ int cloud_wrap_ui_send(char *buf, size_t len, bool ack, uint32_t id, */ int cloud_wrap_cloud_location_send(char *buf, size_t len, bool ack, uint32_t id); +/** + * @brief Indicates whether cloud implementation can and is configured to return the resolved + * location back to the device. + * + * @return Indicates whether resolved location is sent back to the device. + */ +bool cloud_wrap_cloud_location_response_wait(void); + /** * @brief Send A-GPS request to cloud. * diff --git a/applications/asset_tracker_v2/src/cloud/nrf_cloud_integration.c b/applications/asset_tracker_v2/src/cloud/nrf_cloud_integration.c index 064a1ae1cfd..269c4331c20 100644 --- a/applications/asset_tracker_v2/src/cloud/nrf_cloud_integration.c +++ b/applications/asset_tracker_v2/src/cloud/nrf_cloud_integration.c @@ -233,7 +233,16 @@ static void nrf_cloud_event_handler(const struct nrf_cloud_evt *evt) notify = true; break; case NRF_CLOUD_EVT_RX_DATA_LOCATION: - LOG_DBG("NRF_CLOUD_EVT_RX_DATA_LOCATION"); + LOG_DBG("NRF_CLOUD_EVT_RX_DATA_LOCATION: %s", (char *)evt->data.ptr); + + /* If GNSS is NOT the first priority, we'll handle the cloud response */ + if (!IS_ENABLED(CONFIG_LOCATION_REQUEST_DEFAULT_METHOD_FIRST_GNSS)) { + cloud_wrap_evt.type = CLOUD_WRAP_EVT_CLOUD_LOCATION_RESULT_RECEIVED; + cloud_wrap_evt.data.buf = (char *)evt->data.ptr; + cloud_wrap_evt.data.len = evt->data.len; + + notify = true; + } break; case NRF_CLOUD_EVT_USER_ASSOCIATION_REQUEST: LOG_WRN("NRF_CLOUD_EVT_USER_ASSOCIATION_REQUEST"); @@ -440,6 +449,16 @@ int cloud_wrap_cloud_location_send(char *buf, size_t len, bool ack, uint32_t id) return 0; } +bool cloud_wrap_cloud_location_response_wait(void) +{ + /* If GNSS is the first priority, then we can ignore the location response from the cloud */ + if (IS_ENABLED(CONFIG_LOCATION_REQUEST_DEFAULT_METHOD_FIRST_GNSS)) { + return false; + } else { + return true; + } +} + #if defined(CONFIG_LOCATION_METHOD_WIFI) int cloud_wrap_wifi_access_points_send(char *buf, size_t len, bool ack, uint32_t id) { diff --git a/applications/asset_tracker_v2/src/events/cloud_module_event.c b/applications/asset_tracker_v2/src/events/cloud_module_event.c index 2db589fc7a9..2b19ab03a38 100644 --- a/applications/asset_tracker_v2/src/events/cloud_module_event.c +++ b/applications/asset_tracker_v2/src/events/cloud_module_event.c @@ -32,6 +32,12 @@ static char *get_evt_type_str(enum cloud_module_event_type type) return "CLOUD_EVT_REBOOT_REQUEST"; case CLOUD_EVT_CONFIG_RECEIVED: return "CLOUD_EVT_CONFIG_RECEIVED"; + case CLOUD_EVT_CLOUD_LOCATION_RECEIVED: + return "CLOUD_EVT_CLOUD_LOCATION_RECEIVED"; + case CLOUD_EVT_CLOUD_LOCATION_ERROR: + return "CLOUD_EVT_CLOUD_LOCATION_ERROR"; + case CLOUD_EVT_CLOUD_LOCATION_UNKNOWN: + return "CLOUD_EVT_CLOUD_LOCATION_UNKNOWN"; case CLOUD_EVT_CONFIG_EMPTY: return "CLOUD_EVT_CONFIG_EMPTY"; case CLOUD_EVT_DATA_SEND_QOS: diff --git a/applications/asset_tracker_v2/src/events/cloud_module_event.h b/applications/asset_tracker_v2/src/events/cloud_module_event.h index 4a79060701f..8845e1b3904 100644 --- a/applications/asset_tracker_v2/src/events/cloud_module_event.h +++ b/applications/asset_tracker_v2/src/events/cloud_module_event.h @@ -16,6 +16,9 @@ #include #include #include +#if defined(CONFIG_LOCATION) +#include +#endif #include "cloud/cloud_codec/cloud_codec.h" @@ -67,6 +70,17 @@ enum cloud_module_event_type { */ CLOUD_EVT_CONFIG_RECEIVED, + /** Cloud location data has been received from cloud. + * The payload associated with this event is of type @ref location_data. + */ + CLOUD_EVT_CLOUD_LOCATION_RECEIVED, + + /** Cloud location response has been received but location could not be resolved. */ + CLOUD_EVT_CLOUD_LOCATION_ERROR, + + /** Cloud location resolution is unknown. */ + CLOUD_EVT_CLOUD_LOCATION_UNKNOWN, + /** An empty device configuration has been received from cloud. */ CLOUD_EVT_CONFIG_EMPTY, @@ -115,13 +129,17 @@ struct cloud_module_event { enum cloud_module_event_type type; union { - /** Variable that contains a new configuration received from the cloud service. */ + /** New configuration received from the cloud service. */ struct cloud_data_cfg config; - /** Variable that contains data that was attempted to be sent. Could be used +#if defined(CONFIG_LOCATION) + /** Cloud location data received from the cloud service. */ + struct location_data cloud_location; +#endif + /** Data that was attempted to be sent. Could be used * to free allocated data post transmission. */ struct cloud_module_data_ack ack; - /** Variable that contains the message that should be sent to cloud. */ + /** The message that should be sent to cloud. */ struct qos_data message; /** Module ID, used when acknowledging shutdown requests. */ uint32_t id; diff --git a/applications/asset_tracker_v2/src/modules/cloud_module.c b/applications/asset_tracker_v2/src/modules/cloud_module.c index 502d4cbded9..1bd29942a7e 100644 --- a/applications/asset_tracker_v2/src/modules/cloud_module.c +++ b/applications/asset_tracker_v2/src/modules/cloud_module.c @@ -324,6 +324,47 @@ static void pgps_data_handle(const uint8_t *buf, const size_t len) #endif /* CONFIG_NRF_CLOUD_PGPS */ } +#if defined(CONFIG_LOCATION) +void submit_cloud_module_event( + enum cloud_module_event_type type, + struct location_data *location, + int error) +{ + struct cloud_module_event *cloud_module_event = new_cloud_module_event(); + + __ASSERT(cloud_module_event, "Not enough heap left to allocate event"); + + cloud_module_event->type = type; + if (type == CLOUD_EVT_CLOUD_LOCATION_RECEIVED) { + cloud_module_event->data.cloud_location = *location; + } else if (type == CLOUD_EVT_ERROR) { + cloud_module_event->data.err = error; + } + + APP_EVENT_SUBMIT(cloud_module_event); +} +#endif + +static void cloud_location_data_handle(uint8_t *buf, const size_t len) +{ +#if defined(CONFIG_LOCATION) + int err; + struct location_data location; + + err = cloud_codec_decode_cloud_location(buf, len, &location); + if (err == 0) { + submit_cloud_module_event(CLOUD_EVT_CLOUD_LOCATION_RECEIVED, &location, 0); + } else if (err == -EFAULT) { + submit_cloud_module_event(CLOUD_EVT_CLOUD_LOCATION_ERROR, NULL, err); + } else if (err == -ENOTSUP) { + submit_cloud_module_event(CLOUD_EVT_CLOUD_LOCATION_UNKNOWN, NULL, 0); + } else { + LOG_ERR("Decoding of cloud location response, error: %d", err); + SEND_ERROR(cloud, CLOUD_EVT_ERROR, err); + } +#endif +} + static void cloud_wrap_event_handler(const struct cloud_wrap_event *const evt) { switch (evt->type) { @@ -354,6 +395,10 @@ static void cloud_wrap_event_handler(const struct cloud_wrap_event *const evt) LOG_DBG("CLOUD_WRAP_EVT_AGPS_DATA_RECEIVED"); agps_data_handle(evt->data.buf, evt->data.len); break; + case CLOUD_WRAP_EVT_CLOUD_LOCATION_RESULT_RECEIVED: + LOG_DBG("CLOUD_WRAP_EVT_CLOUD_LOCATION_RESULT_RECEIVED"); + cloud_location_data_handle(evt->data.buf, evt->data.len); + break; case CLOUD_WRAP_EVT_USER_ASSOCIATION_REQUEST: { LOG_DBG("CLOUD_WRAP_EVT_USER_ASSOCIATION_REQUEST"); @@ -818,6 +863,17 @@ static void on_sub_state_cloud_connected(struct cloud_msg_data *msg) CLOUD_LOCATION, QOS_FLAG_RELIABILITY_ACK_REQUIRED, true); + + /* Check if the configured cloud service will return the resolved location back + * to the device. If it does not, indicate that location result is unknown. + */ + if (!cloud_wrap_cloud_location_response_wait()) { + struct cloud_module_event *cloud_module_event = new_cloud_module_event(); + + __ASSERT(cloud_module_event, "Not enough heap left to allocate event"); + cloud_module_event->type = CLOUD_EVT_CLOUD_LOCATION_UNKNOWN; + APP_EVENT_SUBMIT(cloud_module_event); + } } if (IS_EVENT(msg, cloud, CLOUD_EVT_DATA_SEND_QOS)) { diff --git a/applications/asset_tracker_v2/src/modules/data_module.c b/applications/asset_tracker_v2/src/modules/data_module.c index eaba80cfeaa..5878ad1ac47 100644 --- a/applications/asset_tracker_v2/src/modules/data_module.c +++ b/applications/asset_tracker_v2/src/modules/data_module.c @@ -855,10 +855,15 @@ static void data_send_work_fn(struct k_work *work) static void requested_data_status_set(enum app_module_data_type data_type) { if (!k_work_delayable_is_pending(&data_send_work)) { - /* If the data_send_work is not pending it means that the module has already - * triggered an data encode/send. - */ - LOG_DBG("Data already encoded and sent, abort."); + if (data_type == APP_DATA_LOCATION) { + /* We may get location related data when a fallback to next method occurs */ + data_send_work_fn(NULL); + } else { + /* If the data_send_work is not pending it means that the module has already + * triggered an data encode/send. + */ + LOG_DBG("Data already encoded and sent, abort."); + } return; } @@ -1411,6 +1416,8 @@ static void on_all_states(struct data_msg_data *msg) } if (IS_EVENT(msg, location, LOCATION_MODULE_EVT_CLOUD_LOCATION_DATA_READY)) { + cloud_location.neighbor_cells_valid = false; + cloud_location.neighbor_cells.queued = false; if (msg->module.location.data.cloud_location.neighbor_cells_valid) { BUILD_ASSERT(sizeof(cloud_location.neighbor_cells.cell_data) == sizeof(msg->module.location.data.cloud_location @@ -1435,6 +1442,9 @@ static void on_all_states(struct data_msg_data *msg) sizeof(cloud_location.neighbor_cells.neighbor_cells)); } #if defined(CONFIG_LOCATION_METHOD_WIFI) + cloud_location.wifi_access_points_valid = false; + cloud_location.wifi_access_points.queued = false; + if (msg->module.location.data.cloud_location.wifi_access_points_valid) { BUILD_ASSERT(sizeof(cloud_location.wifi_access_points.ap_info) == sizeof(msg->module.location.data.cloud_location diff --git a/applications/asset_tracker_v2/src/modules/location_module.c b/applications/asset_tracker_v2/src/modules/location_module.c index 4f1c8c56a2a..4f1c9a00724 100644 --- a/applications/asset_tracker_v2/src/modules/location_module.c +++ b/applications/asset_tracker_v2/src/modules/location_module.c @@ -21,6 +21,7 @@ #include "events/data_module_event.h" #include "events/util_module_event.h" #include "events/modem_module_event.h" +#include "events/cloud_module_event.h" #include LOG_MODULE_REGISTER(MODULE, CONFIG_LOCATION_MODULE_LOG_LEVEL); @@ -43,6 +44,7 @@ struct location_msg_data { struct data_module_event data; struct util_module_event util; struct modem_module_event modem; + struct cloud_module_event cloud; struct location_module_event location; } module; }; @@ -193,6 +195,15 @@ static bool app_event_handler(const struct app_event_header *aeh) message_handler(&msg); } + + if (is_cloud_module_event(aeh)) { + struct cloud_module_event *event = cast_cloud_module_event(aeh); + struct location_msg_data msg = { + .module.cloud = *event + }; + + message_handler(&msg); + } return false; } @@ -328,6 +339,7 @@ static void send_cloud_location_update(const struct location_data_cloud *cloud_l struct location_module_event *evt = new_location_module_event(); struct location_module_neighbor_cells *evt_ncells = &evt->data.cloud_location.neighbor_cells; + evt->data.cloud_location.neighbor_cells_valid = false; if (cloud_location_info->cell_data != NULL) { BUILD_ASSERT(sizeof(evt_ncells->cell_data) == sizeof(struct lte_lc_cells_info)); @@ -357,6 +369,7 @@ static void send_cloud_location_update(const struct location_data_cloud *cloud_l } #if defined(CONFIG_LOCATION_METHOD_WIFI) + evt->data.cloud_location.wifi_access_points_valid = false; if (cloud_location_info->wifi_data != NULL) { BUILD_ASSERT(sizeof(evt->data.cloud_location.wifi_access_points.ap_info) >= sizeof(struct wifi_scan_result) * @@ -488,7 +501,6 @@ void location_event_handler(const struct location_event_data *event_data) case LOCATION_EVT_CLOUD_LOCATION_EXT_REQUEST: LOG_DBG("Getting cloud location request"); send_cloud_location_update(&event_data->cloud_location_request); - location_cloud_location_ext_result_set(LOCATION_EXT_RESULT_UNKNOWN, NULL); break; #endif @@ -545,6 +557,21 @@ static void on_state_running_location_search(struct location_msg_data *msg) sub_state_set(SUB_STATE_IDLE); } + if (IS_EVENT(msg, cloud, CLOUD_EVT_CLOUD_LOCATION_RECEIVED)) { +#if defined(CONFIG_LOCATION) + location_cloud_location_ext_result_set( + LOCATION_EXT_RESULT_SUCCESS, + &msg->module.cloud.data.cloud_location); +#endif + } + if (IS_EVENT(msg, cloud, CLOUD_EVT_CLOUD_LOCATION_ERROR)) { + location_cloud_location_ext_result_set(LOCATION_EXT_RESULT_ERROR, NULL); + } + + if (IS_EVENT(msg, cloud, CLOUD_EVT_CLOUD_LOCATION_UNKNOWN)) { + location_cloud_location_ext_result_set(LOCATION_EXT_RESULT_UNKNOWN, NULL); + } + if (IS_EVENT(msg, app, APP_EVT_DATA_GET)) { if (!location_data_requested(msg->module.app.data_list, msg->module.app.count)) { return; @@ -638,4 +665,5 @@ APP_EVENT_SUBSCRIBE_EARLY(MODULE, app_module_event); APP_EVENT_SUBSCRIBE(MODULE, data_module_event); APP_EVENT_SUBSCRIBE(MODULE, util_module_event); APP_EVENT_SUBSCRIBE(MODULE, modem_module_event); +APP_EVENT_SUBSCRIBE(MODULE, cloud_module_event); APP_EVENT_SUBSCRIBE(MODULE, location_module_event); diff --git a/applications/asset_tracker_v2/tests/location_module/src/location_module_test.c b/applications/asset_tracker_v2/tests/location_module/src/location_module_test.c index 89f69cc2a97..38a219bf379 100644 --- a/applications/asset_tracker_v2/tests/location_module/src/location_module_test.c +++ b/applications/asset_tracker_v2/tests/location_module/src/location_module_test.c @@ -18,6 +18,7 @@ #include "location_module_event.h" #include "data_module_event.h" #include "modem_module_event.h" +#include "cloud_module_event.h" extern struct event_listener __event_listener_location_module; @@ -28,9 +29,20 @@ static struct app_module_event app_module_event_memory; static struct modem_module_event modem_module_event_memory; static struct location_module_event location_module_event_memory; static struct data_module_event data_module_event_memory; +static struct cloud_module_event cloud_module_event_memory; #define LOCATION_MODULE_EVT_HANDLER(aeh) __event_listener_location_module.notification(aeh) +/* Macro used to submit module events of a specific type to the Location module. */ +#define TEST_SEND_EVENT(_mod, _type, _event) \ + __cmock_app_event_manager_alloc_ExpectAnyArgsAndReturn(&_mod##_module_event_memory); \ + __cmock_app_event_manager_free_ExpectAnyArgs(); \ + _event = new_##_mod##_module_event(); \ + _event->type = _type; \ + TEST_ASSERT_FALSE(LOCATION_MODULE_EVT_HANDLER( \ + (struct app_event_header *)_event)); \ + app_event_manager_free(_event) + /* location_event_handler() is implemented in location module and we'll call it directly * to fake received location library events. */ @@ -68,6 +80,7 @@ struct event_type __event_type_app_module_event; struct event_type __event_type_data_module_event; struct event_type __event_type_util_module_event; struct event_type __event_type_modem_module_event; +struct event_type __event_type_cloud_module_event; /* Dummy functions and objects - End. */ @@ -243,39 +256,23 @@ static int module_start_stub(struct module_data *module, int num_calls) static void setup_location_module_in_init_state(void) { - /* Send APP_EVT_START. */ - __cmock_app_event_manager_alloc_ExpectAnyArgsAndReturn(&app_module_event_memory); - __cmock_app_event_manager_free_ExpectAnyArgs(); - struct app_module_event *app_module_event = new_app_module_event(); + struct app_module_event *app_module_event; __cmock_module_start_Stub(&module_start_stub); - - app_module_event->type = APP_EVT_START; - __cmock_app_event_manager_alloc_ExpectAnyArgsAndReturn(&location_module_event_memory); - bool ret = LOCATION_MODULE_EVT_HANDLER((struct app_event_header *)app_module_event); - - app_event_manager_free(app_module_event); - - TEST_ASSERT_EQUAL(0, ret); + TEST_SEND_EVENT(app, APP_EVT_START, app_module_event); } static void setup_location_module_in_running_state(void) { + bool ret; + struct modem_module_event *modem_module_event; + setup_location_module_in_init_state(); /* Send MODEM_EVT_INITIALIZED. */ __cmock_location_init_ExpectAndReturn(&location_event_handler, 0); - - __cmock_app_event_manager_alloc_ExpectAnyArgsAndReturn(&modem_module_event_memory); - __cmock_app_event_manager_free_ExpectAnyArgs(); - struct modem_module_event *modem_module_event = new_modem_module_event(); - - modem_module_event->type = MODEM_EVT_INITIALIZED; - - bool ret = LOCATION_MODULE_EVT_HANDLER((struct app_event_header *)modem_module_event); - - app_event_manager_free(modem_module_event); + TEST_SEND_EVENT(modem, MODEM_EVT_INITIALIZED, modem_module_event); /* Send DATA_EVT_CONFIG_INIT. */ __cmock_app_event_manager_alloc_ExpectAnyArgsAndReturn(&data_module_event_memory); @@ -288,9 +285,7 @@ static void setup_location_module_in_running_state(void) data_module_event->data.cfg.no_data.neighbor_cell = false; ret = LOCATION_MODULE_EVT_HANDLER((struct app_event_header *)data_module_event); - app_event_manager_free(data_module_event); - TEST_ASSERT_EQUAL(0, ret); } @@ -298,6 +293,9 @@ static void setup_location_module_in_running_state(void) */ static void setup_location_module_in_active_state(void) { + bool ret; + struct location_module_event *location_module_event; + /* Pre-condition. */ setup_location_module_in_running_state(); @@ -331,11 +329,18 @@ static void setup_location_module_in_active_state(void) app_module_event->count = 1; app_module_event->data_list[0] = APP_DATA_LOCATION; - __cmock_app_event_manager_alloc_IgnoreAndReturn(&location_module_event_memory); - bool ret = LOCATION_MODULE_EVT_HANDLER((struct app_event_header *)app_module_event); - + ret = LOCATION_MODULE_EVT_HANDLER((struct app_event_header *)app_module_event); app_event_manager_free(app_module_event); + TEST_ASSERT_EQUAL(0, ret); + + /* Send LOCATION_MODULE_EVT_ACTIVE */ + __cmock_app_event_manager_alloc_IgnoreAndReturn(&location_module_event_memory); + __cmock_app_event_manager_free_ExpectAnyArgs(); + location_module_event = new_location_module_event(); + location_module_event->type = LOCATION_MODULE_EVT_ACTIVE; + ret = LOCATION_MODULE_EVT_HANDLER((struct app_event_header *)location_module_event); + app_event_manager_free(location_module_event); TEST_ASSERT_EQUAL(0, ret); } @@ -346,6 +351,8 @@ static void setup_location_module_in_active_state(void) */ void test_location_gnss_with_agps(void) { + struct location_module_event *location_module_event; + /* Set expected location module events. */ expected_location_module_event_count = 4; expected_location_module_events[0].type = LOCATION_MODULE_EVT_ACTIVE; @@ -400,6 +407,8 @@ void test_location_gnss_with_agps(void) .location.details.gnss.satellites_tracked = 8 }; location_event_handler(&event_data_gnss_location); + + TEST_SEND_EVENT(location, LOCATION_MODULE_EVT_INACTIVE, location_module_event); } /* Test whether the location module generates an event for neighbor cell measurements @@ -407,6 +416,9 @@ void test_location_gnss_with_agps(void) */ void test_location_cellular(void) { + struct cloud_module_event *cloud_module_event; + struct location_module_event *location_module_event; + struct lte_lc_ncell neighbor_cells[2] = { {2300, 0, 8, 60, 29}, {2400, 184, 11, 55, 26} @@ -437,11 +449,6 @@ void test_location_cellular(void) /* Set location module into state where location_request() has been called. */ setup_location_module_in_active_state(); - /* Location module indicates that it has handled neighbor cells - * but the location is undefined. - */ - __cmock_location_cloud_location_ext_result_set_Expect(LOCATION_EXT_RESULT_UNKNOWN, NULL); - /* Location request is responded with location library events. */ struct location_event_data event_data_cellular = { .id = LOCATION_EVT_CLOUD_LOCATION_EXT_REQUEST, @@ -449,17 +456,29 @@ void test_location_cellular(void) }; location_event_handler(&event_data_cellular); + /* Location module indicates that it has handled neighbor cells + * but the location is undefined. + */ + __cmock_location_cloud_location_ext_result_set_Expect(LOCATION_EXT_RESULT_UNKNOWN, NULL); + + __cmock_app_event_manager_alloc_ExpectAnyArgsAndReturn(&location_module_event_memory); + TEST_SEND_EVENT(cloud, CLOUD_EVT_CLOUD_LOCATION_UNKNOWN, cloud_module_event); + /* Location library responds with and undefined status. */ struct location_event_data event_data_undefined = { .id = LOCATION_EVT_RESULT_UNKNOWN, }; location_event_handler(&event_data_undefined); + + TEST_SEND_EVENT(location, LOCATION_MODULE_EVT_INACTIVE, location_module_event); } /* Test timeout for location request. */ void test_location_fail_timeout(void) { + struct location_module_event *location_module_event; + /* Set expected location module events. */ expected_location_module_event_count = 3; expected_location_module_events[0].type = LOCATION_MODULE_EVT_ACTIVE; @@ -474,12 +493,16 @@ void test_location_fail_timeout(void) .id = LOCATION_EVT_TIMEOUT, }; location_event_handler(&event_data); + + TEST_SEND_EVENT(location, LOCATION_MODULE_EVT_INACTIVE, location_module_event); } /* Test error while processing for location request. */ void test_location_fail_error(void) { + struct location_module_event *location_module_event; + /* Set expected location module events. */ expected_location_module_event_count = 3; expected_location_module_events[0].type = LOCATION_MODULE_EVT_ACTIVE; @@ -494,12 +517,16 @@ void test_location_fail_error(void) .id = LOCATION_EVT_ERROR, }; location_event_handler(&event_data); + + TEST_SEND_EVENT(location, LOCATION_MODULE_EVT_INACTIVE, location_module_event); } /* Test that an error event is sent when location library initialization fails. */ void test_location_fail_init(void) { + struct modem_module_event *modem_module_event; + setup_location_module_in_init_state(); __cmock__event_submit_Stub(&validate_location_module_evt); @@ -510,17 +537,7 @@ void test_location_fail_init(void) expected_location_module_events[0].type = LOCATION_MODULE_EVT_ERROR_CODE; expected_location_module_events[0].data.err = -1; - __cmock_app_event_manager_alloc_ExpectAnyArgsAndReturn(&modem_module_event_memory); - __cmock_app_event_manager_free_ExpectAnyArgs(); - struct modem_module_event *modem_module_event = new_modem_module_event(); - - modem_module_event->type = MODEM_EVT_INITIALIZED; - - bool ret = LOCATION_MODULE_EVT_HANDLER((struct app_event_header *)modem_module_event); - - app_event_manager_free(modem_module_event); - - TEST_ASSERT_EQUAL(0, ret); + TEST_SEND_EVENT(modem, MODEM_EVT_INITIALIZED, modem_module_event); } int main(void) diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst index 3b7abeccf74..91e6eda0a3d 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -180,8 +180,8 @@ nRF9160: Asset Tracker v2 * Enabled link time optimization to reduce the flash size of the application. You can disable this using the Kconfig option :kconfig:option:`CONFIG_ASSET_TRACKER_V2_LTO`. * Replaced overlay arguments ``OVERLAY_CONFIG`` and ``DTC_OVERLAY_FILE`` with the new Zephyr overlay arguments ``EXTRA_CONF_FILE`` and ``EXTRA_DTC_OVERLAY_FILE`` so as to avoid overriding of board overlay for the nRF9160 DK v0.14.0. - -* Fixed an issue with movement timeout handling in passive mode. + * Possibility for the cloud integration to request the location back to the device for Wi-Fi or cellular positioning. + * Fixed an issue with movement timeout handling in passive mode. nRF9160: Serial LTE modem -------------------------