From 2cac07ce70bffb7e65d31223b356306564c4e50b Mon Sep 17 00:00:00 2001 From: Tommi Rantanen Date: Wed, 23 Aug 2023 10:37:31 +0300 Subject: [PATCH] applications: asset_tracker_v2: Fix Wi-Fi+GNSS location priority ATv2 is ignoring the result of the cloud location methods (Wi-Fi and cellular) and simply indicating to Location library that location is unknown. Hence, Location library will fallback to next method. This is fine when you have location method priority order as GNSS, Wi-Fi and Cellular and there is nothing to fallback after WiFi and cellular. However, when the priorities are Wi-Fi, GNSS and Cellular, you don't want to fallback to GNSS if Wi-Fi positioning returns a valid location. Implemented support for waiting for the location from the cloud and acting based on the outcome. If the cloud backend doesn't support returning the location back to the device, Location library will be still indicated that the location is unknown and it'll do fallback to GNSS. Signed-off-by: Tommi Rantanen --- .../asset_tracker_v2/doc/location_module.rst | 15 ++- .../overlay-nrf7002ek-wifi-scan-only.conf | 3 + .../src/cloud/aws_iot_integration.c | 5 + .../src/cloud/azure_iot_hub_integration.c | 5 + .../cloud/cloud_codec/aws_iot/aws_iot_codec.c | 32 +++--- .../azure_iot_hub/azure_iot_hub_codec.c | 32 +++--- .../src/cloud/cloud_codec/cloud_codec.h | 22 ++++ .../src/cloud/cloud_codec/lwm2m/lwm2m_codec.c | 8 +- .../cloud_codec/nrf_cloud/nrf_cloud_codec.c | 23 ++++ .../src/cloud/cloud_wrapper.h | 12 ++ .../src/cloud/nrf_cloud_integration.c | 21 +++- .../src/events/cloud_module_event.c | 6 + .../src/events/cloud_module_event.h | 24 +++- .../src/modules/cloud_module.c | 56 ++++++++++ .../src/modules/data_module.c | 18 ++- .../src/modules/location_module.c | 30 ++++- .../src/location_module_test.c | 103 ++++++++++-------- .../releases/release-notes-changelog.rst | 4 +- 18 files changed, 336 insertions(+), 83 deletions(-) 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 -------------------------