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

modules: Handle desired configurations #116

Merged
merged 1 commit into from
Jun 4, 2024
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
7 changes: 2 additions & 5 deletions app/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

CONFIG_NCS_SAMPLES_DEFAULTS=y
CONFIG_RESET_ON_FATAL_ERROR=y
CONFIG_DK_LIBRARY=y
CONFIG_PWM=y

# Heap and stacks
# Extended AT host/monitor stack/heap sizes since some nrf_cloud credentials are longer than 1024 bytes.
Expand Down Expand Up @@ -105,11 +107,6 @@ CONFIG_NRF_CLOUD_COAP_SEC_TAG=4242
#Required if device is provisioned to nRF Cloud using attestation token
#CONFIG_NRF_CLOUD_CLIENT_ID_SRC_INTERNAL_UUID=y

CONFIG_SIMPLE_CONFIG=y
CONFIG_DK_LIBRARY=y

CONFIG_SIMPLE_CONFIG_LOG_LEVEL_DBG=y

# needed for processing floats
CONFIG_PICOLIBC=y
CONFIG_FPU=y
Expand Down
8 changes: 4 additions & 4 deletions app/src/common/message_channel.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,18 @@ ZBUS_CHAN_DEFINE(FATAL_ERROR_CHAN,
ZBUS_MSG_INIT(0)
);

ZBUS_CHAN_DEFINE(LED_CHAN,
int,
ZBUS_CHAN_DEFINE(CONFIG_CHAN,
struct configuration,
NULL,
NULL,
ZBUS_OBSERVERS(led),
ZBUS_OBSERVERS(led, trigger),
ZBUS_MSG_INIT(0)
);

ZBUS_CHAN_DEFINE(CLOUD_CHAN,
enum cloud_status,
NULL,
NULL,
ZBUS_OBSERVERS(fota, app, location),
ZBUS_OBSERVERS(fota, app, location, trigger),
CLOUD_DISCONNECTED
);
11 changes: 10 additions & 1 deletion app/src/common/message_channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,23 @@ enum cloud_status {
CLOUD_DISCONNECTED,
};

struct configuration {
int led_red;
int led_green;
int led_blue;
bool gnss;
uint64_t update_interval;
};

ZBUS_CHAN_DECLARE(
TRIGGER_CHAN,
PAYLOAD_CHAN,
NETWORK_CHAN,
FATAL_ERROR_CHAN,
LED_CHAN,
CLOUD_CHAN,
FOTA_ONGOING_CHAN
FOTA_ONGOING_CHAN,
CONFIG_CHAN
);

#ifdef __cplusplus
Expand Down
27 changes: 27 additions & 0 deletions app/src/modules/app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,30 @@
#

target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/app.c)

# generate encoder code using zcbor
set(zcbor_command
zcbor code # Invoke code generation
--cddl ${ZEPHYR_BASE}/subsys/net/lib/lwm2m/lwm2m_senml_cbor.cddl
--cddl ${CMAKE_CURRENT_SOURCE_DIR}/app_object.cddl
--decode # Generate decoding functions
--short-names # Attempt to make generated symbol names shorter (at the risk of collision)
-t app-object # Create a public API for decoding the "app_object" type from the cddl file
--output-cmake ${CMAKE_CURRENT_BINARY_DIR}/app_object.cmake # The generated cmake file will be placed here
)
execute_process(COMMAND ${zcbor_command} COMMAND_ERROR_IS_FATAL ANY)

# Include the cmake file generated by zcbor. It adds the
# generated code and the necessary zcbor C code files.
include(${CMAKE_CURRENT_BINARY_DIR}/app_object.cmake)

simensrostad marked this conversation as resolved.
Show resolved Hide resolved
# Ensure that the cmake reconfiguration is triggerred everytime the cddl file changes.
# This ensures that the codec generation is triggered.
set_property(
DIRECTORY
PROPERTY
CMAKE_CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/app_object.cddl
)

zephyr_link_libraries(app_object)
target_link_libraries(app_object PRIVATE zephyr_interface)
2 changes: 1 addition & 1 deletion app/src/modules/app/Kconfig.app
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ menu "App"

config APP_MODULE_THREAD_STACK_SIZE
int "Thread stack size"
default 4096
default 8192

config APP_MODULE_MESSAGE_QUEUE_SIZE
int "Message queue size"
Expand Down
117 changes: 75 additions & 42 deletions app/src/modules/app/app.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/zbus/zbus.h>
#include <simple_config/simple_config.h>
#include <zephyr/task_wdt/task_wdt.h>
#include <net/nrf_cloud_coap.h>
#include <nrf_cloud_coap_transport.h>

#include "message_channel.h"
#include "app_object_decode.h" /* change this to config object and add led object here */

/* Register log module */
LOG_MODULE_REGISTER(app, CONFIG_APP_LOG_LEVEL);
Expand All @@ -21,45 +23,86 @@ ZBUS_SUBSCRIBER_DEFINE(app, CONFIG_APP_MODULE_MESSAGE_QUEUE_SIZE);
BUILD_ASSERT(CONFIG_APP_MODULE_WATCHDOG_TIMEOUT_SECONDS > CONFIG_APP_MODULE_EXEC_TIME_SECONDS_MAX,
"Watchdog timeout must be greater than maximum execution time");

int config_cb(const char *key, const struct simple_config_val *val)
static void shadow_get(bool get_desired)
{
int err;

if (val->type == SIMPLE_CONFIG_VAL_STRING) {
LOG_DBG("\"%s\" = %s", key, val->val._str);
} else if (val->type == SIMPLE_CONFIG_VAL_BOOL) {
LOG_DBG("\"%s\" = %s", key, val->val._bool ? "true" : "false");
} else if (val->type == SIMPLE_CONFIG_VAL_DOUBLE) {
LOG_DBG("\"%s\" = %f", key, val->val._double);
} else {
return -EINVAL;
struct app_object app_object = { 0 };
uint8_t buf_cbor[512] = { 0 };
uint8_t buf_json[512] = { 0 };
size_t buf_cbor_len = sizeof(buf_cbor);
size_t buf_json_len = sizeof(buf_json);
size_t not_used;

/* Request shadow delta, request only changes by setting delta parameter to true. */
err = nrf_cloud_coap_shadow_get(buf_json, &buf_json_len, !get_desired,
COAP_CONTENT_FORMAT_APP_JSON);
if (err == -EACCES) {
LOG_WRN("Not connected");
return;
} else if (err) {
LOG_ERR("Failed to request shadow delta: %d", err);
SEND_FATAL_ERROR();
return;
}

if (strcmp(key, "led_blue") == 0) {
int status = val->val._bool;

err = zbus_chan_pub(&LED_CHAN, &status, K_NO_WAIT);
if (err) {
LOG_ERR("zbus_chan_pub, error:%d", err);
SEND_FATAL_ERROR();
return err;
}
if (buf_json_len == 0 || strlen(buf_json) == 0) {
LOG_WRN("No shadow delta changes available");
return;
}

return 0;
/* Shadow available, fetch CBOR encoded desired section. */
err = nrf_cloud_coap_shadow_get(buf_cbor, &buf_cbor_len, false,
COAP_CONTENT_FORMAT_APP_CBOR);
if (err == -EACCES) {
LOG_WRN("Not connected");
return;
} else if (err) {
LOG_ERR("Failed to request shadow delta: %d", err);
SEND_FATAL_ERROR();
return;
}

return -EINVAL;
}
err = cbor_decode_app_object(buf_cbor, buf_cbor_len, &app_object, &not_used);
if (err) {
LOG_ERR("Failed to decode app object, error: %d", err);
SEND_FATAL_ERROR();
return;
}

static void init_app_settings(void)
{
struct simple_config_val val = {.type = SIMPLE_CONFIG_VAL_BOOL, .val._bool = true};
int err = simple_config_set("blue_led", &val);
struct configuration configuration = {
.led_red = app_object.lwm2m._1424010._0._0,
.led_green = app_object.lwm2m._1424010._0._1,
.led_blue = app_object.lwm2m._1424010._0._2,
.gnss = app_object.lwm2m._1430110._0._1,
.update_interval = app_object.lwm2m._1430110._0._0,
};

LOG_DBG("LED object (1424010) values received from cloud:");
LOG_DBG("R: %d", app_object.lwm2m._1424010._0._0);
LOG_DBG("G: %d", app_object.lwm2m._1424010._0._1);
LOG_DBG("B: %d", app_object.lwm2m._1424010._0._2);
LOG_DBG("Timestamp: %lld", app_object.lwm2m._1424010._0._99);

LOG_DBG("Application configuration object (1430110) values received from cloud:");
LOG_DBG("Update interval: %lld", app_object.lwm2m._1430110._0._0);
LOG_DBG("GNSS: %d", app_object.lwm2m._1430110._0._1);
LOG_DBG("Timestamp: %lld", app_object.lwm2m._1430110._0._99);

/* Distribute configuration */
err = zbus_chan_pub(&CONFIG_CHAN, &configuration, K_SECONDS(1));
if (err) {
LOG_ERR("simple_config_set, error: %d", err);
LOG_ERR("zbus_chan_pub, error: %d", err);
SEND_FATAL_ERROR();
return;
}

/* Send the received configuration back to the reported shadow section. */
err = nrf_cloud_coap_shadow_state_update(buf_json);
if (err < 0) {
LOG_ERR("Failed to send PATCH request: %d", err);
} else if (err > 0) {
LOG_ERR("Error from server: %d", err);
}
}

static void task_wdt_callback(int channel_id, void *user_data)
Expand All @@ -84,12 +127,6 @@ static void app_task(void)

task_wdt_id = task_wdt_add(wdt_timeout_ms, task_wdt_callback, (void *)k_current_get());

/* Set up callback for runtime config changes from cloud */
simple_config_set_callback(config_cb);

/* Set initial settting values (can be skipped if cloud initializes shadow) */
init_app_settings();

while (true) {
err = task_wdt_feed(task_wdt_id);
if (err) {
Expand Down Expand Up @@ -118,19 +155,15 @@ static void app_task(void)
}

if (cloud_status == CLOUD_CONNECTED) {
LOG_INF("Cloud connected");
LOG_INF("Getting latest device configuration from cloud");
LOG_DBG("Cloud connected");
LOG_DBG("Getting latest device configuration from device shadow");

simple_config_update();
} else {
LOG_INF("Cloud disconnected");
shadow_get(true);
}
}

if ((&TRIGGER_CHAN == chan) && (cloud_status == CLOUD_CONNECTED)) {
LOG_INF("Getting latest device configuration from cloud");

simple_config_update();
shadow_get(false);
}
}
}
Expand Down
41 changes: 41 additions & 0 deletions app/src/modules/app/app_object.cddl
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
; Define the basic structure of the JSON object
app-object = {
"nrfcloud_mqtt_topic_prefix": tstr,
"pairing": pairing-type,
"lwm2m": lwm2m-map
}

; Define the pairing object with nested topics
pairing-type = {
"state": tstr,
"topics": {
"d2c": tstr,
"c2d": tstr
}
}

lwm2m-map = {
"14240:1.0": led,
"14301:1.0": config
}

led = {
"0": lwm2m_inner_object_1
}

lwm2m_inner_object_1 = {
"0": int .size 4,
"1": int .size 4,
"2": int .size 4,
"99": int .size 8
}

config = {
"0": lwm2m_inner_object
}

lwm2m_inner_object = {
"0": int .size 8,
"1": bool,
"99": int .size 8
}
59 changes: 53 additions & 6 deletions app/src/modules/led/led.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,73 @@
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/zbus/zbus.h>
#include <zephyr/drivers/led.h>
#include <zephyr/drivers/pwm.h>
#include <dk_buttons_and_leds.h>

#include "message_channel.h"

/* Register log module */
LOG_MODULE_REGISTER(led, CONFIG_APP_LED_LOG_LEVEL);

#define PWM_LED0_NODE DT_ALIAS(pwm_led0)
#define PWM_LED1_NODE DT_ALIAS(pwm_led1)
#define PWM_LED2_NODE DT_ALIAS(pwm_led2)

#if DT_NODE_HAS_STATUS(PWM_LED0_NODE, okay)
static const struct pwm_dt_spec led0 = PWM_DT_SPEC_GET(PWM_LED0_NODE);
#else
#error "Unsupported board: pwm-led 0 devicetree alias is not defined"
#endif
#if DT_NODE_HAS_STATUS(PWM_LED1_NODE, okay)
static const struct pwm_dt_spec led1 = PWM_DT_SPEC_GET(PWM_LED1_NODE);
#else
#error "Unsupported board: pwm-led 1 devicetree alias is not defined"
#endif
#if DT_NODE_HAS_STATUS(PWM_LED2_NODE, okay)
static const struct pwm_dt_spec led2 = PWM_DT_SPEC_GET(PWM_LED2_NODE);
#else
#error "Unsupported board: pwm-led 2 devicetree alias is not defined"
#endif

#define PWM_PERIOD 20000U
#define LIGHTNESS_MAX UINT8_MAX

void led_callback(const struct zbus_channel *chan)
{
int err;
const int *status;

if (&LED_CHAN == chan) {
/* Get LED status from channel. */
status = zbus_chan_const_msg(chan);
if (&CONFIG_CHAN == chan) {
/* Get LED configuration from channel. */

const struct configuration *config = zbus_chan_const_msg(chan);

LOG_DBG("LED configuration: red:%d, green:%d, blue:%d",
config->led_red, config->led_green, config->led_blue);

/* Red LED */
err = pwm_set_dt(&led0, PWM_USEC(PWM_PERIOD),
PWM_USEC((PWM_PERIOD * config->led_red) / LIGHTNESS_MAX));
if (err) {
LOG_ERR("pwm_set_dt, error:%d", err);
SEND_FATAL_ERROR();
return;
}

/* Blue LED */
err = dk_set_led(DK_LED2, *status);
err = pwm_set_dt(&led1, PWM_USEC(PWM_PERIOD),
PWM_USEC((PWM_PERIOD * config->led_blue) / LIGHTNESS_MAX));
if (err) {
LOG_ERR("pwm_set_dt, error:%d", err);
SEND_FATAL_ERROR();
return;
}

/* Green LED */
err = pwm_set_dt(&led2, PWM_USEC(PWM_PERIOD),
PWM_USEC((PWM_PERIOD * config->led_green) / LIGHTNESS_MAX));
if (err) {
LOG_ERR("dk_set_led, error:%d", err);
LOG_ERR("pwm_set_dt, error:%d", err);
SEND_FATAL_ERROR();
return;
}
Expand Down
Loading
Loading