Skip to content

Commit

Permalink
modules: Handle desired configurations
Browse files Browse the repository at this point in the history
Handle desired configurations set in the shadow:
 - Add cddl schema for configurations set in the shadow
 - Add configuration channel that interested modules can subscribe to
 - Enable PWM and set RGB values according to the shadow configuration
   (Currently the colors are a bit off, but the concept is working)

Missing:
 - Reporting back to the shadows reported state (Do we need this?)
 - Adopting gnss enable and update interval.

Signed-off-by: Simen S. Røstad <[email protected]>
  • Loading branch information
simensrostad committed Jun 3, 2024
1 parent 7300a84 commit 904c73a
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 57 deletions.
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
4 changes: 2 additions & 2 deletions app/src/common/message_channel.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ 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),
Expand Down
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;
uint32_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
20 changes: 20 additions & 0 deletions app/src/modules/app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,23 @@
#

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
--encode # Generate encoding functions
--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/encoding the "board_config" 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)

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
83 changes: 44 additions & 39 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 "message_channel.h"
#include "app_object_encode.h" /* change this to config object and add led object here */
#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,42 +23,52 @@ 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(void)
{
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[2048] = { 0 };
size_t buf_len = sizeof(buf);
size_t not_used;

err = nrf_cloud_coap_shadow_get(buf, &buf_len, false, COAP_CONTENT_FORMAT_APP_CBOR);
if (err == -EACCES) {
LOG_WRN("Not connected yet.");
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;
}

return 0;
err = cbor_decode_app_object(buf, buf_len, &app_object, &not_used);
if (err) {
LOG_ERR("Failed to decode app object, error: %d", err);
SEND_FATAL_ERROR();
return;
}

return -EINVAL;
}

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("Configuration 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("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);

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;
}
Expand Down Expand Up @@ -84,12 +96,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 @@ -121,16 +127,15 @@ static void app_task(void)
LOG_INF("Cloud connected");
LOG_INF("Getting latest device configuration from cloud");

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

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

simple_config_update();
shadow_get();
}
}
}
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": lwm2m_object_1,
"14301:1.0": lwm2m_object
}

lwm2m_object_1 = {
"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
}

lwm2m_object = {
"0": lwm2m_inner_object
}

lwm2m_inner_object = {
"0": int .size 8,
"1": bool,
"99": int .size 8
}
58 changes: 51 additions & 7 deletions app/src/modules/led/led.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,70 @@
#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_WRN("LED configuration: red:%d, green:%d, blue:%d",
config->led_red, config->led_green, config->led_blue);

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;
}

err = pwm_set_dt(&led1, PWM_USEC(PWM_PERIOD),
PWM_USEC((PWM_PERIOD * config->led_green) / 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(&led2, PWM_USEC(PWM_PERIOD),
PWM_USEC((PWM_PERIOD * config->led_blue) / 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
1 change: 0 additions & 1 deletion app/src/modules/transport/Kconfig.transport
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

menu "Transport"
depends on NRF_CLOUD_COAP
depends on SIMPLE_CONFIG

config APP_TRANSPORT_RECONNECTION_TIMEOUT_SECONDS
int "Reconnection timeout in seconds"
Expand Down
2 changes: 1 addition & 1 deletion west.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ manifest:
- name: nrf
remote: ncs
repo-path: sdk-nrf
revision: ca9fd500085adefe4b742fba8166bd692e07f808
revision: pull/15669/head
import: true

0 comments on commit 904c73a

Please sign in to comment.