Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use nrf_cloud lib's new encode/decode functionality for nRF Cloud data #11403

Merged
merged 3 commits into from
Jun 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 56 additions & 93 deletions applications/serial_lte_modem/src/gnss/slm_at_gnss.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ LOG_MODULE_REGISTER(slm_gnss, CONFIG_SLM_LOG_LEVEL);
#define SERVICE_INFO_GNSS \
"{\"state\":{\"reported\":{\"device\": {\"serviceInfo\":{\"ui\":[\"GNSS\"]}}}}}"

#define MODEM_AT_RSP \
"{\"appId\":\"MODEM\", \"messageType\":\"RSP\", \"data\":\"%s\"}"

#define LOCATION_REPORT_MS 5000

/**@brief GNSS operations. */
Expand Down Expand Up @@ -538,12 +535,11 @@ static void on_gnss_evt_pvt(void)
}
}

static int do_cloud_send_msg(const char *message, int len)
static int do_cloud_send_obj(struct nrf_cloud_obj *const obj)
{
int err;
struct nrf_cloud_tx_data msg = {
.data.ptr = message,
.data.len = len,
.obj = obj,
.topic_type = NRF_CLOUD_TOPIC_MESSAGE,
.qos = MQTT_QOS_0_AT_MOST_ONCE
};
Expand All @@ -553,20 +549,21 @@ static int do_cloud_send_msg(const char *message, int len)
LOG_ERR("nrf_cloud_send failed, error: %d", err);
}

(void)nrf_cloud_obj_free(obj);

return err;
}

static void send_location(struct nrf_modem_gnss_nmea_data_frame * const nmea_data)
{
static int64_t last_ts_ms = NRF_CLOUD_NO_TIMESTAMP;
int err;
char *json_msg = NULL;
cJSON *msg_obj = NULL;
struct nrf_cloud_gnss_data gnss = {
.ts_ms = NRF_CLOUD_NO_TIMESTAMP,
.type = NRF_CLOUD_GNSS_TYPE_MODEM_NMEA,
.mdm_nmea = nmea_data
};
NRF_CLOUD_OBJ_JSON_DEFINE(msg_obj);

/* On failure, NRF_CLOUD_NO_TIMESTAMP is used and the timestamp is omitted */
(void)date_time_now(&gnss.ts_ms);
Expand All @@ -579,28 +576,10 @@ static void send_location(struct nrf_modem_gnss_nmea_data_frame * const nmea_dat
return;
}

msg_obj = cJSON_CreateObject();
if (!msg_obj) {
return;
}

err = nrf_cloud_gnss_msg_json_encode(&gnss, msg_obj);
if (err) {
goto clean_up;
}

json_msg = cJSON_PrintUnformatted(msg_obj);
if (!json_msg) {
err = -ENOMEM;
goto clean_up;
}

err = do_cloud_send_msg(json_msg, strlen(json_msg));

clean_up:
cJSON_Delete(msg_obj);
if (json_msg) {
cJSON_free((void *)json_msg);
/* Encode the location data into a device message */
err = nrf_cloud_obj_gnss_msg_create(&msg_obj, &gnss);
if (!err) {
err = do_cloud_send_obj(&msg_obj);
}

if (err) {
Expand Down Expand Up @@ -820,7 +799,7 @@ static void on_cloud_evt_location_data_received(const struct nrf_cloud_data *con
static void cloud_cmd_wk(struct k_work *work)
{
int ret;
char *cmd_rsp;
NRF_CLOUD_OBJ_JSON_DEFINE(cmd_rsp_obj);

ARG_UNUSED(work);

Expand All @@ -839,83 +818,65 @@ static void cloud_cmd_wk(struct k_work *work)
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");

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;
}
sprintf(cmd_rsp, MODEM_AT_RSP, at_buf);

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_msg(cmd_rsp, strlen(cmd_rsp));
ret = do_cloud_send_obj(&cmd_rsp_obj);
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)
static bool handle_cloud_cmd(const struct nrf_cloud_data *const data)
{
const cJSON *app_id = NULL;
const cJSON *msg_type = NULL;
struct nrf_cloud_obj input_obj;
char *at_cmd;
bool ret = false;
int err = nrf_cloud_obj_input_decode(&input_obj, data);

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":"<AT command>"}
*/
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;
}
}

const cJSON *at_cmd = NULL;

/* 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);
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;
}
/* 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;
}
}
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:
cJSON_Delete(cloud_cmd_json);
(void)nrf_cloud_obj_free(&input_obj);
return ret;
}

Expand All @@ -924,7 +885,7 @@ 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)) {
if (handle_cloud_cmd(data)) {
return;
}
}
Expand Down Expand Up @@ -1005,7 +966,9 @@ 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);
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);
Expand Down
5 changes: 5 additions & 0 deletions doc/nrf/releases/release-notes-changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ nRF9160 samples

* Added documentation for using the :ref:`lib_nrf_cloud_alert` and :ref:`lib_nrf_cloud_log` libraries.
* Changed the :file:`overlay_nrfcloud_logging.conf` file to enable JSON logs by default.
* The :c:struct:`nrf_cloud_obj` structure and associated functions are now used to encode and decode nRF Cloud data.

* :ref:`http_application_update_sample` sample:

Expand All @@ -234,6 +235,10 @@ nRF9160 samples

* Updated the sample to print its version when started.

* :ref:`modem_shell_application` sample:

* The sample now uses the :ref:`lib_nrf_cloud` library function :c:func:`nrf_cloud_obj_pgps_request_create` to create a P-GPS request.

Trusted Firmware-M (TF-M) samples
---------------------------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <net/nrf_cloud_agps.h>
#include <net/nrf_cloud_pgps.h>
#include <net/nrf_cloud_location.h>
#include <net/nrf_cloud_codec.h>
#include <modem/location.h>

#include "mosh_print.h"
Expand Down Expand Up @@ -38,81 +39,16 @@ void location_srv_ext_agps_handle(const struct nrf_modem_gnss_agps_data_frame *a
void location_srv_ext_pgps_handle(const struct gps_pgps_request *pgps_req)
{
int err = 0;
cJSON *data_obj;
cJSON *pgps_req_obj;
cJSON *ret;
cJSON *appid_key;
cJSON *msg_type_key;
char *msg_string = NULL;

/* Encode P-GPS request */
pgps_req_obj = cJSON_CreateObject();
if (pgps_req_obj == NULL) {
err = -ENOMEM;
goto cleanup;
}

appid_key = cJSON_AddStringToObjectCS(pgps_req_obj, NRF_CLOUD_JSON_APPID_KEY,
NRF_CLOUD_JSON_APPID_VAL_PGPS);
if (appid_key == NULL) {
mosh_error("Failed to add application id to P-GPS request");
err = -ENOMEM;
goto cleanup;
}
msg_type_key = cJSON_AddStringToObjectCS(pgps_req_obj, NRF_CLOUD_JSON_MSG_TYPE_KEY,
NRF_CLOUD_JSON_MSG_TYPE_VAL_DATA);
if (msg_type_key == NULL) {
mosh_error("Failed to add message type to P-GPS request");
err = -ENOMEM;
goto cleanup;
}

data_obj = cJSON_AddObjectToObject(pgps_req_obj, NRF_CLOUD_JSON_DATA_KEY);
if (data_obj == NULL) {
mosh_error("Failed to add pred count to P-GPS request");
err = -ENOMEM;
goto cleanup;
}
NRF_CLOUD_OBJ_JSON_DEFINE(pgps_obj);

ret = cJSON_AddNumberToObject(data_obj, NRF_CLOUD_JSON_PGPS_PRED_COUNT,
pgps_req->prediction_count);
if (ret == NULL) {
mosh_error("Failed to add pred count to P-GPS request");
err = -ENOMEM;
goto cleanup;
}
ret = cJSON_AddNumberToObject(data_obj, NRF_CLOUD_JSON_PGPS_INT_MIN,
pgps_req->prediction_period_min);
if (ret == NULL) {
mosh_error("Failed to add pred int min to P-GPS request");
err = -ENOMEM;
goto cleanup;
}
ret = cJSON_AddNumberToObject(data_obj, NRF_CLOUD_JSON_PGPS_GPS_DAY,
pgps_req->gps_day);
if (ret == NULL) {
mosh_error("Failed to add GPS day to P-GPS request");
err = -ENOMEM;
goto cleanup;
}
ret = cJSON_AddNumberToObject(data_obj, NRF_CLOUD_JSON_PGPS_GPS_TIME,
pgps_req->gps_time_of_day);
if (ret == NULL) {
mosh_error("Failed to add GPS time to P-GPS request");
err = -ENOMEM;
goto cleanup;
}

/* Convert CJSON object to string and send to nRF Cloud */
msg_string = cJSON_PrintUnformatted(pgps_req_obj);
if (msg_string == NULL) {
mosh_error("Could not allocate memory for request message");
err = nrf_cloud_obj_pgps_request_create(&pgps_obj, pgps_req);
if (err) {
mosh_error("Could not create P-GPS request message, error: %d", err);
goto cleanup;
}

struct nrf_cloud_tx_data mqtt_msg = {
.data.ptr = msg_string,
.data.len = strlen(msg_string),
.obj = &pgps_obj,
.qos = MQTT_QOS_1_AT_LEAST_ONCE,
.topic_type = NRF_CLOUD_TOPIC_MESSAGE,
};
Expand All @@ -123,8 +59,7 @@ void location_srv_ext_pgps_handle(const struct gps_pgps_request *pgps_req)
}

cleanup:
cJSON_Delete(pgps_req_obj);
cJSON_free(msg_string);
(void)nrf_cloud_obj_free(&pgps_obj);

if (err) {
mosh_error("nRF Cloud P-GPS request failed, error: %d", err);
Expand Down
6 changes: 3 additions & 3 deletions samples/nrf9160/nrf_cloud_mqtt_multi_service/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ This sample implements or demonstrates the following features:
* Periodic cellular, Wi-Fi, and GNSS location tracking using the :ref:`lib_location` library.
* Periodic temperature sensor sampling on your `Nordic Thingy:91`_, or fake temperature measurements on your `Nordic nRF9160 DK`_.
* Transmission of sensor and GNSS location samples to the nRF Cloud portal as `nRF Cloud device messages <nRF Cloud Device Messages_>`_.
* Construction of valid `nRF Cloud device messages <nRF Cloud Device Messages_>`_ using `cJSON`_.
* Construction of valid `nRF Cloud device messages <nRF Cloud Device Messages_>`_.
* Minimal LED status indication using the `Zephyr LED API`_.
* Transmission of an alert on sample startup using the :ref:`lib_nrf_cloud_alert` library.
* Transmission of additional alerts, whenever a specified temperature limit is exceeded.
Expand Down Expand Up @@ -117,7 +117,7 @@ It performs the following major tasks:

* Establishes periodic position tracking (which the :ref:`lib_location` library performs).
* Periodically samples temperature data (using the :file:`src/temperature.c` file).
* Constructs timestamped sensor sample and location `device messages <nRF Cloud Device Messages_>`_ using `cJSON`_.
* Constructs timestamped sensor sample and location `device messages <nRF Cloud Device Messages_>`_.
* Sends sensor sample and location device messages to the :ref:`nrf_cloud_mqtt_multi_service_device_message_queue`.
* Checks for and executes :ref:`remote modem AT command requests <nrf_cloud_mqtt_multi_service_remote_at>`.

Expand Down Expand Up @@ -289,7 +289,7 @@ This plot is useful for tracking, visualizing, and debugging connection loss, re
Device message formatting
=========================

This sample constructs JSON-based `device messages <nRF Cloud Device Messages_>`_ using `cJSON`_.
This sample constructs JSON-based `device messages <nRF Cloud Device Messages_>`_.

While any valid JSON string can be sent as a device message, and accepted and stored by `nRF Cloud`_, there are some pre-designed message structures, known as schemas.
The nRF Cloud portal knows how to interpret these schemas.
Expand Down
Loading