From a0e006578c8c3126389ddfa640f7846cc901f890 Mon Sep 17 00:00:00 2001 From: Jun Qing Zou Date: Fri, 16 Jun 2023 09:45:14 +0900 Subject: [PATCH] application: serial_lte_modem: new nRF Cloud service Separate nRF Cloud service from the GNSS service to allow include/exclude of nRF Cloud services by configurating prj.conf, and allow more nRF Cloud-specific services be added later. New configuration CONFIG_SLM_NRF_CLOUD for nRF Cloud access. nRF Cloud A-GPS, P-GPS and Location service to be configured separately with lib_nrfcloud configuration items: .Remove CONFIG_SLM_LOCATION, to use CONFIG_NRF_CLOUD_LOCATION. .Remove CONFIG_SLM_AGPS, to use CONFIG_NRF_CLOUD_AGPS. .Remove CONFIG_SLM_PGPS, to use CONFIG_NRF_CLOUD_PGPS. Enable nRF Cloud A-GPS and Location service by default. Signed-off-by: Jun Qing Zou --- applications/serial_lte_modem/CMakeLists.txt | 1 + applications/serial_lte_modem/Kconfig | 10 +- .../doc/AT_commands_intro.rst | 1 + .../serial_lte_modem/doc/GNSS_AT_commands.rst | 451 +--------- .../doc/NRFCLOUD_AT_commands.rst | 459 ++++++++++ .../serial_lte_modem/doc/slm_description.rst | 19 +- .../serial_lte_modem/overlay-carrier.conf | 4 - applications/serial_lte_modem/prj.conf | 14 +- .../serial_lte_modem/src/gnss/Kconfig | 34 - .../serial_lte_modem/src/gnss/slm_at_gnss.c | 798 +--------------- .../src/nrfcloud/CMakeLists.txt | 8 + .../src/nrfcloud/slm_at_nrfcloud.c | 850 ++++++++++++++++++ .../src/nrfcloud/slm_at_nrfcloud.h | 34 + .../serial_lte_modem/src/slm_at_commands.c | 56 +- .../releases/release-notes-changelog.rst | 4 + 15 files changed, 1455 insertions(+), 1288 deletions(-) create mode 100644 applications/serial_lte_modem/doc/NRFCLOUD_AT_commands.rst delete mode 100644 applications/serial_lte_modem/src/gnss/Kconfig create mode 100644 applications/serial_lte_modem/src/nrfcloud/CMakeLists.txt create mode 100644 applications/serial_lte_modem/src/nrfcloud/slm_at_nrfcloud.c create mode 100644 applications/serial_lte_modem/src/nrfcloud/slm_at_nrfcloud.h diff --git a/applications/serial_lte_modem/CMakeLists.txt b/applications/serial_lte_modem/CMakeLists.txt index 858778d4033a..2490cb58cc60 100644 --- a/applications/serial_lte_modem/CMakeLists.txt +++ b/applications/serial_lte_modem/CMakeLists.txt @@ -26,6 +26,7 @@ target_sources_ifdef(CONFIG_SLM_NATIVE_TLS app PRIVATE src/slm_native_tls.c) target_sources_ifdef(CONFIG_SLM_NATIVE_TLS app PRIVATE src/slm_at_cmng.c) add_subdirectory_ifdef(CONFIG_SLM_GNSS src/gnss) +add_subdirectory_ifdef(CONFIG_SLM_NRF_CLOUD src/nrfcloud) add_subdirectory_ifdef(CONFIG_SLM_FTPC src/ftp_c) add_subdirectory_ifdef(CONFIG_SLM_MQTTC src/mqtt_c) add_subdirectory_ifdef(CONFIG_SLM_HTTPC src/http_c) diff --git a/applications/serial_lte_modem/Kconfig b/applications/serial_lte_modem/Kconfig index 2cf8730be52c..40b24e3fa46c 100644 --- a/applications/serial_lte_modem/Kconfig +++ b/applications/serial_lte_modem/Kconfig @@ -17,6 +17,7 @@ config SLM_AT_MODE bool "Serial LTE Modem by raw AT mode" default y select AT_CMD_PARSER + select AT_MONITOR config SLM_AT_MAX_PARAM int "Maximum number of parameters in AT command" @@ -124,7 +125,14 @@ config SLM_SMS help Support SMS send/receive in plain text -rsource "src/gnss/Kconfig" +config SLM_GNSS + bool "GNSS support in SLM" + default y + +config SLM_NRF_CLOUD + bool "nRF Cloud support in SLM" + default y + rsource "src/ftp_c/Kconfig" rsource "src/mqtt_c/Kconfig" rsource "src/http_c/Kconfig" diff --git a/applications/serial_lte_modem/doc/AT_commands_intro.rst b/applications/serial_lte_modem/doc/AT_commands_intro.rst index c6c1269acfb9..0d6638313272 100644 --- a/applications/serial_lte_modem/doc/AT_commands_intro.rst +++ b/applications/serial_lte_modem/doc/AT_commands_intro.rst @@ -47,3 +47,4 @@ The modem-specific AT commands are documented in the `nRF91 AT Commands Referenc TWI_AT_commands GPIO_AT_commands CARRIER_AT_commands + NRFCLOUD_AT_commands diff --git a/applications/serial_lte_modem/doc/GNSS_AT_commands.rst b/applications/serial_lte_modem/doc/GNSS_AT_commands.rst index a010c2cfc1aa..019b7a8055fb 100644 --- a/applications/serial_lte_modem/doc/GNSS_AT_commands.rst +++ b/applications/serial_lte_modem/doc/GNSS_AT_commands.rst @@ -167,183 +167,6 @@ Example OK - -Connect to nRF Cloud -==================== - -The ``#XNRFCLOUD`` command controls the connection to the nRF Cloud service. - -.. note:: - To use ``#XNRFCLOUD``, you must first provision the device to nRF Cloud, using the UUID from the modem firmware as device ID. - -Set command ------------ - -The set command allows you to connect and disconnect the nRF Cloud service. - -Syntax -~~~~~~ - -:: - - #XNRFCLOUD=[,] - -The ```` parameter accepts the following integer values: - -* ``0`` - Disconnect from the nRF Cloud service. -* ``1`` - Connect to the nRF Cloud service. -* ``2`` - Send a message in the JSON format to the nRF Cloud service. - -When ```` is ``2``, SLM enters ``slm_data_mode``. - -The ```` parameter is used only when the ```` value is ``1`` -It accepts the following integer values: - -* ``0`` - It does not signify the location info to nRF Cloud. -* ``1`` - It does signify the location info to nRF Cloud. - -When the ```` parameter is not specified, it does not signify the location info to nRF Cloud. - -.. note:: - The application signifies the location info to nRF Cloud in a best-effort way. - The minimal report interval is 5 seconds. - -.. note:: - The application supports nRF Cloud cloud2device appId ``MODEM`` to send AT command from cloud: - - * cloud2device schema:: - - {"appId":"MODEM", "messageType":"CMD", "data":""}. - - * device2cloud schema:: - - {"appId":"MODEM", "messageType":"RSP", "data":""}. - - The application executes the AT command in a best-effort way. - -.. note:: - The application supports nRF Cloud cloud2device appId ``DEVICE`` to gracefully disconnect from cloud: - - * cloud2device schema:: - - {"appId":"DEVICE", "messageType":"DISCON"}. - - There is no response sending to nRF Cloud for this appId. - -Unsolicited notification -~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - #XNRFCLOUD: , - -* The ```` value indicates whether the nRF Cloud connection is ready or not. -* The ```` value indicates whether the location info will be signified to nRF Cloud or not. - -:: - - #XNRFCLOUD: - -* The ```` value indicates the nRF Cloud data received when A-GPS, P-GPS, and Cell_Pos are not active. - -Example -~~~~~~~ - -:: - - AT#XNRFCLOUD=1 - - OK - #XNRFCLOUD: 1,0 - - AT#XNRFCLOUD=2 - OK - {"msg":"Hello, nRF Cloud"}+++ - #XDATAMODE: 0 - - #XNRFCLOUD: {"msg":"Hello"} - - AT#XNRFCLOUD=0 - - AT#XNRFCLOUD: 0,0 - - OK - AT#XNRFCLOUD=1,1 - - OK - #XNRFCLOUD: 1,1 - AT#XNRFCLOUD=0 - - AT#XNRFCLOUD: 0,1 - - OK - -Read command ------------- - -The read command checks if nRF Cloud is connected or not. - -Syntax -~~~~~~ - -:: - - #XNRFCLOUD? - -Response syntax -~~~~~~~~~~~~~~~ - -:: - - #XNRFCLOUD: ,,, - -* The ```` value indicates whether the nRF Cloud connection is ready or not. -* The ```` value indicates whether the location info will be signified to nRF Cloud or not. -* The ```` value indicates the ``sec_tag`` used for accessing nRF Cloud. -* The ```` value indicates the device ID used for accessing nRF Cloud. - -Example -~~~~~~~ - -:: - - AT#XNRFCLOUD? - - #XNRFCLOUD: 1,0,16842753,"nrf-352656106443792" - - OK - -:: - - AT#XNRFCLOUD? - - #XNRFCLOUD: 1,0,8888,"50503041-3633-4261-803d-1e2b8f70111a" - - OK - -Test command ------------- - -The test command tests the existence of the command and provides information about the type of its subparameters. - -Syntax -~~~~~~ - -:: - - #XNRFCLOUD=? - -Example -~~~~~~~ - -:: - - AT#XXNRFCLOUD=? - - #XNRFCLOUD: (0,1,2), - - OK - GNSS with nRF Cloud A-GPS ========================= @@ -352,7 +175,7 @@ The ``#XAGPS`` command runs the GNSS together with the nRF Cloud A-GPS service. .. note:: To use ``#XAGPS``, the following preconditions apply: - * You must define :ref:`CONFIG_SLM_AGPS `. + * You must define :ref:`CONFIG_SLM_NRF_CLOUD ` and :kconfig:option:`CONFIG_NRF_CLOUD_AGPS `. * You must have access to nRF Cloud through the LTE network for receiving A-GPS data. Set command @@ -526,7 +349,7 @@ The ``#XPGPS`` command runs the GNSS together with the nRF Cloud P-GPS service. .. note:: To use ``#XPGPS``, the following preconditions apply: - * You must define :ref:`CONFIG_SLM_PGPS `. + * You must define :ref:`CONFIG_SLM_NRF_CLOUD ` and :kconfig:option:`CONFIG_NRF_CLOUD_PGPS `. * You must have access to nRF Cloud through the LTE network for receiving P-GPS data. Set command @@ -754,273 +577,3 @@ Example #XGPSDEL: OK - -nRF Cloud cellular location -=========================== - -The ``#XCELLPOS`` command runs the nRF Cloud cellular location service for location information. - -.. note:: - To use ``#XCELLPOS``, the following preconditions apply: - - * You must define :ref:`CONFIG_SLM_LOCATION `. - * You must have access to nRF Cloud through the LTE network. - -Set command ------------ - -The set command allows you to start and stop the nRF Cloud cellular location service. - -Syntax -~~~~~~ - -:: - - #XCELLPOS= - -The ```` parameter accepts the following integer values: - -* ``0`` - Stop cellular location. -* ``1`` - Start cellular location in single-cell mode. -* ``2`` - Start cellular location in multi-cell mode. - To use ``2``, you must issue the ``AT%NCELLMEAS`` command with of ``0-2`` first. - -Unsolicited notification -~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - #XCELLPOS: ,,, - -* The ```` value indicates in which mode the cellular location service is running: - - * ``0`` - The service is running in single-cell mode - * ``1`` - The service is running in multi-cell mode - -* The ```` value represents the latitude in degrees. -* The ```` value represents the longitude in degrees. -* The ```` value represents the certainty of the result. - -Example -~~~~~~~ - -:: - - AT%XSYSTEMMODE=1,0,0,0 - - OK - AT+CFUN=1 - - OK - AT#XNRFCLOUD=1 - - OK - #XNRFCLOUD: 1,0 - AT#XCELLPOS=1 - - OK - - #XCELLPOS: 0,35.455833,139.626111,1094 - - AT%NCELLMEAS - - OK - - %NCELLMEAS: 0,"0199F10A","44020","107E",65535,3750,5,49,27,107504,3750,251,33,4,0,475,107,26,14,25,475,58,26,17,25,475,277,24,9,25,475,51,18,1,25 - - AT#XCELLPOS=2 - - OK - - #XCELLPOS: 1,35.534999,139.722362,1801 - AT#XCELLPOS=0 - - OK - -Read command ------------- - -The read command allows you to check the cellular location service status. - -Syntax -~~~~~~ - -:: - - #XCELLPOS? - -Response syntax -~~~~~~~~~~~~~~~ - -:: - - #XCELLPOS: - -* The ```` value is an integer. - When it returns the value of ``1``, it means that the cellular location service is started. - -Example -~~~~~~~ - -:: - - AT#XCELLPOS? - - #XCELLPOS: 1 - - OK - -Test command ------------- - -The test command tests the existence of the command and provides information about the type of its subparameters. - -Syntax -~~~~~~ - -:: - - #XCELLPOS=? - -Example -~~~~~~~ - -:: - - AT#XCELLPOS=? - - #XCELLPOS: (0,1,2) - - OK - -nRF Cloud Wi-Fi location -======================== - -The ``#XWIFIPOS`` command runs the nRF Cloud Wi-Fi location service for location information. - -.. note:: - To use ``#XWIFIPOS``, the following preconditions apply: - - * You must define :ref:`CONFIG_SLM_LOCATION `. - * You must have access to nRF Cloud through the LTE network. - -Set command ------------ - -The set command allows you to start and stop the nRF Cloud Wi-Fi location service. - -Syntax -~~~~~~ - -:: - - #XWIFIPOS=[,,[,,[...]]] - -The ```` parameter accepts the following integer values: - -* ``0`` - Stop Wi-Fi location. -* ``1`` - Start Wi-Fi location. - -* The ```` parameter is a string. - It indicates the SSID of the Wi-Fi access point. - -* The ```` parameter is a string. - It indicates the MAC address string of the Wi-Fi access point. - The string should be formatted as "%02x:%02x:%02x:%02x:%02x:%02x". - -The command accepts ```` and ```` of up to 5 access points. - -Unsolicited notification -~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - #XWIFIPOS: ,,, - -* The ```` value indicates in which mode the Wi-Fi location service is running: - - * ``2`` - The service is running in Wi-Fi mode - -* The ```` value represents the latitude in degrees. -* The ```` value represents the longitude in degrees. -* The ```` value represents the certainty of the result. - -Example -~~~~~~~ - -:: - - AT%XSYSTEMMODE=1,0,0,0 - - OK - AT+CFUN=1 - - OK - AT#XNRFCLOUD=1 - - OK - #XNRFCLOUD: 1,0 - AT#XWIFIPOS=1,"Nordic_WLAN_5GHz","40:9b:cd:c1:5a:40","Nordic_Guest","00:90:fe:eb:4f:42" - - OK - - #XWIFIPOS: 2,35.457272,139.624395,60 - AT#XWIFIPOS=0 - - OK - -Read command ------------- - -The read command allows you to check the Wi-Fi location service status. - -Syntax -~~~~~~ - -:: - - #XWIFIPOS? - -Response syntax -~~~~~~~~~~~~~~~ - -:: - - #XWIFIPOS: - -* The ```` value is an integer. - When it returns the value of ``1``, it means that the Wi-Fi location service is started. - -Example -~~~~~~~ - -:: - - AT#XWIFIPOS? - - #XWIFIPOS: 0 - - OK - -Test command ------------- - -The test command tests the existence of the command and provides information about the type of its subparameters. - -Syntax -~~~~~~ - -:: - - #XWIFIPOS=? - -Example -~~~~~~~ - -:: - - AT#XWIFIPOS=? - - #XWIFIPOS: (0,1) - - OK diff --git a/applications/serial_lte_modem/doc/NRFCLOUD_AT_commands.rst b/applications/serial_lte_modem/doc/NRFCLOUD_AT_commands.rst new file mode 100644 index 000000000000..49fa2ec83228 --- /dev/null +++ b/applications/serial_lte_modem/doc/NRFCLOUD_AT_commands.rst @@ -0,0 +1,459 @@ +.. _SLM_AT_NRFCLOUD: + +nRF Cloud AT commands +********************* + +.. contents:: + :local: + :depth: 2 + +The following commands list contains nRF Cloud-related AT commands. + +nRF Cloud access +================ + +The ``#XNRFCLOUD`` command controls the access to the nRF Cloud service. + +.. note:: + To use ``#XNRFCLOUD``, the following preconditions apply: + + * You must first provision the device to nRF Cloud, using the UUID from the modem firmware as device ID. + * You must define :ref:`CONFIG_SLM_NRF_CLOUD `. + * You must have access to nRF Cloud through the LTE network. + +Set command +----------- + +The set command allows you to access the nRF Cloud service. + +Syntax +~~~~~~ + +:: + + #XNRFCLOUD=[,] + +The ```` parameter accepts the following integer values: + +* ``0`` - Disconnect from the nRF Cloud service. +* ``1`` - Connect to the nRF Cloud service. +* ``2`` - Send a message in the JSON format to the nRF Cloud service. + +When ```` is ``2``, SLM enters ``slm_data_mode``. + +The ```` parameter is used only when the ```` value is ``1`` +It accepts the following integer values: + +* ``0`` - It does not signify the location info to nRF Cloud. +* ``1`` - It does signify the location info to nRF Cloud. + +When the ```` parameter is not specified, it does not signify the location info to nRF Cloud. + +.. note:: + The application signifies the location info to nRF Cloud in a best-effort way. + The minimal report interval is 5 seconds. + +.. note:: + The application supports nRF Cloud cloud2device appId ``MODEM`` to send AT command from cloud: + + * cloud2device schema:: + + {"appId":"MODEM", "messageType":"CMD", "data":""}. + + * device2cloud schema:: + + {"appId":"MODEM", "messageType":"RSP", "data":""}. + + The application executes the AT command in a best-effort way. + +.. note:: + The application supports nRF Cloud cloud2device appId ``DEVICE`` to gracefully disconnect from cloud: + + * cloud2device schema:: + + {"appId":"DEVICE", "messageType":"DISCON"}. + + There is no response sending to nRF Cloud for this appId. + +Unsolicited notification +~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + #XNRFCLOUD: , + +* The ```` value indicates whether the nRF Cloud connection is ready or not. +* The ```` value indicates whether the location info will be signified to nRF Cloud or not. + +:: + + #XNRFCLOUD: + +* The ```` value indicates the nRF Cloud data received when A-GPS, P-GPS, and Cell_Pos are not active. + +Example +~~~~~~~ + +:: + + AT#XNRFCLOUD=1 + + OK + #XNRFCLOUD: 1,0 + + AT#XNRFCLOUD=2 + OK + {"msg":"Hello, nRF Cloud"}+++ + #XDATAMODE: 0 + + #XNRFCLOUD: {"msg":"Hello"} + + AT#XNRFCLOUD=0 + + AT#XNRFCLOUD: 0,0 + + OK + AT#XNRFCLOUD=1,1 + + OK + #XNRFCLOUD: 1,1 + AT#XNRFCLOUD=0 + + AT#XNRFCLOUD: 0,1 + + OK + +Read command +------------ + +The read command checks if nRF Cloud is connected or not. + +Syntax +~~~~~~ + +:: + + #XNRFCLOUD? + +Response syntax +~~~~~~~~~~~~~~~ + +:: + + #XNRFCLOUD: ,,, + +* The ```` value indicates whether the nRF Cloud connection is ready or not. +* The ```` value indicates whether the location info will be signified to nRF Cloud or not. +* The ```` value indicates the ``sec_tag`` used for accessing nRF Cloud. +* The ```` value indicates the device ID used for accessing nRF Cloud. + +Example +~~~~~~~ + +:: + + AT#XNRFCLOUD? + + #XNRFCLOUD: 1,0,16842753,"nrf-352656106443792" + + OK + +:: + + AT#XNRFCLOUD? + + #XNRFCLOUD: 1,0,8888,"50503041-3633-4261-803d-1e2b8f70111a" + + OK + +Test command +------------ + +The test command tests the existence of the command and provides information about the type of its subparameters. + +Syntax +~~~~~~ + +:: + + #XNRFCLOUD=? + +Example +~~~~~~~ + +:: + + AT#XXNRFCLOUD=? + + #XNRFCLOUD: (0,1,2), + + OK + +nRF Cloud cellular location +=========================== + +The ``#XCELLPOS`` command runs the nRF Cloud cellular location service for location information. + +.. note:: + To use ``#XCELLPOS``, the following preconditions apply: + + * You must define :ref:`CONFIG_SLM_NRF_CLOUD ` and :kconfig:option:`CONFIG_NRF_CLOUD_LOCATION `. + * You must have access to nRF Cloud through the LTE network. + +Set command +----------- + +The set command allows you to start and stop the nRF Cloud cellular location service. + +Syntax +~~~~~~ + +:: + + #XCELLPOS= + +The ```` parameter accepts the following integer values: + +* ``0`` - Stop cellular location. +* ``1`` - Start cellular location in single-cell mode. +* ``2`` - Start cellular location in multi-cell mode. + To use ``2``, you must issue the ``AT%NCELLMEAS`` command first. + +Unsolicited notification +~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + #XCELLPOS: ,,, + +* The ```` value indicates in which mode the cellular location service is running: + + * ``0`` - The service is running in single-cell mode + * ``1`` - The service is running in multi-cell mode + +* The ```` value represents the latitude in degrees. +* The ```` value represents the longitude in degrees. +* The ```` value represents the certainty of the result. + +Example +~~~~~~~ + +:: + + AT%XSYSTEMMODE=1,0,0,0 + + OK + AT+CFUN=1 + + OK + AT#XNRFCLOUD=1 + + OK + #XNRFCLOUD: 1,0 + AT#XCELLPOS=1 + + OK + + #XCELLPOS: 0,35.455833,139.626111,1094 + + AT%NCELLMEAS + + OK + + %NCELLMEAS: 0,"0199F10A","44020","107E",65535,3750,5,49,27,107504,3750,251,33,4,0,475,107,26,14,25,475,58,26,17,25,475,277,24,9,25,475,51,18,1,25 + + AT#XCELLPOS=2 + + OK + + #XCELLPOS: 1,35.534999,139.722362,1801 + AT#XCELLPOS=0 + + OK + +Read command +------------ + +The read command allows you to check the cellular location service status. + +Syntax +~~~~~~ + +:: + + #XCELLPOS? + +Response syntax +~~~~~~~~~~~~~~~ + +:: + + #XCELLPOS: + +* The ```` value is an integer. + When it returns the value of ``1``, it means that the cellular location service is started. + +Example +~~~~~~~ + +:: + + AT#XCELLPOS? + + #XCELLPOS: 1 + + OK + +Test command +------------ + +The test command tests the existence of the command and provides information about the type of its subparameters. + +Syntax +~~~~~~ + +:: + + #XCELLPOS=? + +Example +~~~~~~~ + +:: + + AT#XCELLPOS=? + + #XCELLPOS: (0,1,2) + + OK + +nRF Cloud Wi-Fi location +======================== + +The ``#XWIFIPOS`` command runs the nRF Cloud Wi-Fi location service to receive location information. + +.. note:: + To use ``#XWIFIPOS``, the following preconditions apply: + + * You must define :ref:`CONFIG_SLM_NRF_CLOUD ` and :kconfig:option:`CONFIG_NRF_CLOUD_LOCATION `. + * You must have access to nRF Cloud through the LTE network. + +Set command +----------- + +The set command allows you to start and stop the nRF Cloud Wi-Fi location service. + +Syntax +~~~~~~ + +:: + + #XWIFIPOS=[,,[,,[...]]] + +The ```` parameter accepts the following integer values: + +* ``0`` - Stop Wi-Fi location. +* ``1`` - Start Wi-Fi location. + +* The ```` parameter is a string. + It indicates the SSID of the Wi-Fi access point. + +* The ```` parameter is a string. + It indicates the MAC address of the Wi-Fi access point and should be formatted as ``%02x:%02x:%02x:%02x:%02x:%02x``. + +The command accepts the ```` and ```` values of up to 5 access points. + +Unsolicited notification +~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + #XWIFIPOS: ,,, + +* The ```` value indicates in which mode the Wi-Fi location service is running: + + * ``2`` - The service is running in Wi-Fi mode + +* The ```` value represents the latitude in degrees. +* The ```` value represents the longitude in degrees. +* The ```` value represents the certainty of the result. + +Example +~~~~~~~ + +:: + + AT%XSYSTEMMODE=1,0,0,0 + + OK + AT+CFUN=1 + + OK + AT#XNRFCLOUD=1 + + OK + #XNRFCLOUD: 1,0 + AT#XWIFIPOS=1,"Nordic_WLAN_5GHz","40:9b:cd:c1:5a:40","Nordic_Guest","00:90:fe:eb:4f:42" + + OK + + #XWIFIPOS: 2,35.457272,139.624395,60 + AT#XWIFIPOS=0 + + OK + +Read command +------------ + +The read command allows you to check the Wi-Fi location service status. + +Syntax +~~~~~~ + +:: + + #XWIFIPOS? + +Response syntax +~~~~~~~~~~~~~~~ + +:: + + #XWIFIPOS: + +* The ```` value is an integer. + When it returns the value of ``1``, it means that the Wi-Fi location service is started. + +Example +~~~~~~~ + +:: + + AT#XWIFIPOS? + + #XWIFIPOS: 0 + + OK + +Test command +------------ + +The test command tests the existence of the command and provides information about the type of its subparameters. + +Syntax +~~~~~~ + +:: + + #XWIFIPOS=? + +Example +~~~~~~~ + +:: + + AT#XWIFIPOS=? + + #XWIFIPOS: (0,1) + + OK diff --git a/applications/serial_lte_modem/doc/slm_description.rst b/applications/serial_lte_modem/doc/slm_description.rst index 2fc7b0abadf7..bccfa94916cb 100644 --- a/applications/serial_lte_modem/doc/slm_description.rst +++ b/applications/serial_lte_modem/doc/slm_description.rst @@ -144,23 +144,10 @@ CONFIG_SLM_SMS - SMS support in SLM CONFIG_SLM_GNSS - GNSS support in SLM This option enables additional AT commands for using the GNSS service. -.. _CONFIG_SLM_AGPS: +.. _CONFIG_SLM_NRF_CLOUD: -CONFIG_SLM_AGPS - nRF Cloud A-GPS support in SLM - This option enables additional AT commands for using the nRF Cloud A-GPS service. - It is not selected by default. - -.. _CONFIG_SLM_PGPS: - -CONFIG_SLM_PGPS - nRF Cloud P-GPS support in SLM - This option enables additional AT commands for using the nRF Cloud P-GPS service. - It is not selected by default. - -.. _CONFIG_SLM_LOCATION: - -CONFIG_SLM_LOCATION - nRF Cloud cellular and Wi-Fi location support in SLM - This option enables additional AT commands for using the nRF Cloud location service. - It is not selected by default. +CONFIG_SLM_NRF_CLOUD - nRF Cloud support in SLM + This option enables additional AT commands for using the nRF Cloud service. .. _CONFIG_SLM_FTPC: diff --git a/applications/serial_lte_modem/overlay-carrier.conf b/applications/serial_lte_modem/overlay-carrier.conf index 9a0edb5d1e97..46646bbc3a75 100644 --- a/applications/serial_lte_modem/overlay-carrier.conf +++ b/applications/serial_lte_modem/overlay-carrier.conf @@ -34,7 +34,3 @@ CONFIG_PM_PARTITION_SIZE_NVS_STORAGE=0 # Enable LwM2M carrier support in SLM CONFIG_SLM_CARRIER=y - -# Disable PGPS to have enough space for carrier library -CONFIG_SLM_PGPS=n -CONFIG_NRF_CLOUD_PGPS=n diff --git a/applications/serial_lte_modem/prj.conf b/applications/serial_lte_modem/prj.conf index 2ff6e2a14f9d..f0240740a23f 100644 --- a/applications/serial_lte_modem/prj.conf +++ b/applications/serial_lte_modem/prj.conf @@ -74,21 +74,22 @@ CONFIG_SETTINGS_FCB=y CONFIG_FCB=y # nRF Cloud -#CONFIG_NRF_CLOUD_IPV6=y +CONFIG_NRF_CLOUD_IPV6=n CONFIG_NRF_CLOUD_MQTT=y CONFIG_NRF_CLOUD_FOTA=n CONFIG_NRF_CLOUD_AGPS=y CONFIG_NRF_CLOUD_AGPS_FILTERED=n -CONFIG_NRF_CLOUD_PGPS=y +CONFIG_NRF_CLOUD_PGPS=n CONFIG_NRF_CLOUD_LOCATION=y CONFIG_NRF_CLOUD_LOG_LEVEL_INF=y CONFIG_NRF_CLOUD_GPS_LOG_LEVEL_INF=y +CONFIG_NRF_CLOUD_CLIENT_ID_SRC_INTERNAL_UUID=y CONFIG_DATE_TIME=y CONFIG_MODEM_INFO=y CONFIG_MODEM_INFO_ADD_DATE_TIME=n -CONFIG_AT_MONITOR=y + +# UUID and JWT CONFIG_MODEM_JWT=y -CONFIG_NRF_CLOUD_CLIENT_ID_SRC_INTERNAL_UUID=y # # SLM-specific configurations @@ -98,8 +99,3 @@ CONFIG_SLM_LOG_LEVEL_INF=y CONFIG_SLM_EXTERNAL_XTAL=n CONFIG_SLM_START_SLEEP=n CONFIG_SLM_DATAMODE_URC=n - -# nRF Cloud based location services -CONFIG_SLM_AGPS=n -CONFIG_SLM_PGPS=n -CONFIG_SLM_LOCATION=n diff --git a/applications/serial_lte_modem/src/gnss/Kconfig b/applications/serial_lte_modem/src/gnss/Kconfig deleted file mode 100644 index 17352c6340dd..000000000000 --- a/applications/serial_lte_modem/src/gnss/Kconfig +++ /dev/null @@ -1,34 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - -config SLM_GNSS - bool "GNSS support in SLM" - default y - -if SLM_GNSS - -config SLM_AGPS - bool "Use nRF Cloud A-GPS" - depends on NRF_CLOUD_AGPS - depends on NRF_CLOUD_MQTT - help - Use nRF Cloud A-GPS in GPS operation - -config SLM_PGPS - bool "Use nRF Cloud P-GPS" - depends on NRF_CLOUD_PGPS - depends on NRF_CLOUD_MQTT - help - Use nRF Cloud P-GPS in GPS operation - -config SLM_LOCATION - bool "Use nRF Cloud Location" - depends on AT_MONITOR - depends on NRF_CLOUD_LOCATION - depends on NRF_CLOUD_MQTT - help - Request nRF Cloud cellular or Wi-Fi location service - -endif # SLM_GNSS diff --git a/applications/serial_lte_modem/src/gnss/slm_at_gnss.c b/applications/serial_lte_modem/src/gnss/slm_at_gnss.c index 90700b9646fc..01adc4153e22 100644 --- a/applications/serial_lte_modem/src/gnss/slm_at_gnss.c +++ b/applications/serial_lte_modem/src/gnss/slm_at_gnss.c @@ -9,62 +9,44 @@ #include #include #include +#if defined(CONFIG_SLM_NRF_CLOUD) #include #include #include -#if defined(CONFIG_SLM_PGPS) #include #endif -#include #include "slm_util.h" #include "slm_at_host.h" #include "slm_at_gnss.h" LOG_MODULE_REGISTER(slm_gnss, CONFIG_SLM_LOG_LEVEL); -#define SERVICE_INFO_GNSS \ - "{\"state\":{\"reported\":{\"device\": {\"serviceInfo\":{\"ui\":[\"GNSS\"]}}}}}" - #define LOCATION_REPORT_MS 5000 /**@brief GNSS operations. */ enum slm_gnss_operation { GPS_STOP, GPS_START, - nRF_CLOUD_DISCONNECT = GPS_STOP, - nRF_CLOUD_CONNECT = GPS_START, - nRF_CLOUD_SEND, AGPS_STOP = GPS_STOP, AGPS_START = GPS_START, PGPS_STOP = GPS_STOP, - PGPS_START = GPS_START, - CELLPOS_STOP = GPS_STOP, - CELLPOS_START_SCELL = GPS_START, - CELLPOS_START_MCELL = nRF_CLOUD_SEND, - WIFIPOS_STOP = GPS_STOP, - WIFIPOS_START = GPS_START + PGPS_START = GPS_START }; -static struct k_work cloud_cmd; +#if defined(CONFIG_NRF_CLOUD_AGPS) static struct k_work agps_req; -#if defined(CONFIG_SLM_PGPS) +#endif +#if defined(CONFIG_NRF_CLOUD_PGPS) static struct k_work pgps_req; #endif static struct k_work fix_rep; -static struct k_work cell_pos_req; -static struct k_work wifi_pos_req; -static enum nrf_cloud_location_type cell_pos_type; -static bool nrf_cloud_ready; -static bool location_signify; static uint64_t ttft_start; static enum { RUN_TYPE_NONE, RUN_TYPE_GPS, RUN_TYPE_AGPS, RUN_TYPE_PGPS, - RUN_TYPE_CELL_POS, - RUN_TYPE_WIFI_POS } run_type; static enum { RUN_STATUS_STOPPED, @@ -75,40 +57,11 @@ static enum { RUN_STATUS_MAX } run_status; -static K_SEM_DEFINE(sem_date_time, 0, 1); - -/** Definitins for %NCELLMEAS notification - * %NCELLMEAS: status [,, , , , , - * , , ,,] - * [,1, 1, 1, 1,1] - * [,2, 2, 2, 2,2] ... - * [,17, 17, 17, 17,17 - * - * Max 17 ncell, but align with CONFIG_SLM_AT_MAX_PARAM - * 11 number of parameters for current cell (including "%NCELLMEAS") - * 5 number of parameters for one neighboring cell - */ -#define MAX_PARAM_CELL 11 -#define MAX_PARAM_NCELL 5 -/* Must support at least all params for current cell plus one ncell */ -BUILD_ASSERT(CONFIG_SLM_AT_MAX_PARAM > (MAX_PARAM_CELL + MAX_PARAM_NCELL), - "CONFIG_SLM_AT_MAX_PARAM too small"); -#define NCELL_CNT ((CONFIG_SLM_AT_MAX_PARAM - MAX_PARAM_CELL) / MAX_PARAM_NCELL) - -#define AP_CNT_MAX 5 - -static struct lte_lc_ncell neighbor_cells[NCELL_CNT]; -static struct lte_lc_cells_info cell_data = { - .neighbor_cells = neighbor_cells -}; -static int ncell_meas_status; -static struct wifi_scan_result scan_results[AP_CNT_MAX]; -static struct wifi_scan_info wifi_data = { - .ap_info = scan_results -}; -static char device_id[NRF_CLOUD_CLIENT_ID_MAX_LEN]; - /* global variable defined in different files */ +#if defined(CONFIG_SLM_NRF_CLOUD) +extern bool nrf_cloud_ready; +extern bool nrf_cloud_location_signify; +#endif extern struct k_work_q slm_work_q; extern struct at_param_list at_param_list; extern uint8_t at_buf[SLM_AT_MAX_CMD_LEN]; @@ -196,6 +149,7 @@ static int gnss_shutdown(void) return ret; } +#if defined(CONFIG_NRF_CLOUD_AGPS) || defined(CONFIG_NRF_CLOUD_PGPS) static int read_agps_req(struct nrf_modem_gnss_agps_data_frame *req) { int err; @@ -209,7 +163,9 @@ static int read_agps_req(struct nrf_modem_gnss_agps_data_frame *req) return 0; } +#endif /* CONFIG_NRF_CLOUD_AGPS || CONFIG_NRF_CLOUD_PGPS */ +#if defined(CONFIG_NRF_CLOUD_AGPS) static void agps_req_wk(struct k_work *work) { int err; @@ -228,8 +184,9 @@ static void agps_req_wk(struct k_work *work) } LOG_INF("A-GPS requested"); } +#endif /* CONFIG_NRF_CLOUD_AGPS */ -#if defined(CONFIG_SLM_PGPS) +#if defined(CONFIG_NRF_CLOUD_PGPS) static void pgps_req_wk(struct k_work *work) { int err; @@ -244,215 +201,7 @@ static void pgps_req_wk(struct k_work *work) LOG_INF("P-GPS requested"); } } -#endif - -AT_MONITOR(ncell_meas, "NCELLMEAS", ncell_meas_mon, PAUSED); - -static void ncell_meas_mon(const char *notify) -{ - int err; - uint32_t param_count; - char cid[9] = {0}; - char plmn[6] = {0}; - char mcc[4] = {0}; - char tac[9] = {0}; - size_t size; - - ncell_meas_status = -1; - at_params_list_clear(&at_param_list); - err = at_parser_params_from_str(notify, NULL, &at_param_list); - if (err) { - goto exit; - } - - /* parse status, 0: success 1: fail */ - err = at_params_int_get(&at_param_list, 1, &ncell_meas_status); - if (err) { - goto exit; - } - if (ncell_meas_status != 0) { - LOG_ERR("NCELLMEAS failed"); - err = -EAGAIN; - goto exit; - } - param_count = at_params_valid_count_get(&at_param_list); - if (param_count < MAX_PARAM_CELL) { /* at least current cell */ - LOG_ERR("Missing param in NCELLMEAS notification"); - err = -EAGAIN; - goto exit; - } - - /* parse Cell ID */ - size = sizeof(cid); - err = util_string_get(&at_param_list, 2, cid, &size); - if (err) { - goto exit; - } - err = util_str_to_int(cid, 16, (int *)&cell_data.current_cell.id); - if (err) { - goto exit; - } - - /* parse PLMN */ - size = sizeof(plmn); - err = util_string_get(&at_param_list, 3, plmn, &size); - if (err) { - goto exit; - } - strncpy(mcc, plmn, 3); /* MCC always 3-digit */ - err = util_str_to_int(mcc, 10, &cell_data.current_cell.mcc); - if (err) { - goto exit; - } - err = util_str_to_int(&plmn[3], 10, &cell_data.current_cell.mnc); - if (err) { - goto exit; - } - - /* parse TAC */ - size = sizeof(tac); - err = util_string_get(&at_param_list, 4, tac, &size); - if (err) { - goto exit; - } - err = util_str_to_int(tac, 16, (int *)&cell_data.current_cell.tac); - if (err) { - goto exit; - } - - /* omit timing_advance */ - cell_data.current_cell.timing_advance = NRF_CLOUD_LOCATION_CELL_OMIT_TIME_ADV; - - /* parse EARFCN */ - err = at_params_unsigned_int_get(&at_param_list, 6, &cell_data.current_cell.earfcn); - if (err) { - goto exit; - } - - /* parse PCI */ - err = at_params_unsigned_short_get(&at_param_list, 7, - &cell_data.current_cell.phys_cell_id); - if (err) { - goto exit; - } - - /* parse RSRP and RSRQ */ - err = at_params_short_get(&at_param_list, 8, &cell_data.current_cell.rsrp); - if (err < 0) { - goto exit; - } - err = at_params_short_get(&at_param_list, 9, &cell_data.current_cell.rsrq); - if (err < 0) { - goto exit; - } - - /* omit measurement_time */ - - cell_data.ncells_count = 0; - for (int i = 0; i < NCELL_CNT; i++) { - int offset = i * MAX_PARAM_NCELL + 11; - - if (param_count < (offset + MAX_PARAM_NCELL)) { - break; - } - - /* parse n_earfcn */ - err = at_params_unsigned_int_get(&at_param_list, offset, - &neighbor_cells[i].earfcn); - if (err < 0) { - goto exit; - } - - /* parse n_phys_cell_id */ - err = at_params_unsigned_short_get(&at_param_list, offset + 1, - &neighbor_cells[i].phys_cell_id); - if (err < 0) { - goto exit; - } - - /* parse n_rsrp */ - err = at_params_short_get(&at_param_list, offset + 2, &neighbor_cells[i].rsrp); - if (err < 0) { - goto exit; - } - - /* parse n_rsrq */ - err = at_params_short_get(&at_param_list, offset + 3, &neighbor_cells[i].rsrq); - if (err < 0) { - goto exit; - } - - /* omit time_diff */ - - cell_data.ncells_count++; - } - - err = 0; - -exit: - LOG_INF("NCELLMEAS notification parse (err: %d)", err); -} - -static void cell_pos_req_wk(struct k_work *work) -{ - int err; - ARG_UNUSED(work); - - if (cell_pos_type == LOCATION_TYPE_SINGLE_CELL) { - struct lte_lc_cells_info scell_inf = {.ncells_count = 0}; - - /* Obtain the single cell info from the modem */ - err = nrf_cloud_location_scell_data_get(&scell_inf.current_cell); - if (err) { - LOG_ERR("Failed to obtain cellular network info, error: %d", err); - return; - } - - /* Request location using only the single cell info */ - err = nrf_cloud_location_request(&scell_inf, NULL, true, NULL); - if (err) { - LOG_ERR("Failed to request SCELL, error: %d", err); - rsp_send("\r\n#XCELLPOS: %d\r\n", err); - run_type = RUN_TYPE_NONE; - } else { - LOG_INF("nRF Cloud SCELL requested"); - } - } else if (cell_pos_type == LOCATION_TYPE_MULTI_CELL) { - if (ncell_meas_status == 0 && cell_data.current_cell.id != 0) { - err = nrf_cloud_location_request(&cell_data, NULL, true, NULL); - if (err) { - LOG_ERR("Failed to request MCELL, error: %d", err); - rsp_send("\r\n#XCELLPOS: %d\r\n", err); - run_type = RUN_TYPE_NONE; - } else { - LOG_INF("nRF Cloud MCELL requested, with %d neighboring cells", - cell_data.ncells_count); - } - } else { - LOG_WRN("No request of MCELL"); - rsp_send("\r\n#XCELLPOS: \r\n"); - run_type = RUN_TYPE_NONE; - } - } -} - -static void wifi_pos_req_wk(struct k_work *work) -{ - int err; - - /* Request location using Wi-Fi access point info */ - err = nrf_cloud_location_request(NULL, &wifi_data, true, NULL); - if (err) { - LOG_ERR("Failed to request Wi-Fi location, error: %d", err); - rsp_send("\r\n#XWIFIPOS: %d\r\n", err); - run_type = RUN_TYPE_NONE; - } else { - LOG_INF("nRF Cloud Wi-Fi location requested"); - } -} - -#if defined(CONFIG_SLM_PGPS) static void pgps_event_handler(struct nrf_cloud_pgps_event *event) { int err; @@ -503,7 +252,7 @@ static void pgps_event_handler(struct nrf_cloud_pgps_event *event) break; } } -#endif +#endif /* CONFIG_NRF_CLOUD_PGPS) */ static void on_gnss_evt_nmea(void) { @@ -535,6 +284,7 @@ static void on_gnss_evt_pvt(void) } } +#if defined(CONFIG_SLM_NRF_CLOUD) static int do_cloud_send_obj(struct nrf_cloud_obj *const obj) { int err; @@ -586,12 +336,12 @@ static void send_location(struct nrf_modem_gnss_nmea_data_frame * const nmea_dat LOG_WRN("Failed to send location, error %d", err); } } +#endif /* CONFIG_SLM_NRF_CLOUD */ static void fix_rep_wk(struct k_work *work) { int err; struct nrf_modem_gnss_pvt_data_frame pvt; - struct nrf_modem_gnss_nmea_data_frame nmea; ARG_UNUSED(work); @@ -618,28 +368,31 @@ static void fix_rep_wk(struct k_work *work) } } +#if defined(CONFIG_SLM_NRF_CLOUD) if (IS_ENABLED(CONFIG_SLM_LOG_LEVEL_DBG)) { goto update_pgps; } + struct nrf_modem_gnss_nmea_data_frame nmea; + /* Read $GPGGA NMEA message */ err = nrf_modem_gnss_read((void *)&nmea, sizeof(nmea), NRF_MODEM_GNSS_DATA_NMEA); if (err) { LOG_WRN("Failed to read GNSS NMEA data, error %d", err); } else { /* Report to nRF Cloud by best-effort */ - if (nrf_cloud_ready && location_signify) { + if (nrf_cloud_ready && nrf_cloud_location_signify) { send_location(&nmea); } /* GGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx \r\n */ - if (location_signify) { + if (nrf_cloud_location_signify) { rsp_send("\r\n#XGPS: %s", nmea.nmea_str); } } update_pgps: -#if defined(CONFIG_SLM_PGPS) +#if defined(CONFIG_NRF_CLOUD_PGPS) if (run_type == RUN_TYPE_PGPS) { struct tm gps_time = { .tm_year = pvt.datetime.year - 1900, @@ -655,7 +408,8 @@ static void fix_rep_wk(struct k_work *work) /* help nrf_cloud_pgps as most recent known location */ nrf_cloud_pgps_set_location(pvt.latitude, pvt.longitude); } -#endif +#endif /* CONFIG_NRF_CLOUD_PGPS */ +#endif /* CONFIG_SLM_NRF_CLOUD */ return; } @@ -673,14 +427,17 @@ static void on_gnss_evt_fix(void) static void on_gnss_evt_agps_req(void) { +#if defined(CONFIG_NRF_CLOUD_AGPS) if (run_type == RUN_TYPE_AGPS) { k_work_submit_to_queue(&slm_work_q, &agps_req); -#if defined(CONFIG_SLM_PGPS) - } else if (run_type == RUN_TYPE_PGPS) { + return; + } +#elif defined(CONFIG_NRF_CLOUD_PGPS) + if (run_type == RUN_TYPE_PGPS) { /* Check whether prediction data available or not */ k_work_submit_to_queue(&slm_work_q, &pgps_req); -#endif } +#endif } /* NOTE this event handler runs in interrupt context */ @@ -731,255 +488,6 @@ static void gnss_event_handler(int event) } } -static void on_cloud_evt_ready(void) -{ - if (location_signify) { - int err; - struct nrf_cloud_tx_data msg = { - .data.ptr = SERVICE_INFO_GNSS, - .data.len = strlen(SERVICE_INFO_GNSS), - .topic_type = NRF_CLOUD_TOPIC_STATE, - .qos = MQTT_QOS_0_AT_MOST_ONCE - }; - - /* Update nRF Cloud with GPS service info signifying GPS capabilities. */ - err = nrf_cloud_send(&msg); - if (err) { - LOG_WRN("Failed to send message to cloud, error: %d", err); - } - } - - nrf_cloud_ready = true; - rsp_send("\r\n#XNRFCLOUD: %d,%d\r\n", nrf_cloud_ready, location_signify); - at_monitor_resume(&ncell_meas); -} - -static void on_cloud_evt_disconnected(void) -{ - nrf_cloud_ready = false; - rsp_send("\r\n#XNRFCLOUD: %d,%d\r\n", nrf_cloud_ready, location_signify); - at_monitor_pause(&ncell_meas); -} - -static void on_cloud_evt_location_data_received(const struct nrf_cloud_data *const data) -{ - if (run_type != RUN_TYPE_CELL_POS && run_type != RUN_TYPE_WIFI_POS) { - LOG_WRN("Not expecting location data during run_type: %d", run_type); - return; - } - - int err; - struct nrf_cloud_location_result result; - - err = nrf_cloud_location_process(data->ptr, &result); - if (err == 0) { - if (ttft_start != 0) { - LOG_INF("TTFF %ds", (int)k_uptime_delta(&ttft_start)/1000); - ttft_start = 0; - } - if (run_type == RUN_TYPE_CELL_POS) { - rsp_send("\r\n#XCELLPOS: %d,%lf,%lf,%d\r\n", - result.type, result.lat, result.lon, result.unc); - } else { - rsp_send("\r\n#XWIFIPOS: %d,%lf,%lf,%d\r\n", - result.type, result.lat, result.lon, result.unc); - } - run_type = RUN_TYPE_NONE; - } else if (err == 1) { - LOG_WRN("No position found"); - } else if (err == -EFAULT) { - LOG_ERR("Unable to determine location from cell data, error: %d", - result.err); - } else { - LOG_ERR("Failed to process cellular location response, error: %d", - err); - } -} - -static void cloud_cmd_wk(struct k_work *work) -{ - int ret; - NRF_CLOUD_OBJ_JSON_DEFINE(cmd_rsp_obj); - - ARG_UNUSED(work); - - /* Send AT command to modem */ - ret = nrf_modem_at_cmd(at_buf, sizeof(at_buf), "%s", at_buf); - if (ret < 0) { - LOG_ERR("AT command failed: %d", ret); - return; - } else if (ret > 0) { - LOG_WRN("AT command error, type: %d", nrf_modem_at_err_type(ret)); - } - LOG_INF("MODEM RSP %s", at_buf); - /* replace \" with \' in JSON string-type value */ - for (int i = 0; i < strlen(at_buf); i++) { - if (at_buf[i] == '\"') { - at_buf[i] = '\''; - } - } - - ret = nrf_cloud_obj_msg_init(&cmd_rsp_obj, NRF_CLOUD_JSON_APPID_VAL_MODEM, "RSP"); - if (ret) { - LOG_WRN("Unable initialize AT response message"); - return; - } - - ret = nrf_cloud_obj_str_add(&cmd_rsp_obj, NRF_CLOUD_JSON_DATA_KEY, at_buf, false); - if (ret) { - LOG_WRN("Unable to format AT response message"); - (void)nrf_cloud_obj_free(&cmd_rsp_obj); - return; - } - - /* Send AT response to cloud */ - ret = do_cloud_send_obj(&cmd_rsp_obj); - if (ret) { - LOG_ERR("Send AT response to cloud error: %d", ret); - } -} - -static bool handle_cloud_cmd(const struct nrf_cloud_data *const data) -{ - struct nrf_cloud_obj input_obj; - char *at_cmd; - bool ret = false; - int err = nrf_cloud_obj_input_decode(&input_obj, data); - - if (err) { - LOG_ERR("Unable to decode data from nRF Cloud, error: %d", err); - return false; - } - - /* Check for a modem AT command message */ - err = nrf_cloud_obj_msg_check(&input_obj, - NRF_CLOUD_JSON_APPID_VAL_MODEM, - NRF_CLOUD_JSON_MSG_TYPE_VAL_CMD); - if (!err) { - err = nrf_cloud_obj_str_get(&input_obj, NRF_CLOUD_JSON_DATA_KEY, &at_cmd); - if (!err) { - LOG_INF("MODEM CMD %s", at_cmd); - strcpy(at_buf, at_cmd); - k_work_submit_to_queue(&slm_work_q, &cloud_cmd); - ret = true; - } - goto end; - } - - /* Check for a disconnect message */ - err = nrf_cloud_obj_msg_check(&input_obj, - NRF_CLOUD_JSON_APPID_VAL_DEVICE, - NRF_CLOUD_JSON_MSG_TYPE_VAL_DISCONNECT); - if (!err) { - LOG_INF("DEVICE DISCON"); - /* No action required, handled in lib_nrf_cloud */ - ret = true; - } -end: - (void)nrf_cloud_obj_free(&input_obj); - return ret; -} - -static void on_cloud_evt_data_received(const struct nrf_cloud_data *const data) -{ - if (nrf_cloud_ready) { - if (((char *)data->ptr)[0] == '{') { - /* Check if it's a cloud command sent from the cloud */ - if (handle_cloud_cmd(data)) { - return; - } - } - rsp_send("\r\n#XNRFCLOUD: %s\r\n", (char *)data->ptr); - } -} - -static void cloud_event_handler(const struct nrf_cloud_evt *evt) -{ - switch (evt->type) { - case NRF_CLOUD_EVT_TRANSPORT_CONNECTING: - LOG_DBG("NRF_CLOUD_EVT_TRANSPORT_CONNECTING"); - break; - case NRF_CLOUD_EVT_TRANSPORT_CONNECTED: - LOG_INF("NRF_CLOUD_EVT_TRANSPORT_CONNECTED"); - break; - case NRF_CLOUD_EVT_TRANSPORT_CONNECT_ERROR: - LOG_ERR("NRF_CLOUD_EVT_TRANSPORT_CONNECT_ERROR: status %d", evt->status); - break; - case NRF_CLOUD_EVT_READY: - LOG_INF("NRF_CLOUD_EVT_READY"); - on_cloud_evt_ready(); - break; - case NRF_CLOUD_EVT_TRANSPORT_DISCONNECTED: - LOG_INF("NRF_CLOUD_EVT_TRANSPORT_DISCONNECTED: status %d", evt->status); - on_cloud_evt_disconnected(); - break; - case NRF_CLOUD_EVT_ERROR: - LOG_ERR("NRF_CLOUD_EVT_ERROR: status %d", evt->status); - break; - case NRF_CLOUD_EVT_SENSOR_DATA_ACK: - LOG_DBG("NRF_CLOUD_EVT_SENSOR_DATA_ACK"); - break; - case NRF_CLOUD_EVT_RX_DATA_GENERAL: - LOG_INF("NRF_CLOUD_EVT_RX_DATA_GENERAL"); - on_cloud_evt_data_received(&evt->data); - break; - case NRF_CLOUD_EVT_RX_DATA_LOCATION: - LOG_INF("NRF_CLOUD_EVT_RX_DATA_LOCATION"); - on_cloud_evt_location_data_received(&evt->data); - break; - case NRF_CLOUD_EVT_RX_DATA_SHADOW: - LOG_DBG("NRF_CLOUD_EVT_RX_DATA_SHADOW"); - break; - case NRF_CLOUD_EVT_USER_ASSOCIATION_REQUEST: - LOG_DBG("NRF_CLOUD_EVT_USER_ASSOCIATION_REQUEST"); - break; - case NRF_CLOUD_EVT_USER_ASSOCIATED: - LOG_DBG("NRF_CLOUD_EVT_USER_ASSOCIATED"); - break; - case NRF_CLOUD_EVT_FOTA_DONE: - LOG_DBG("NRF_CLOUD_EVT_FOTA_DONE"); - break; - default: - break; - } -} - -static void date_time_event_handler(const struct date_time_evt *evt) -{ - switch (evt->type) { - case DATE_TIME_OBTAINED_MODEM: - case DATE_TIME_OBTAINED_NTP: - case DATE_TIME_OBTAINED_EXT: - LOG_DBG("DATE_TIME OBTAINED"); - k_sem_give(&sem_date_time); - break; - case DATE_TIME_NOT_OBTAINED: - LOG_INF("DATE_TIME_NOT_OBTAINED"); - break; - default: - break; - } -} - -static int nrf_cloud_datamode_callback(uint8_t op, const uint8_t *data, int len) -{ - int ret = 0; - - if (op == DATAMODE_SEND) { - NRF_CLOUD_OBJ_PRE_ENC_DEFINE(obj, data, len); - - ret = do_cloud_send_obj(&obj); - LOG_INF("datamode send: %d", ret); - if (ret < 0) { - (void)exit_datamode(ret); - } - } else if (op == DATAMODE_EXIT) { - LOG_DBG("datamode exit"); - } - - return ret; -} - /**@brief handle AT#XGPS commands * AT#XGPS=[,[,]] * AT#XGPS? @@ -1045,73 +553,7 @@ int handle_at_gps(enum at_cmd_type cmd_type) return err; } -/**@brief handle AT#XNRFCLOUD commands - * AT#XNRFCLOUD=[,] - * AT#XNRFCLOUD? - * AT#XNRFCLOUD=? - */ -int handle_at_nrf_cloud(enum at_cmd_type cmd_type) -{ - int err = -EINVAL; - uint16_t op; - uint16_t signify = 0; - - switch (cmd_type) { - case AT_CMD_TYPE_SET_COMMAND: - err = at_params_unsigned_short_get(&at_param_list, 1, &op); - if (err < 0) { - return err; - } - if (op == nRF_CLOUD_CONNECT && !nrf_cloud_ready) { - location_signify = 0; - if (at_params_valid_count_get(&at_param_list) > 2) { - err = at_params_unsigned_short_get(&at_param_list, 2, &signify); - if (err < 0) { - return err; - } - location_signify = (signify > 0); - } - err = nrf_cloud_connect(); - if (err) { - LOG_ERR("Cloud connection failed, error: %d", err); - } else { - /* A-GPS & P-GPS needs date_time, trigger to update current time */ - date_time_update_async(date_time_event_handler); - if (k_sem_take(&sem_date_time, K_SECONDS(10)) != 0) { - LOG_WRN("Failed to get current time"); - } - } - } else if (op == nRF_CLOUD_SEND && nrf_cloud_ready) { - /* enter data mode */ - err = enter_datamode(nrf_cloud_datamode_callback); - } else if (op == nRF_CLOUD_DISCONNECT && nrf_cloud_ready) { - err = nrf_cloud_disconnect(); - if (err) { - LOG_ERR("Cloud disconnection failed, error: %d", err); - } - } else { - err = -EINVAL; - } break; - - case AT_CMD_TYPE_READ_COMMAND: { - rsp_send("\r\n#XNRFCLOUD: %d,%d,%d,\"%s\"\r\n", nrf_cloud_ready, - location_signify, CONFIG_NRF_CLOUD_SEC_TAG, device_id); - err = 0; - } break; - - case AT_CMD_TYPE_TEST_COMMAND: - rsp_send("\r\n#XNRFCLOUD: (%d,%d,%d),\r\n", - nRF_CLOUD_DISCONNECT, nRF_CLOUD_CONNECT, nRF_CLOUD_SEND); - err = 0; - break; - - default: - break; - } - - return err; -} - +#if defined(CONFIG_SLM_NRF_CLOUD) && defined(CONFIG_NRF_CLOUD_AGPS) /**@brief handle AT#XAGPS commands * AT#XAGPS=[,[,]] * AT#XAGPS? @@ -1194,8 +636,9 @@ int handle_at_agps(enum at_cmd_type cmd_type) return err; } +#endif /* CONFIG_NRF_CLOUD_AGPS */ -#if defined(CONFIG_SLM_PGPS) +#if defined(CONFIG_SLM_NRF_CLOUD) && defined(CONFIG_NRF_CLOUD_PGPS) /**@brief handle AT#XPGPS commands * AT#XPGPS=[,[,]] * AT#XPGPS? @@ -1280,7 +723,7 @@ int handle_at_pgps(enum at_cmd_type cmd_type) return err; } -#endif +#endif /* CONFIG_NRF_CLOUD_PGPS */ /**@brief handle AT#XGPSDEL commands * AT#XGPSDEL= @@ -1313,163 +756,11 @@ int handle_at_gps_delete(enum at_cmd_type cmd_type) return err; } -/**@brief handle AT#XCELLPOS commands - * AT#XCELLPOS= - * AT#XCELLPOS? - * AT#XCELLPOS=? - */ -int handle_at_cellpos(enum at_cmd_type cmd_type) -{ - int err = -EINVAL; - uint16_t op; - - switch (cmd_type) { - case AT_CMD_TYPE_SET_COMMAND: - err = at_params_unsigned_short_get(&at_param_list, 1, &op); - if (err < 0) { - return err; - } - if ((op == CELLPOS_START_SCELL || op == CELLPOS_START_MCELL) && - nrf_cloud_ready && run_type == RUN_TYPE_NONE) { - if (op == CELLPOS_START_SCELL) { - cell_pos_type = LOCATION_TYPE_SINGLE_CELL; - } else { - cell_pos_type = LOCATION_TYPE_MULTI_CELL; - } - ttft_start = k_uptime_get(); - k_work_submit_to_queue(&slm_work_q, &cell_pos_req); - run_type = RUN_TYPE_CELL_POS; - } else if (op == CELLPOS_STOP) { - run_type = RUN_TYPE_NONE; - } else { - err = -EINVAL; - } break; - - case AT_CMD_TYPE_READ_COMMAND: - rsp_send("\r\n#XCELLPOS: %d\r\n", (run_type == RUN_TYPE_CELL_POS) ? 1 : 0); - err = 0; - break; - - case AT_CMD_TYPE_TEST_COMMAND: - rsp_send("\r\n#XCELLPOS: (%d,%d,%d)\r\n", - CELLPOS_STOP, CELLPOS_START_SCELL, CELLPOS_START_MCELL); - err = 0; - break; - - default: - break; - } - - return err; -} - -/**@brief handle AT#XWIFIPOS commands - * AT#XWIFIPOS=[,,[,,[...]]] - * AT#XWIFIPOS? - * AT#XWIFIPOS=? - */ -int handle_at_wifipos(enum at_cmd_type cmd_type) -{ - int err = -EINVAL; - uint32_t count = at_params_valid_count_get(&at_param_list); - char mac_addr_str[WIFI_MAC_ADDR_STR_LEN]; - uint32_t mac_addr[WIFI_MAC_ADDR_LEN]; - struct wifi_scan_result *ap; - uint16_t op; - int index = 1; - int size; - - switch (cmd_type) { - case AT_CMD_TYPE_SET_COMMAND: - err = at_params_unsigned_short_get(&at_param_list, index, &op); - if (err < 0) { - return err; - } - - if (op == WIFIPOS_START && nrf_cloud_ready && run_type == RUN_TYPE_NONE) { - if (count < (2 + NRF_CLOUD_LOCATION_WIFI_AP_CNT_MIN * 2)) { - LOG_ERR("Too few APs %d, minimal %d", (count - 2) / 2, - NRF_CLOUD_LOCATION_WIFI_AP_CNT_MIN); - return -EINVAL; - } - - wifi_data.cnt = 0; - while (true) { - ap = &wifi_data.ap_info[wifi_data.cnt]; - - size = WIFI_SSID_MAX_LEN; - err = at_params_string_get(&at_param_list, ++index, ap->ssid, - &size); - if (err < 0) { - return err; - } - ap->ssid_length = size; - size = WIFI_MAC_ADDR_STR_LEN; - err = at_params_string_get(&at_param_list, ++index, mac_addr_str, - &size); - if (err < 0 || size != WIFI_MAC_ADDR_STR_LEN) { - return err; - } - err = sscanf(mac_addr_str, WIFI_MAC_ADDR_TEMPLATE, - &mac_addr[0], &mac_addr[1], &mac_addr[2], - &mac_addr[3], &mac_addr[4], &mac_addr[5]); - if (err != WIFI_MAC_ADDR_LEN) { - LOG_ERR("Parse MAC address %d error", wifi_data.cnt); - return -EINVAL; - } - ap->mac[0] = mac_addr[0]; - ap->mac[1] = mac_addr[1]; - ap->mac[2] = mac_addr[2]; - ap->mac[3] = mac_addr[3]; - ap->mac[4] = mac_addr[4]; - ap->mac[5] = mac_addr[5]; - ap->mac_length = WIFI_MAC_ADDR_LEN; - ap->channel = NRF_CLOUD_LOCATION_WIFI_OMIT_CHAN; - ap->rssi = NRF_CLOUD_LOCATION_WIFI_OMIT_RSSI; - - wifi_data.cnt++; - if (wifi_data.cnt == AP_CNT_MAX) { - break; /* max AP reached */ - } else if (index == count - 1) { - break; /* no more params */ - } - } - - ttft_start = k_uptime_get(); - k_work_submit_to_queue(&slm_work_q, &wifi_pos_req); - run_type = RUN_TYPE_WIFI_POS; - err = 0; - } else if (op == WIFIPOS_STOP) { - run_type = RUN_TYPE_NONE; - } else { - err = -EINVAL; - } break; - - case AT_CMD_TYPE_READ_COMMAND: - rsp_send("\r\n#XWIFIPOS: %d\r\n", (run_type == RUN_TYPE_WIFI_POS) ? 1 : 0); - err = 0; - break; - - case AT_CMD_TYPE_TEST_COMMAND: - rsp_send("\r\n#XWIFIPOS: (%d,%d)\r\n", WIFIPOS_STOP, WIFIPOS_START); - err = 0; - break; - - default: - break; - } - - return err; -} - /**@brief API to initialize GNSS AT commands handler */ int slm_at_gnss_init(void) { int err = 0; - struct nrf_cloud_init_param init_param = { - .event_handler = cloud_event_handler - }; err = nrf_modem_gnss_event_handler_set(gnss_event_handler); if (err) { @@ -1477,21 +768,13 @@ int slm_at_gnss_init(void) return err; } - err = nrf_cloud_init(&init_param); - if (err && err != -EACCES) { - LOG_ERR("Cloud could not be initialized, error: %d", err); - return err; - } - - k_work_init(&cloud_cmd, cloud_cmd_wk); +#if defined(CONFIG_NRF_CLOUD_AGPS) k_work_init(&agps_req, agps_req_wk); -#if defined(CONFIG_SLM_PGPS) +#endif +#if defined(CONFIG_NRF_CLOUD_PGPS) k_work_init(&pgps_req, pgps_req_wk); #endif - k_work_init(&cell_pos_req, cell_pos_req_wk); - k_work_init(&wifi_pos_req, wifi_pos_req_wk); k_work_init(&fix_rep, fix_rep_wk); - nrf_cloud_client_id_get(device_id, sizeof(device_id)); return err; } @@ -1500,10 +783,5 @@ int slm_at_gnss_init(void) */ int slm_at_gnss_uninit(void) { - if (nrf_cloud_ready) { - (void)nrf_cloud_disconnect(); - } - (void)nrf_cloud_uninit(); - return 0; } diff --git a/applications/serial_lte_modem/src/nrfcloud/CMakeLists.txt b/applications/serial_lte_modem/src/nrfcloud/CMakeLists.txt new file mode 100644 index 000000000000..7a235fccbc5c --- /dev/null +++ b/applications/serial_lte_modem/src/nrfcloud/CMakeLists.txt @@ -0,0 +1,8 @@ +# +# Copyright (c) 2023 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +zephyr_include_directories(.) +target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/slm_at_nrfcloud.c) diff --git a/applications/serial_lte_modem/src/nrfcloud/slm_at_nrfcloud.c b/applications/serial_lte_modem/src/nrfcloud/slm_at_nrfcloud.c new file mode 100644 index 000000000000..f50cdad50c90 --- /dev/null +++ b/applications/serial_lte_modem/src/nrfcloud/slm_at_nrfcloud.c @@ -0,0 +1,850 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "slm_util.h" +#include "slm_at_host.h" +#include "slm_at_nrfcloud.h" + +LOG_MODULE_REGISTER(slm_nrfcloud, CONFIG_SLM_LOG_LEVEL); + +#define SERVICE_INFO_GNSS \ + "{\"state\":{\"reported\":{\"device\": {\"serviceInfo\":{\"ui\":[\"GNSS\"]}}}}}" + +#define MODEM_AT_RSP \ + "{\"appId\":\"MODEM\", \"messageType\":\"RSP\", \"data\":\"%s\"}" + +/**@brief nRF Cloud operations. */ +enum slm_nrfcloud_operation { + SLM_NRF_CLOUD_DISCONNECT, + SLM_NRF_CLOUD_CONNECT, + SLM_NRF_CLOUD_SEND, + SLM_NRF_CLOUD_CELLPOS_STOP = SLM_NRF_CLOUD_DISCONNECT, + SLM_NRF_CLOUD_CELLPOS_START_SCELL = SLM_NRF_CLOUD_CONNECT, + SLM_NRF_CLOUD_CELLPOS_START_MCELL = SLM_NRF_CLOUD_SEND, + SLM_NRF_CLOUD_WIFIPOS_STOP = SLM_NRF_CLOUD_DISCONNECT, + SLM_NRF_CLOUD_WIFIPOS_START = SLM_NRF_CLOUD_CONNECT +}; + +static struct k_work cloud_cmd; +static K_SEM_DEFINE(sem_date_time, 0, 1); + +#if defined(CONFIG_NRF_CLOUD_LOCATION) +static struct k_work cell_pos_req; +static struct k_work wifi_pos_req; +static enum nrf_cloud_location_type cell_pos_type; +static uint64_t ttft_start; +static enum { + RUN_TYPE_NONE, + RUN_TYPE_CELL_POS, + RUN_TYPE_WIFI_POS +} run_type; + +/** Definitions for %NCELLMEAS notification + * %NCELLMEAS: status [,, , , , , + * , , ,,] + * [,1, 1, 1, 1,1] + * [,2, 2, 2, 2,2] ... + * [,17, 17, 17, 17,17 + * + * Max 17 ncell, but align with CONFIG_SLM_AT_MAX_PARAM + * 11 number of parameters for current cell (including "%NCELLMEAS") + * 5 number of parameters for one neighboring cell + */ +#define MAX_PARAM_CELL 11 +#define MAX_PARAM_NCELL 5 +/* Must support at least all params for current cell plus one ncell */ +BUILD_ASSERT(CONFIG_SLM_AT_MAX_PARAM > (MAX_PARAM_CELL + MAX_PARAM_NCELL), + "CONFIG_SLM_AT_MAX_PARAM too small"); +#define NCELL_CNT ((CONFIG_SLM_AT_MAX_PARAM - MAX_PARAM_CELL) / MAX_PARAM_NCELL) + +#define AP_CNT_MAX 5 + +static struct lte_lc_ncell neighbor_cells[NCELL_CNT]; +static struct lte_lc_cells_info cell_data = { + .neighbor_cells = neighbor_cells +}; +static int ncell_meas_status; +static struct wifi_scan_result scan_results[AP_CNT_MAX]; +static struct wifi_scan_info wifi_data = { + .ap_info = scan_results +}; +#endif /* CONFIG_NRF_CLOUD_LOCATION */ + +static char device_id[NRF_CLOUD_CLIENT_ID_MAX_LEN]; + +/* global variable to be used in different files */ +bool nrf_cloud_ready; +bool nrf_cloud_location_signify; + +/* global variable defined in different files */ +extern struct k_work_q slm_work_q; +extern struct at_param_list at_param_list; +extern uint8_t at_buf[SLM_AT_MAX_CMD_LEN]; + +#if defined(CONFIG_NRF_CLOUD_LOCATION) +AT_MONITOR(ncell_meas, "NCELLMEAS", ncell_meas_mon, PAUSED); + +static void ncell_meas_mon(const char *notify) +{ + int err; + uint32_t param_count; + char cid[9] = {0}; + size_t size; + char plmn[6] = {0}; + char mcc[4] = {0}; + char tac[9] = {0}; + + ncell_meas_status = -1; + at_params_list_clear(&at_param_list); + err = at_parser_params_from_str(notify, NULL, &at_param_list); + if (err) { + goto exit; + } + + /* parse status, 0: success 1: fail */ + err = at_params_int_get(&at_param_list, 1, &ncell_meas_status); + if (err) { + goto exit; + } + if (ncell_meas_status != 0) { + LOG_ERR("NCELLMEAS failed"); + err = -EAGAIN; + goto exit; + } + param_count = at_params_valid_count_get(&at_param_list); + if (param_count < MAX_PARAM_CELL) { /* at least current cell */ + LOG_ERR("Missing param in NCELLMEAS notification"); + err = -EAGAIN; + goto exit; + } + + /* parse Cell ID */ + size = sizeof(cid); + err = util_string_get(&at_param_list, 2, cid, &size); + if (err) { + goto exit; + } + err = util_str_to_int(cid, 16, (int *)&cell_data.current_cell.id); + if (err) { + goto exit; + } + + /* parse PLMN */ + size = sizeof(plmn); + err = util_string_get(&at_param_list, 3, plmn, &size); + if (err) { + goto exit; + } + strncpy(mcc, plmn, 3); /* MCC always 3-digit */ + err = util_str_to_int(mcc, 10, &cell_data.current_cell.mcc); + if (err) { + goto exit; + } + err = util_str_to_int(&plmn[3], 10, &cell_data.current_cell.mnc); + if (err) { + goto exit; + } + + /* parse TAC */ + size = sizeof(tac); + err = util_string_get(&at_param_list, 4, tac, &size); + if (err) { + goto exit; + } + err = util_str_to_int(tac, 16, (int *)&cell_data.current_cell.tac); + if (err) { + goto exit; + } + + /* omit timing_advance */ + cell_data.current_cell.timing_advance = NRF_CLOUD_LOCATION_CELL_OMIT_TIME_ADV; + + /* parse EARFCN */ + err = at_params_unsigned_int_get(&at_param_list, 6, &cell_data.current_cell.earfcn); + if (err) { + goto exit; + } + + /* parse PCI */ + err = at_params_unsigned_short_get(&at_param_list, 7, + &cell_data.current_cell.phys_cell_id); + if (err) { + goto exit; + } + + /* parse RSRP and RSRQ */ + err = at_params_short_get(&at_param_list, 8, &cell_data.current_cell.rsrp); + if (err < 0) { + goto exit; + } + err = at_params_short_get(&at_param_list, 9, &cell_data.current_cell.rsrq); + if (err < 0) { + goto exit; + } + + /* omit measurement_time and parse neighboring cells */ + cell_data.ncells_count = 0; + for (int i = 0; i < NCELL_CNT; i++) { + int offset = i * MAX_PARAM_NCELL + 11; + + if (param_count < (offset + MAX_PARAM_NCELL)) { + break; + } + + /* parse n_earfcn */ + err = at_params_unsigned_int_get(&at_param_list, offset, + &neighbor_cells[i].earfcn); + if (err < 0) { + goto exit; + } + + /* parse n_phys_cell_id */ + err = at_params_unsigned_short_get(&at_param_list, offset + 1, + &neighbor_cells[i].phys_cell_id); + if (err < 0) { + goto exit; + } + + /* parse n_rsrp */ + err = at_params_short_get(&at_param_list, offset + 2, &neighbor_cells[i].rsrp); + if (err < 0) { + goto exit; + } + + /* parse n_rsrq */ + err = at_params_short_get(&at_param_list, offset + 3, &neighbor_cells[i].rsrq); + if (err < 0) { + goto exit; + } + /* omit time_diff */ + + cell_data.ncells_count++; + } + + err = 0; + +exit: + LOG_INF("NCELLMEAS notification parse (err: %d)", err); +} + +static void cell_pos_req_wk(struct k_work *work) +{ + int err; + + ARG_UNUSED(work); + + if (cell_pos_type == LOCATION_TYPE_SINGLE_CELL) { + struct lte_lc_cells_info scell_inf = {.ncells_count = 0}; + + /* Obtain the single cell info from the modem */ + err = nrf_cloud_location_scell_data_get(&scell_inf.current_cell); + if (err) { + LOG_ERR("Failed to obtain cellular network info, error: %d", err); + return; + } + + /* Request location using only the single cell info */ + err = nrf_cloud_location_request(&scell_inf, NULL, true, NULL); + if (err) { + LOG_ERR("Failed to request SCELL, error: %d", err); + rsp_send("\r\n#XCELLPOS: %d\r\n", err); + run_type = RUN_TYPE_NONE; + } else { + LOG_INF("nRF Cloud SCELL requested"); + } + } else if (cell_pos_type == LOCATION_TYPE_MULTI_CELL) { + if (ncell_meas_status == 0 && cell_data.current_cell.id != 0) { + err = nrf_cloud_location_request(&cell_data, NULL, true, NULL); + if (err) { + LOG_ERR("Failed to request MCELL, error: %d", err); + rsp_send("\r\n#XCELLPOS: %d\r\n", err); + run_type = RUN_TYPE_NONE; + } else { + LOG_INF("nRF Cloud MCELL requested, with %d neighboring cells", + cell_data.ncells_count); + } + } else { + LOG_WRN("No request of MCELL"); + rsp_send("\r\n#XCELLPOS: \r\n"); + run_type = RUN_TYPE_NONE; + } + } +} + +static void wifi_pos_req_wk(struct k_work *work) +{ + int err; + + /* Request location using Wi-Fi access point info */ + err = nrf_cloud_location_request(NULL, &wifi_data, true, NULL); + if (err) { + LOG_ERR("Failed to request Wi-Fi location, error: %d", err); + rsp_send("\r\n#XWIFIPOS: %d\r\n", err); + run_type = RUN_TYPE_NONE; + } else { + LOG_INF("nRF Cloud Wi-Fi location requested"); + } +} +#endif /* CONFIG_NRF_CLOUD_LOCATION */ + +static int do_cloud_send_msg(const char *message, int len) +{ + int err; + struct nrf_cloud_tx_data msg = { + .data.ptr = message, + .data.len = len, + .topic_type = NRF_CLOUD_TOPIC_MESSAGE, + .qos = MQTT_QOS_0_AT_MOST_ONCE + }; + + err = nrf_cloud_send(&msg); + if (err) { + LOG_ERR("nrf_cloud_send failed, error: %d", err); + } + + return err; +} + +static void on_cloud_evt_ready(void) +{ + int err; + + if (nrf_cloud_location_signify) { + struct nrf_cloud_tx_data msg = { + .data.ptr = SERVICE_INFO_GNSS, + .data.len = strlen(SERVICE_INFO_GNSS), + .topic_type = NRF_CLOUD_TOPIC_STATE, + .qos = MQTT_QOS_0_AT_MOST_ONCE + }; + + /* Update nRF Cloud with GPS service info signifying GPS capabilities. */ + err = nrf_cloud_send(&msg); + if (err) { + LOG_WRN("Failed to send message to cloud, error: %d", err); + } + } + + nrf_cloud_ready = true; + rsp_send("\r\n#XNRFCLOUD: %d,%d\r\n", nrf_cloud_ready, nrf_cloud_location_signify); +#if defined(CONFIG_NRF_CLOUD_LOCATION) + at_monitor_resume(&ncell_meas); +#endif +} + +static void on_cloud_evt_disconnected(void) +{ + nrf_cloud_ready = false; + rsp_send("\r\n#XNRFCLOUD: %d,%d\r\n", nrf_cloud_ready, nrf_cloud_location_signify); +#if defined(CONFIG_NRF_CLOUD_LOCATION) + at_monitor_pause(&ncell_meas); +#endif +} + +static void on_cloud_evt_location_data_received(const struct nrf_cloud_data *const data) +{ +#if defined(CONFIG_NRF_CLOUD_LOCATION) + int err; + struct nrf_cloud_location_result result; + + if (run_type != RUN_TYPE_CELL_POS && run_type != RUN_TYPE_WIFI_POS) { + LOG_WRN("Not expecting location data during run_type: %d", run_type); + return; + } + + err = nrf_cloud_location_process(data->ptr, &result); + if (err == 0) { + if (ttft_start != 0) { + LOG_INF("TTFF %ds", (int)k_uptime_delta(&ttft_start)/1000); + ttft_start = 0; + } + if (run_type == RUN_TYPE_CELL_POS) { + rsp_send("\r\n#XCELLPOS: %d,%lf,%lf,%d\r\n", + result.type, result.lat, result.lon, result.unc); + } else { + rsp_send("\r\n#XWIFIPOS: %d,%lf,%lf,%d\r\n", + result.type, result.lat, result.lon, result.unc); + } + run_type = RUN_TYPE_NONE; + } else if (err == 1) { + LOG_WRN("No position found"); + } else if (err == -EFAULT) { + LOG_ERR("Unable to determine location from cell data, error: %d", + result.err); + } else { + LOG_ERR("Failed to process cellular location response, error: %d", + err); + } +#else + ARG_UNUSED(data); +#endif +} + +static void cloud_cmd_wk(struct k_work *work) +{ + int ret; + char *cmd_rsp; + + ARG_UNUSED(work); + + /* Send AT command to modem */ + ret = nrf_modem_at_cmd(at_buf, sizeof(at_buf), "%s", at_buf); + if (ret < 0) { + LOG_ERR("AT command failed: %d", ret); + return; + } else if (ret > 0) { + LOG_WRN("AT command error, type: %d", nrf_modem_at_err_type(ret)); + } + LOG_INF("MODEM RSP %s", at_buf); + /* replace \" with \' in JSON string-type value */ + for (int i = 0; i < strlen(at_buf); i++) { + if (at_buf[i] == '\"') { + at_buf[i] = '\''; + } + } + /* format JSON reply */ + cmd_rsp = k_malloc(strlen(at_buf) + sizeof(MODEM_AT_RSP)); + if (cmd_rsp == NULL) { + LOG_WRN("Unable to allocate buffer"); + return; + } + sprintf(cmd_rsp, MODEM_AT_RSP, at_buf); + /* Send AT response to cloud */ + ret = do_cloud_send_msg(cmd_rsp, strlen(cmd_rsp)); + if (ret) { + LOG_ERR("Send AT response to cloud error: %d", ret); + } + k_free(cmd_rsp); +} + +static bool handle_cloud_cmd(const char *buf_in) +{ + const cJSON *app_id = NULL; + const cJSON *msg_type = NULL; + const cJSON *at_cmd = NULL; + bool ret = false; + + cJSON *cloud_cmd_json = cJSON_Parse(buf_in); + + if (cloud_cmd_json == NULL) { + const char *error_ptr = cJSON_GetErrorPtr(); + + if (error_ptr != NULL) { + LOG_ERR("JSON parsing error before: %s", error_ptr); + } + goto end; + } + + app_id = cJSON_GetObjectItemCaseSensitive(cloud_cmd_json, NRF_CLOUD_JSON_APPID_KEY); + if (cJSON_GetStringValue(app_id) == NULL) { + goto end; + } + + /* Format expected from nrf cloud: + * {"appId":"MODEM", "messageType":"CMD", "data":""} + */ + if (strcmp(app_id->valuestring, NRF_CLOUD_JSON_APPID_VAL_MODEM) == 0) { + msg_type = cJSON_GetObjectItemCaseSensitive(cloud_cmd_json, + NRF_CLOUD_JSON_MSG_TYPE_KEY); + if (cJSON_GetStringValue(msg_type) != NULL) { + if (strcmp(msg_type->valuestring, NRF_CLOUD_JSON_MSG_TYPE_VAL_CMD) != 0) { + goto end; + } + } + + /* The value of attribute "data" contains the actual command */ + at_cmd = cJSON_GetObjectItemCaseSensitive(cloud_cmd_json, NRF_CLOUD_JSON_DATA_KEY); + if (cJSON_GetStringValue(at_cmd) != NULL) { + LOG_INF("MODEM CMD %s", at_cmd->valuestring); + strcpy(at_buf, at_cmd->valuestring); + k_work_submit_to_queue(&slm_work_q, &cloud_cmd); + ret = true; + } + /* Format expected from nrf cloud: + * {"appId":"DEVICE", "messageType":"DISCON"} + */ + } else if (strcmp(app_id->valuestring, NRF_CLOUD_JSON_APPID_VAL_DEVICE) == 0) { + msg_type = cJSON_GetObjectItemCaseSensitive(cloud_cmd_json, + NRF_CLOUD_JSON_MSG_TYPE_KEY); + if (cJSON_GetStringValue(msg_type) != NULL) { + if (strcmp(msg_type->valuestring, + NRF_CLOUD_JSON_MSG_TYPE_VAL_DISCONNECT) == 0) { + LOG_INF("DEVICE DISCON"); + /* No action required, handled in lib_nrf_cloud */ + ret = true; + } + } + } + +end: + cJSON_Delete(cloud_cmd_json); + return ret; +} + +static void on_cloud_evt_data_received(const struct nrf_cloud_data *const data) +{ + if (nrf_cloud_ready) { + if (((char *)data->ptr)[0] == '{') { + /* Check if it's a cloud command sent from the cloud */ + if (handle_cloud_cmd(data->ptr)) { + return; + } + } + rsp_send("\r\n#XNRFCLOUD: %s\r\n", (char *)data->ptr); + } +} + +static void cloud_event_handler(const struct nrf_cloud_evt *evt) +{ + switch (evt->type) { + case NRF_CLOUD_EVT_TRANSPORT_CONNECTING: + LOG_DBG("NRF_CLOUD_EVT_TRANSPORT_CONNECTING"); + if (evt->status != NRF_CLOUD_CONNECT_RES_SUCCESS) { + LOG_ERR("Failed to connect to nRF Cloud, status: %d", + (enum nrf_cloud_connect_result)evt->status); + } + break; + case NRF_CLOUD_EVT_TRANSPORT_CONNECTED: + LOG_INF("NRF_CLOUD_EVT_TRANSPORT_CONNECTED"); + break; + case NRF_CLOUD_EVT_READY: + LOG_INF("NRF_CLOUD_EVT_READY"); + on_cloud_evt_ready(); + break; + case NRF_CLOUD_EVT_TRANSPORT_DISCONNECTED: + LOG_INF("NRF_CLOUD_EVT_TRANSPORT_DISCONNECTED: status %d", + (enum nrf_cloud_disconnect_status)evt->status); + on_cloud_evt_disconnected(); + break; + case NRF_CLOUD_EVT_ERROR: + LOG_ERR("NRF_CLOUD_EVT_ERROR: %d", evt->status); + break; + case NRF_CLOUD_EVT_SENSOR_DATA_ACK: + LOG_DBG("NRF_CLOUD_EVT_SENSOR_DATA_ACK"); + break; + case NRF_CLOUD_EVT_RX_DATA_GENERAL: + LOG_INF("NRF_CLOUD_EVT_RX_DATA_GENERAL"); + on_cloud_evt_data_received(&evt->data); + break; + case NRF_CLOUD_EVT_RX_DATA_LOCATION: + LOG_INF("NRF_CLOUD_EVT_RX_DATA_LOCATION"); + on_cloud_evt_location_data_received(&evt->data); + break; + case NRF_CLOUD_EVT_RX_DATA_SHADOW: + LOG_DBG("NRF_CLOUD_EVT_RX_DATA_SHADOW"); + break; + case NRF_CLOUD_EVT_USER_ASSOCIATION_REQUEST: + LOG_DBG("NRF_CLOUD_EVT_USER_ASSOCIATION_REQUEST"); + break; + case NRF_CLOUD_EVT_USER_ASSOCIATED: + LOG_DBG("NRF_CLOUD_EVT_USER_ASSOCIATED"); + break; + case NRF_CLOUD_EVT_FOTA_DONE: + LOG_DBG("NRF_CLOUD_EVT_FOTA_DONE"); + break; + default: + break; + } +} + +static void date_time_event_handler(const struct date_time_evt *evt) +{ + switch (evt->type) { + case DATE_TIME_OBTAINED_MODEM: + case DATE_TIME_OBTAINED_NTP: + case DATE_TIME_OBTAINED_EXT: + LOG_DBG("DATE_TIME OBTAINED"); + k_sem_give(&sem_date_time); + break; + case DATE_TIME_NOT_OBTAINED: + LOG_INF("DATE_TIME_NOT_OBTAINED"); + break; + default: + break; + } +} + +static int nrf_cloud_datamode_callback(uint8_t op, const uint8_t *data, int len) +{ + int ret = 0; + + if (op == DATAMODE_SEND) { + ret = do_cloud_send_msg(data, len); + LOG_INF("datamode send: %d", ret); + if (ret < 0) { + (void)exit_datamode(ret); + } + } else if (op == DATAMODE_EXIT) { + LOG_DBG("datamode exit"); + } + + return ret; +} + +/**@brief handle AT#XNRFCLOUD commands + * AT#XNRFCLOUD=[,] + * AT#XNRFCLOUD? + * AT#XNRFCLOUD=? + */ +int handle_at_nrf_cloud(enum at_cmd_type cmd_type) +{ + int err = -EINVAL; + uint16_t op; + uint16_t signify = 0; + + switch (cmd_type) { + case AT_CMD_TYPE_SET_COMMAND: + err = at_params_unsigned_short_get(&at_param_list, 1, &op); + if (err < 0) { + return err; + } + if (op == SLM_NRF_CLOUD_CONNECT && !nrf_cloud_ready) { + nrf_cloud_location_signify = 0; + if (at_params_valid_count_get(&at_param_list) > 2) { + err = at_params_unsigned_short_get(&at_param_list, 2, &signify); + if (err < 0) { + return err; + } + nrf_cloud_location_signify = (signify > 0); + } + err = nrf_cloud_connect(); + if (err) { + LOG_ERR("Cloud connection failed, error: %d", err); + } else { + /* A-GPS & P-GPS needs date_time, trigger to update current time */ + date_time_update_async(date_time_event_handler); + if (k_sem_take(&sem_date_time, K_SECONDS(10)) != 0) { + LOG_WRN("Failed to get current time"); + } + } + } else if (op == SLM_NRF_CLOUD_SEND && nrf_cloud_ready) { + /* enter data mode */ + err = enter_datamode(nrf_cloud_datamode_callback); + } else if (op == SLM_NRF_CLOUD_DISCONNECT && nrf_cloud_ready) { + err = nrf_cloud_disconnect(); + if (err) { + LOG_ERR("Cloud disconnection failed, error: %d", err); + } + } else { + err = -EINVAL; + } break; + + case AT_CMD_TYPE_READ_COMMAND: { + rsp_send("\r\n#XNRFCLOUD: %d,%d,%d,\"%s\"\r\n", nrf_cloud_ready, + nrf_cloud_location_signify, CONFIG_NRF_CLOUD_SEC_TAG, device_id); + err = 0; + } break; + + case AT_CMD_TYPE_TEST_COMMAND: + rsp_send("\r\n#XNRFCLOUD: (%d,%d,%d),\r\n", + SLM_NRF_CLOUD_DISCONNECT, SLM_NRF_CLOUD_CONNECT, SLM_NRF_CLOUD_SEND); + err = 0; + break; + + default: + break; + } + + return err; +} + +#if defined(CONFIG_NRF_CLOUD_LOCATION) +/**@brief handle AT#XCELLPOS commands + * AT#XCELLPOS= + * AT#XCELLPOS? + * AT#XCELLPOS=? + */ +int handle_at_cellpos(enum at_cmd_type cmd_type) +{ + int err = -EINVAL; + uint16_t op; + + switch (cmd_type) { + case AT_CMD_TYPE_SET_COMMAND: + err = at_params_unsigned_short_get(&at_param_list, 1, &op); + if (err < 0) { + return err; + } + if ((op == SLM_NRF_CLOUD_CELLPOS_START_SCELL || + op == SLM_NRF_CLOUD_CELLPOS_START_MCELL) && + nrf_cloud_ready && run_type == RUN_TYPE_NONE) { + if (op == SLM_NRF_CLOUD_CELLPOS_START_SCELL) { + cell_pos_type = LOCATION_TYPE_SINGLE_CELL; + } else { + cell_pos_type = LOCATION_TYPE_MULTI_CELL; + } + ttft_start = k_uptime_get(); + k_work_submit_to_queue(&slm_work_q, &cell_pos_req); + run_type = RUN_TYPE_CELL_POS; + } else if (op == SLM_NRF_CLOUD_CELLPOS_STOP) { + run_type = RUN_TYPE_NONE; + } else { + err = -EINVAL; + } break; + + case AT_CMD_TYPE_READ_COMMAND: + rsp_send("\r\n#XCELLPOS: %d\r\n", (run_type == RUN_TYPE_CELL_POS) ? 1 : 0); + err = 0; + break; + + case AT_CMD_TYPE_TEST_COMMAND: + rsp_send("\r\n#XCELLPOS: (%d,%d,%d)\r\n", SLM_NRF_CLOUD_CELLPOS_STOP, + SLM_NRF_CLOUD_CELLPOS_START_SCELL, SLM_NRF_CLOUD_CELLPOS_START_MCELL); + err = 0; + break; + + default: + break; + } + + return err; +} + +/**@brief handle AT#XWIFIPOS commands + * AT#XWIFIPOS=[,,[,,[...]]] + * AT#XWIFIPOS? + * AT#XWIFIPOS=? + */ +int handle_at_wifipos(enum at_cmd_type cmd_type) +{ + int err = -EINVAL; + uint32_t count = at_params_valid_count_get(&at_param_list); + char mac_addr_str[WIFI_MAC_ADDR_STR_LEN]; + uint32_t mac_addr[WIFI_MAC_ADDR_LEN]; + struct wifi_scan_result *ap; + uint16_t op; + int index = 1; + int size; + + switch (cmd_type) { + case AT_CMD_TYPE_SET_COMMAND: + err = at_params_unsigned_short_get(&at_param_list, index, &op); + if (err < 0) { + return err; + } + + if (op == SLM_NRF_CLOUD_WIFIPOS_START && nrf_cloud_ready && + run_type == RUN_TYPE_NONE) { + if (count < (2 + NRF_CLOUD_LOCATION_WIFI_AP_CNT_MIN * 2)) { + LOG_ERR("Too few APs %d, minimal %d", (count - 2) / 2, + NRF_CLOUD_LOCATION_WIFI_AP_CNT_MIN); + return -EINVAL; + } + + wifi_data.cnt = 0; + while (true) { + ap = &wifi_data.ap_info[wifi_data.cnt]; + + size = WIFI_SSID_MAX_LEN; + err = at_params_string_get(&at_param_list, ++index, ap->ssid, + &size); + if (err < 0) { + return err; + } + ap->ssid_length = size; + size = WIFI_MAC_ADDR_STR_LEN; + err = at_params_string_get(&at_param_list, ++index, mac_addr_str, + &size); + if (err < 0 || size != WIFI_MAC_ADDR_STR_LEN) { + return err; + } + err = sscanf(mac_addr_str, WIFI_MAC_ADDR_TEMPLATE, + &mac_addr[0], &mac_addr[1], &mac_addr[2], + &mac_addr[3], &mac_addr[4], &mac_addr[5]); + if (err != WIFI_MAC_ADDR_LEN) { + LOG_ERR("Parse MAC address %d error", wifi_data.cnt); + return -EINVAL; + } + ap->mac[0] = mac_addr[0]; + ap->mac[1] = mac_addr[1]; + ap->mac[2] = mac_addr[2]; + ap->mac[3] = mac_addr[3]; + ap->mac[4] = mac_addr[4]; + ap->mac[5] = mac_addr[5]; + ap->mac_length = WIFI_MAC_ADDR_LEN; + ap->channel = NRF_CLOUD_LOCATION_WIFI_OMIT_CHAN; + ap->rssi = NRF_CLOUD_LOCATION_WIFI_OMIT_RSSI; + + wifi_data.cnt++; + if (wifi_data.cnt == AP_CNT_MAX) { + break; /* Max AP reached */ + } else if (index == count - 1) { + break; /* no more input params */ + } + } + + ttft_start = k_uptime_get(); + k_work_submit_to_queue(&slm_work_q, &wifi_pos_req); + run_type = RUN_TYPE_WIFI_POS; + err = 0; + } else if (op == SLM_NRF_CLOUD_WIFIPOS_STOP) { + run_type = RUN_TYPE_NONE; + } else { + err = -EINVAL; + } break; + + case AT_CMD_TYPE_READ_COMMAND: + rsp_send("\r\n#XWIFIPOS: %d\r\n", (run_type == RUN_TYPE_WIFI_POS) ? 1 : 0); + err = 0; + break; + + case AT_CMD_TYPE_TEST_COMMAND: + rsp_send("\r\n#XWIFIPOS: (%d,%d)\r\n", SLM_NRF_CLOUD_WIFIPOS_STOP, + SLM_NRF_CLOUD_WIFIPOS_START); + err = 0; + break; + + default: + break; + } + + return err; +} +#endif + +/**@brief API to initialize nRF Cloud AT commands handler + */ +int slm_at_nrfcloud_init(void) +{ + int err = 0; + struct nrf_cloud_init_param init_param = { + .event_handler = cloud_event_handler + }; + + err = nrf_cloud_init(&init_param); + if (err && err != -EACCES) { + LOG_ERR("Cloud could not be initialized, error: %d", err); + return err; + } + + k_work_init(&cloud_cmd, cloud_cmd_wk); +#if defined(CONFIG_NRF_CLOUD_LOCATION) + k_work_init(&cell_pos_req, cell_pos_req_wk); + k_work_init(&wifi_pos_req, wifi_pos_req_wk); +#endif + nrf_cloud_client_id_get(device_id, sizeof(device_id)); + + return err; +} + +/**@brief API to uninitialize nRF Cloud AT commands handler + */ +int slm_at_nrfcloud_uninit(void) +{ + if (nrf_cloud_ready) { + (void)nrf_cloud_disconnect(); + } + (void)nrf_cloud_uninit(); + + return 0; +} diff --git a/applications/serial_lte_modem/src/nrfcloud/slm_at_nrfcloud.h b/applications/serial_lte_modem/src/nrfcloud/slm_at_nrfcloud.h new file mode 100644 index 000000000000..c2bed9417b06 --- /dev/null +++ b/applications/serial_lte_modem/src/nrfcloud/slm_at_nrfcloud.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef SLM_AT_NRFCLOUD_ +#define SLM_AT_NRFCLOUD_ + +/**@file slm_at_nrfcloud.h + * + * @brief Vendor-specific AT command for nRF Cloud service. + * @{ + */ + +/** + * @brief Initialize nRF Cloud AT command parser. + * + * @retval 0 If the operation was successful. + * Otherwise, a (negative) error code is returned. + */ +int slm_at_nrfcloud_init(void); + +/** + * @brief Uninitialize nRF Cloud AT command parser. + * + * @retval 0 If the operation was successful. + * Otherwise, a (negative) error code is returned. + */ +int slm_at_nrfcloud_uninit(void); + +/** @} */ + +#endif /* SLM_AT_NRFCLOUD_ */ diff --git a/applications/serial_lte_modem/src/slm_at_commands.c b/applications/serial_lte_modem/src/slm_at_commands.c index 427a4c5cfe37..3157ee1f1a90 100644 --- a/applications/serial_lte_modem/src/slm_at_commands.c +++ b/applications/serial_lte_modem/src/slm_at_commands.c @@ -29,6 +29,9 @@ #include "slm_at_icmp.h" #include "slm_at_sms.h" #include "slm_at_fota.h" +#if defined(CONFIG_SLM_NRF_CLOUD) +#include "slm_at_nrfcloud.h" +#endif #if defined(CONFIG_SLM_GNSS) #include "slm_at_gnss.h" #endif @@ -415,16 +418,23 @@ int handle_at_sms(enum at_cmd_type cmd_type); /* FOTA commands */ int handle_at_fota(enum at_cmd_type cmd_type); +#if defined(CONFIG_SLM_NRF_CLOUD) +int handle_at_nrf_cloud(enum at_cmd_type cmd_type); +#if defined(CONFIG_NRF_CLOUD_LOCATION) +int handle_at_cellpos(enum at_cmd_type cmd_type); +int handle_at_wifipos(enum at_cmd_type cmd_type); +#endif +#endif + #if defined(CONFIG_SLM_GNSS) int handle_at_gps(enum at_cmd_type cmd_type); -int handle_at_nrf_cloud(enum at_cmd_type cmd_type); +int handle_at_gps_delete(enum at_cmd_type cmd_type); +#if defined(CONFIG_SLM_NRF_CLOUD) && defined(CONFIG_NRF_CLOUD_AGPS) int handle_at_agps(enum at_cmd_type cmd_type); -#if defined(CONFIG_SLM_PGPS) +#endif +#if defined(CONFIG_SLM_NRF_CLOUD) && defined(CONFIG_NRF_CLOUD_PGPS) int handle_at_pgps(enum at_cmd_type cmd_type); #endif -int handle_at_gps_delete(enum at_cmd_type cmd_type); -int handle_at_cellpos(enum at_cmd_type cmd_type); -int handle_at_wifipos(enum at_cmd_type cmd_type); #endif #if defined(CONFIG_SLM_FTPC) @@ -518,20 +528,23 @@ static struct slm_at_cmd { /* FOTA commands */ {"AT#XFOTA", handle_at_fota}, +#if defined(CONFIG_SLM_NRF_CLOUD) + {"AT#XNRFCLOUD", handle_at_nrf_cloud}, +#if defined(CONFIG_NRF_CLOUD_LOCATION) + {"AT#XCELLPOS", handle_at_cellpos}, + {"AT#XWIFIPOS", handle_at_wifipos}, +#endif +#endif + #if defined(CONFIG_SLM_GNSS) /* GNSS commands */ {"AT#XGPS", handle_at_gps}, - {"AT#XNRFCLOUD", handle_at_nrf_cloud}, -#if defined(CONFIG_SLM_AGPS) + {"AT#XGPSDEL", handle_at_gps_delete}, +#if defined(CONFIG_SLM_NRF_CLOUD) && defined(CONFIG_NRF_CLOUD_AGPS) {"AT#XAGPS", handle_at_agps}, #endif -#if defined(CONFIG_SLM_PGPS) +#if defined(CONFIG_SLM_NRF_CLOUD) && defined(CONFIG_NRF_CLOUD_PGPS) {"AT#XPGPS", handle_at_pgps}, -#endif - {"AT#XGPSDEL", handle_at_gps_delete}, -#if defined(CONFIG_SLM_LOCATION) - {"AT#XCELLPOS", handle_at_cellpos}, - {"AT#XWIFIPOS", handle_at_wifipos}, #endif #endif @@ -659,10 +672,17 @@ int slm_at_init(void) LOG_ERR("FOTA could not be initialized: %d", err); return -EFAULT; } +#if defined(CONFIG_SLM_NRF_CLOUD) + err = slm_at_nrfcloud_init(); + if (err) { + LOG_ERR("nRF Cloud could not be initialized: %d", err); + return -EFAULT; + } +#endif #if defined(CONFIG_SLM_GNSS) err = slm_at_gnss_init(); if (err) { - LOG_ERR("GPS could not be initialized: %d", err); + LOG_ERR("GNSS could not be initialized: %d", err); return -EFAULT; } #endif @@ -748,10 +768,16 @@ void slm_at_uninit(void) if (err) { LOG_WRN("FOTA could not be uninitialized: %d", err); } +#if defined(CONFIG_SLM_NRF_CLOUD) + err = slm_at_nrfcloud_uninit(); + if (err) { + LOG_WRN("nRF Cloud could not be uninitialized: %d", err); + } +#endif #if defined(CONFIG_SLM_GNSS) err = slm_at_gnss_uninit(); if (err) { - LOG_WRN("GPS could not be uninitialized: %d", err); + LOG_WRN("GNSS could not be uninitialized: %d", err); } #endif #if defined(CONFIG_SLM_FTPC) 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 3d146393414a..098abd745062 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -159,6 +159,10 @@ nRF9160: Asset Tracker v2 nRF9160: Serial LTE modem ------------------------- +* Updated: + + * The configuration to enable support for nRF Cloud A-GPS service and nRF Cloud Location service by default. + * Removed: * DFU AT commands ``#XDFUGET``, ``#XDFUSIZE`` and ``#XDFURUN`` because they were not usable without a custom application in the target (nRF52 series) device.