Skip to content

Commit 538f8c7

Browse files
Trond-F-Christiansenjtguggedal
authored andcommitted
app: shell: add att_inspect command
Add a new shell command, att_inspect, that provides an overview of the current state of the application and its modules. Registering each module to the shell command with macros using iterable sections. Signed-off-by: Trond F. Christiansen <trond.christiansen@nordicsemi.no>
1 parent ee8d20a commit 538f8c7

File tree

15 files changed

+431
-0
lines changed

15 files changed

+431
-0
lines changed

app/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@ add_subdirectory_ifdef(CONFIG_APP_CLOUD src/modules/cloud)
3030
add_subdirectory_ifdef(CONFIG_APP_FOTA src/modules/fota)
3131
add_subdirectory_ifdef(CONFIG_APP_STORAGE src/modules/storage)
3232
add_subdirectory_ifdef(CONFIG_APP_SGP4 src/modules/sgp4)
33+
add_subdirectory_ifdef(CONFIG_APP_INSPECT_SHELL src/inspect)

app/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ rsource "src/modules/environmental/Kconfig.environmental"
1717
rsource "src/modules/button/Kconfig.button"
1818
rsource "src/modules/storage/Kconfig.storage"
1919
rsource "src/modules/sgp4/Kconfig.sgp4"
20+
rsource "src/inspect/Kconfig.inspect"
2021
endmenu
2122

2223
menu "Zephyr Kernel"

app/src/inspect/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#
2+
# Copyright (c) 2026 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/inspect_shell.c)
8+
zephyr_linker_sources(SECTIONS inspect_sections.ld)
9+
target_include_directories(app PRIVATE .)

app/src/inspect/Kconfig.inspect

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#
2+
# Copyright (c) 2026 Nordic Semiconductor
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
menu "Inspect"
8+
9+
config APP_INSPECT_SHELL
10+
bool "Inspect shell command"
11+
depends on SHELL
12+
default y
13+
help
14+
Enable the 'att_inspect' shell command.
15+
16+
endmenu # Inspect

app/src/inspect/app_inspect.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright (c) 2026 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
#ifndef _APP_INSPECT_H_
8+
#define _APP_INSPECT_H_
9+
10+
#include <zephyr/sys/iterable_sections.h>
11+
12+
#ifdef __cplusplus
13+
extern "C" {
14+
#endif
15+
16+
struct app_inspect_provider {
17+
const char *name;
18+
const char *(*get_state_name)(void);
19+
};
20+
21+
#if defined(CONFIG_APP_INSPECT_SHELL)
22+
/* Macro to register an inspect provider for a module.
23+
* The provider will be used to print the state of the module in the inspect shell.
24+
*/
25+
#define APP_INSPECT_MODULE_REGISTER(_name, _get_state_name_fn) \
26+
STRUCT_SECTION_ITERABLE(app_inspect_provider, _name##_inspect_provider) = { \
27+
.name = #_name, \
28+
.get_state_name = _get_state_name_fn, \
29+
}
30+
31+
/* Macro to register an inspect provider and state names */
32+
#define APP_INSPECT_MODULE_REGISTER_STATE(_name, _state_ctx_ptr, _states, _state_enum_t, \
33+
_to_string_fn) \
34+
/* Create a function that translates the current state to a string */ \
35+
static const char *_name##_state_name_get(void) \
36+
{ \
37+
const struct smf_state *current; \
38+
\
39+
if ((_state_ctx_ptr) == NULL) { \
40+
return "STATE_UNINITIALIZED"; \
41+
} \
42+
\
43+
current = smf_get_current_leaf_state(SMF_CTX(_state_ctx_ptr)); \
44+
\
45+
for (size_t i = 0; i < ARRAY_SIZE(_states); i++) { \
46+
if (current == &(_states)[i]) { \
47+
return _to_string_fn((_state_enum_t)i); \
48+
} \
49+
} \
50+
\
51+
return "STATE_UNKNOWN"; \
52+
} \
53+
APP_INSPECT_MODULE_REGISTER(_name, _name##_state_name_get)
54+
#else
55+
/* If the inspect shell is not enabled, define empty macros to avoid compilation errors. */
56+
#define APP_INSPECT_MODULE_REGISTER(_name, _get_state_name_fn)
57+
#define APP_INSPECT_MODULE_REGISTER_STATE(_name, _state_ctx_ptr, _states, _state_enum_t, \
58+
_to_string_fn)
59+
#endif
60+
61+
#ifdef __cplusplus
62+
}
63+
#endif
64+
65+
#endif /* _APP_INSPECT_H_ */
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/*
2+
* Copyright (c) 2026 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
ITERABLE_SECTION_ROM(app_inspect_provider, Z_LINK_ITERABLE_SUBALIGN)

app/src/inspect/inspect_shell.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (c) 2026 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
#include <zephyr/kernel.h>
8+
#include <zephyr/shell/shell.h>
9+
#include <zephyr/sys/iterable_sections.h>
10+
11+
#include <errno.h>
12+
#include <strings.h>
13+
14+
#include "app_inspect.h"
15+
16+
/**
17+
* @brief Format string for printing module state in inspect shell.
18+
*/
19+
#define INSPECT_PRINT_FMT "%-14s | %s"
20+
21+
static int cmd_att_inspect(const struct shell *sh, size_t argc, char **argv)
22+
{
23+
STRUCT_SECTION_FOREACH(app_inspect_provider, provider) {
24+
const char *state_name = "STATE_UNKNOWN";
25+
26+
if ((provider->get_state_name != NULL)) {
27+
state_name = provider->get_state_name();
28+
}
29+
30+
shell_print(sh, INSPECT_PRINT_FMT, provider->name, state_name);
31+
}
32+
33+
return 0;
34+
}
35+
36+
SHELL_CMD_REGISTER(att_inspect, NULL, "Inspect module SMF states: att_inspect", cmd_att_inspect);

app/src/main.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
#include <zephyr/sys/reboot.h>
1414

1515
#include "app_common.h"
16+
#ifdef CONFIG_APP_INSPECT_SHELL
17+
#include "app_inspect.h"
18+
#endif /* CONFIG_APP_INSPECT_SHELL */
1619
#include "button.h"
1720
#include "modules/button/button.h"
1821
#include "modules/storage/storage.h"
@@ -380,6 +383,54 @@ static const struct smf_state states[] = {
380383
),
381384
};
382385

386+
#if defined(CONFIG_APP_INSPECT_SHELL)
387+
static struct main_state *main_state_ctx;
388+
389+
static const char *main_state_to_string(enum app_state state)
390+
{
391+
switch (state) {
392+
case STATE_WAITING_FOR_MODULES_INIT:
393+
return "STATE_WAITING_FOR_MODULES_INIT";
394+
case STATE_RUNNING:
395+
return "STATE_RUNNING";
396+
case STATE_DISCONNECTED:
397+
return "STATE_DISCONNECTED";
398+
case STATE_DISCONNECTED_SAMPLING:
399+
return "STATE_DISCONNECTED_SAMPLING";
400+
case STATE_DISCONNECTED_WAITING:
401+
return "STATE_DISCONNECTED_WAITING";
402+
case STATE_CONNECTED:
403+
return "STATE_CONNECTED";
404+
case STATE_CONNECTED_SAMPLING:
405+
return "STATE_CONNECTED_SAMPLING";
406+
case STATE_CONNECTED_WAITING:
407+
return "STATE_CONNECTED_WAITING";
408+
case STATE_CONNECTED_SENDING:
409+
return "STATE_CONNECTED_SENDING";
410+
case STATE_FOTA:
411+
return "STATE_FOTA";
412+
case STATE_FOTA_DOWNLOADING:
413+
return "STATE_FOTA_DOWNLOADING";
414+
case STATE_FOTA_WAITING_FOR_NETWORK_DISCONNECT:
415+
return "STATE_FOTA_WAITING_FOR_NETWORK_DISCONNECT";
416+
case STATE_FOTA_WAITING_FOR_NETWORK_DISCONNECT_TO_APPLY_IMAGE:
417+
return "STATE_FOTA_WAITING_FOR_NETWORK_DISCONNECT_TO_APPLY_IMAGE";
418+
case STATE_FOTA_APPLYING_IMAGE:
419+
return "STATE_FOTA_APPLYING_IMAGE";
420+
case STATE_FOTA_REBOOTING:
421+
return "STATE_FOTA_REBOOTING";
422+
default:
423+
return "STATE_UNKNOWN";
424+
}
425+
}
426+
427+
APP_INSPECT_MODULE_REGISTER_STATE(main,
428+
main_state_ctx,
429+
states,
430+
enum app_state,
431+
main_state_to_string);
432+
#endif /* CONFIG_APP_INSPECT_SHELL */
433+
383434
/* Static helper function */
384435

385436
static void task_wdt_callback(int channel_id, void *user_data)
@@ -1760,6 +1811,10 @@ int main(void)
17601811
const k_timeout_t zbus_wait_ms = K_MSEC(wdt_timeout_ms - execution_time_ms);
17611812
static struct main_state main_state;
17621813

1814+
#if defined(CONFIG_APP_INSPECT_SHELL)
1815+
main_state_ctx = &main_state;
1816+
#endif /* CONFIG_APP_INSPECT_SHELL */
1817+
17631818
main_state.sample_interval_sec = CONFIG_APP_SAMPLING_INTERVAL_SECONDS;
17641819
main_state.update_interval_sec = CONFIG_APP_CLOUD_UPDATE_INTERVAL_SECONDS;
17651820
main_state.storage_threshold = CONFIG_APP_STORAGE_INITIAL_THRESHOLD;

app/src/modules/cloud/cloud.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
#endif /* CONFIG_MEMFAULT */
2525

2626
#include "app_common.h"
27+
#ifdef CONFIG_APP_INSPECT_SHELL
28+
#include "app_inspect.h"
29+
#endif /* CONFIG_APP_INSPECT_SHELL */
2730
#include "cloud.h"
2831
#include "cloud_internal.h"
2932
#include "cloud_configuration.h"
@@ -239,6 +242,41 @@ static const struct smf_state states[] = {
239242
NULL),
240243
};
241244

245+
#if defined(CONFIG_APP_INSPECT_SHELL)
246+
static struct cloud_state_object *cloud_state_ctx;
247+
248+
static const char *cloud_state_to_string(enum cloud_module_state state)
249+
{
250+
switch (state) {
251+
case STATE_RUNNING:
252+
return "STATE_RUNNING";
253+
case STATE_DISCONNECTED:
254+
return "STATE_DISCONNECTED";
255+
case STATE_CONNECTING:
256+
return "STATE_CONNECTING";
257+
case STATE_CONNECTING_ATTEMPT:
258+
return "STATE_CONNECTING_ATTEMPT";
259+
case STATE_PROVISIONED:
260+
return "STATE_PROVISIONED";
261+
case STATE_PROVISIONING:
262+
return "STATE_PROVISIONING";
263+
case STATE_CONNECTING_BACKOFF:
264+
return "STATE_CONNECTING_BACKOFF";
265+
case STATE_CONNECTED:
266+
return "STATE_CONNECTED";
267+
case STATE_CONNECTED_READY:
268+
return "STATE_CONNECTED_READY";
269+
case STATE_CONNECTED_PAUSED:
270+
return "STATE_CONNECTED_PAUSED";
271+
default:
272+
return "STATE_UNKNOWN";
273+
}
274+
}
275+
276+
APP_INSPECT_MODULE_REGISTER_STATE(cloud, cloud_state_ctx, states,
277+
enum cloud_module_state, cloud_state_to_string);
278+
#endif /* CONFIG_APP_INSPECT_SHELL */
279+
242280
static void cloud_wdt_callback(int channel_id, void *user_data)
243281
{
244282
LOG_ERR("Watchdog expired, Channel: %d, Thread: %s",
@@ -1274,6 +1312,10 @@ static void cloud_module_thread(void)
12741312
const k_timeout_t zbus_wait_ms = K_MSEC(wdt_timeout_ms - execution_time_ms);
12751313
static struct cloud_state_object cloud_state;
12761314

1315+
#if defined(CONFIG_APP_INSPECT_SHELL)
1316+
cloud_state_ctx = &cloud_state;
1317+
#endif /* CONFIG_APP_INSPECT_SHELL */
1318+
12771319
LOG_DBG("Cloud module task started");
12781320

12791321
task_wdt_id = task_wdt_add(wdt_timeout_ms, cloud_wdt_callback, (void *)k_current_get());

app/src/modules/environmental/environmental.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
#include <date_time.h>
1414

1515
#include "app_common.h"
16+
#ifdef CONFIG_APP_INSPECT_SHELL
17+
#include "app_inspect.h"
18+
#endif /* CONFIG_APP_INSPECT_SHELL */
1619
#include "environmental.h"
1720

1821
/* Register log module */
@@ -78,6 +81,26 @@ static const struct smf_state states[] = {
7881
[STATE_RUNNING] = SMF_CREATE_STATE(NULL, state_running_run, NULL, NULL, NULL),
7982
};
8083

84+
#if defined(CONFIG_APP_INSPECT_SHELL)
85+
static struct environmental_state_object *environmental_state_ctx;
86+
87+
static const char *environmental_state_to_string(enum environmental_module_state state)
88+
{
89+
switch (state) {
90+
case STATE_RUNNING:
91+
return "STATE_RUNNING";
92+
default:
93+
return "STATE_UNKNOWN";
94+
}
95+
}
96+
97+
APP_INSPECT_MODULE_REGISTER_STATE(env,
98+
environmental_state_ctx,
99+
states,
100+
enum environmental_module_state,
101+
environmental_state_to_string);
102+
#endif /* CONFIG_APP_INSPECT_SHELL */
103+
81104
static void sample_sensors(const struct device *const bme680)
82105
{
83106
int err;
@@ -181,6 +204,10 @@ static void env_module_thread(void)
181204
.bme680 = DEVICE_DT_GET(DT_NODELABEL(bme680)),
182205
};
183206

207+
#if defined(CONFIG_APP_INSPECT_SHELL)
208+
environmental_state_ctx = &environmental_state;
209+
#endif /* CONFIG_APP_INSPECT_SHELL */
210+
184211
LOG_DBG("Environmental module task started");
185212

186213
task_wdt_id = task_wdt_add(wdt_timeout_ms, env_wdt_callback, (void *)k_current_get());

0 commit comments

Comments
 (0)