Add shell support

Asset Tracker Template

tags
Asset Tracker Template

A shell command provides an easy way to publish requests and inspect responses without rebuilding or reflashing. The template uses this pattern in several modules (for example power_shell.c, network_shell.c, cloud_shell.c) and you can reuse it for your own modules.

The recipe has three parts:

  • A <module>_shell.c file that registers a SHELL_CMD_REGISTER root command and a zbus listener that prints responses.
  • A Kconfig option (CONFIG_APP_<MODULE>_SHELL, default y if SHELL) that gates the shell file so it is compiled only when the shell subsystem is enabled.
  • A target_sources_ifdef() line in the module's CMakeLists.txt so the shell file is built only when the option is set.

Apply the recipe to the dummy module as follows:

  1. Create app/src/modules/dummy/dummy_shell.c:

    #include <zephyr/shell/shell.h>
    #include <zephyr/zbus/zbus.h>
    #include <zephyr/kernel.h>
    #include <zephyr/logging/log.h>
    #include <errno.h>
    
    #include "app_common.h"
    #include "dummy.h"
    
    LOG_MODULE_DECLARE(dummy_module, CONFIG_APP_DUMMY_LOG_LEVEL);
    
    static bool sample_requested;
    
    static void dummy_shell_listener_callback(const struct zbus_channel *chan)
    {
        if (!sample_requested) {
            return;
        }
    
        const struct dummy_msg *msg = zbus_chan_const_msg(chan);
    
        if (msg->type == DUMMY_SAMPLE_RESPONSE) {
            LOG_INF("Dummy sample response: %d", msg->value);
            sample_requested = false;
        }
    }
    
    ZBUS_LISTENER_DEFINE(dummy_shell_listener, dummy_shell_listener_callback);
    ZBUS_CHAN_ADD_OBS(dummy_chan, dummy_shell_listener, 0);
    
    static int cmd_dummy_sample(const struct shell *sh, size_t argc, char **argv)
    {
        ARG_UNUSED(argc);
        ARG_UNUSED(argv);
    
        int err;
        struct dummy_msg msg = {
            .type = DUMMY_SAMPLE_REQUEST,
        };
    
        err = zbus_chan_pub(&dummy_chan, &msg, PUB_TIMEOUT);
        if (err) {
            shell_print(sh, "Failed to send request: %d", err);
            return err;
        }
    
        sample_requested = true;
        shell_print(sh, "Requesting dummy sample...");
        return 0;
    }
    
    SHELL_STATIC_SUBCMD_SET_CREATE(
        sub_cmds,
        SHELL_CMD(sample,
                  NULL,
                  "Request a dummy sample (publishes DUMMY_SAMPLE_REQUEST and prints the response)",
                  cmd_dummy_sample),
        SHELL_SUBCMD_SET_END);
    
    SHELL_CMD_REGISTER(att_dummy,
                       &sub_cmds,
                       "Asset Tracker Template Dummy module commands",
                       NULL);
    

    The listener checks a local sample_requested flag so it logs only the responses triggered by the most recent shell command, rather than every DUMMY_SAMPLE_RESPONSE published on the channel.

  2. Add the Kconfig option to app/src/modules/dummy/Kconfig.dummy, inside the existing if APP_DUMMY block:

    config APP_DUMMY_SHELL
        bool "Dummy module shell commands"
        default y if SHELL
        help
            Enable shell commands for the dummy module. Adds the att_dummy sample
            command, which publishes a DUMMY_SAMPLE_REQUEST and logs the response.
    
  3. Append the conditional source to app/src/modules/dummy/CMakeLists.txt:

    target_sources_ifdef(CONFIG_APP_DUMMY_SHELL app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/dummy_shell.c)
    

To apply the same pattern to your own module, replace dummy with your module name in the file, Kconfig symbol, CMake option, channel, and message type. The root command (att_dummy here) is conventionally att_<module> so all template commands share a common prefix.