We use Zephyr’s shell subsystem every day at Golioth to interact with custom hardware and vendor development boards. It reduces friction around directly implementing a change on a board without recompiling code, almost like ‘business logic’. I might want to send an LED brightness value to a board or extract a sensor reading without needing to recompile code to only implement a specific function; the shell subsystem makes that interaction much smoother.
This article started in my mind as “a detailed description of each Zephyr shell”. Wow, that would be a long article, and duplication of a lot of great work on the Zephyr docs. Instead, we’re going to create a ranked list from “wow, that is super useful” to “hunh, I didn’t know about that one but I’ll need it someday”. Let’s dig in.
What is a shell (module) in Zephyr?
When I talk about shells here today, I’m really talking about shell modules, the specific code that can control an aspect of the system you’re interested in. These modules are built on top of the underlying Shell Subsystem built into Zephyr (the “plumbing” that makes it all possible). That subsystem is also working through a variety of backends, usually something like the UART output or RTT.
One thing I like to point out to Zephyr newcomers is that the shell is “free”, something that is built into the RTOS that most people expect when they’re interacting with a device over the serial terminal. One of the many aspects of Zephyr that we love because there is infrastructure built in that might be needed some day. After all, when I type input to a terminal connection to a device, I would like to get some kind of useful output! Building the same from scratch on each project is a hassle and a pile of string compares and switch statements, as anyone who has implemented one will tell you.
How we use shells at Golioth
When we’re building Reference Designs at Golioth, we’re often lucky enough to have a bunch of extra flash memory available. Running out is the answer to, “why wouldn’t I just compile them all in?” They’re useful enough that if you can, and if it didn’t interfere with other parts of your application, that I’d encourage it.
It’s possible to create custom shell commands and even implement entire shell modules. Within the Golioth Firmware SDK (and more specifically the Zephyr port), we implement a custom shell to make it easier to add credentials to your device (GOLIOTH_SAMPLE_SETTINGS_SHELL
). As you’ll see below, this is one of my most-used shell commands for my projects.
Useful shells on for your IoT device
OK, this is really “Shell modules that I find myself using most often”. Your mileage may vary. But luckily there are a huge range of other shells available at the bottom of this post.
- (Golioth) Settings – This is a custom shell module for Golioth that you’ll see referenced whenever we talk about PSK. Whenever you get a credential from the Golioth Console, you enter it like
settings set golioth psk-id <your_psk-id_from_console>
andsettings set golioth psk <your_psk_from_console>
. It makes provisioning a new device onto Golioth super fast and simple. - Kernel – This is one I’m often using right after the above shell command. I’ll type in
kernel reboot warm
(orcold
also works) to put the device into a reboot loop. Much better than hitting a button or replugging a cable. When debugging,kernel thread stacks
helps keep an eye on stack usage. - Sensor – If you have your devices properly described in the devicetree and your code is referencing all of the correct libraries, your application will be pulling in sensor readings when called to do so. But turning on the Sensor shell lets you take advantage of that same set of code/configuration to also live update on the terminal. Super useful during troubleshooting.
- I2C – As described in our post about the topic, the i2c shell module really lets you dig into a sensor’s registers without needing to author any code. Particularly useful during troubleshooting is the
i2c scan <bus_name>
command, which tells you which sensors are responding to pings. - Regulator – We implemented the regulator subsystem for our Aludel series of boards. The shell then lets you toggle the regulators on and off with simple commands, good for power testing when a load switch is inline.
- Net – The Net(work) shell has a lot of stated functionality, but also a lot of required dependencies. Many times you’ll choose one of the sub commands (like
net http
) only to be met with a message that you need to toggle on some other subsystem in order to build in that functionality. I am most often using this shell to verify settings and connectivity with commands likenet iface
,net ping <ip_address>
, andnet dns
.
So many more shell modules
I realized I’m barely scratching the surface aside from the ones that I normally use. I’ve run across others–in the Zephyr Docs and in Menuconfig–so I thought I could just compile a project and look at all the Shells listed there. You can too! Once you successfully compile a Zephyr project run the following:
west build -t menuconfig
This will crawl your devicetree and build files and construct a menuconfig interface like below. If you type /
, you can then search for something like shell
and see the following. There are additional settings that contain the word “shell”, so it won’t only be the option to turn a module on or off, but you can scroll through and pull out the shell names like I did.
One important point is that this is not all shells in Zephyr. It’s simply all the shells shown on a project built using nRF Connect SDK (Nordic Semiconductor’s fork of Zephyr). You might also pull in additional modules like the Golioth Firmware SDK that have a custom shell. Let’s take a look at what shells were compiled into the Reference Design Template based on Golioth 0.17.0 / NCS 2.9.0 / Zephyr 3.7.99 shown in the screenshot of menuconfig above.
Non-exhaustive list of Zephyr shell modules
- ACPI_SHELL – “ACPI command Shell”
- ADC_SHELL – “ADC Shell”
- ARM_SIP_SVC_SUBSYS_SHELL – “ARM SiP SVC service shell”
- AT_SHELL – “AT Shell”
- AUDIO_CODEC_SHELL – “Audio Codec shell”
- BBRAM_SHELL – “Battery-backed RAM shell”
- BT_MESH_SHELL – “Bluetooth Mesh shell”
- BT_MESH_SHELL – “Bluetooth Mesh shell”
- BT_SHELL – “Bluetooth shell”
- CAF_SHELL – “Shell interface for triggering CAF events”
- CAN_SHELL – “CAN shell”
- CHARACTER_FRAMEBUFFER_SHELL – “Character Framebuffer shell”
- CLOCK_CONTROL_NRF_SHELL – “Shell commands”
- COAP_SERVER_SHELL – “CoAP service shell commands”
- COMPARATOR_SHELL – “Comparator shell”
- COUNTER_SHELL – “Counter shell”
- CRC_SHELL – “CRC Shell”
- DAC_SHELL – “DAC shell”
- DATE_SHELL – “Date shell”
- DEBUG_COREDUMP_SHELL – “Coredump shell”
- DEVICE_SHELL – “Device shell”
- DEVMEM_SHELL – “Devmem shell”
- DK_LIBRARY_SHELL – “DK_LIBRARY SHELL”
- DOWNLOAD_CLIENT_SHELL – “Enable shell”
- EDAC_SHELL – “EDAC Shell”
- EEPROM_SHELL – “EEPROM shell”
- FILE_SYSTEM_SHELL – “File system shell”
- FLASH_MAP_SHELL – “Flash map shell interface”
- FLASH_SHELL – “Flash shell”
- FPGA_SHELL – “FPGA Shell”
- GOLIOTH_SAMPLE_SETTINGS_SHELL – “Settings shell”
- GPIO_SHELL – “GPIO Shell”
- HAWKBIT_SHELL – “hawkBit shell utilities”
- HWINFO_SHELL – “HWINFO Shell”
- I2C_SHELL – “I2C Shell”
- I3C_SHELL – “I3C Shell”
- INPUT_SHELL – “Input shell”
- ISR_TABLE_SHELL – “Shell command to dump the ISR tables”
- IVSHMEM_SHELL – “IVshmem shell module”
- KERNEL_SHELL – “Kernel shell”
- LED_SHELL – “LED shell”
- LLEXT_SHELL – “llext shell commands”
- LORA_SHELL – “LoRa Shell”
- LTE_SHELL – “Enable LTE shell commands”
- LV_Z_SHELL – “LVGL Shell”
- LWM2M_CARRIER_SHELL – “LwM2M carrier shell”
- MBEDTLS_SHELL – “mbed TLS shell”
- MCUMGR_GRP_SHELL – “Mcumgr handlers for shell management”
- MCUMGR_TRANSPORT_SHELL – “Shell mcumgr SMP transport”
- MDIO_SHELL – “MDIO Shell”
- MODEM_AT_SHELL – “AT command shell based on modem modules”
- MODEM_SHELL – “Modem shell utilities”
- MODEM_SLM_SHELL – “SLM Shell”
- NET_ETHERNET_BRIDGE_SHELL – “Ethernet Bridging management shell”
- NET_L2_IEEE802154_SHELL – “IEEE 802.15.4 shell module”
- NET_L2_WIFI_SHELL – “Wi-Fi shell module”
- NET_SHELL – “Network shell utilities”
- NET_ZPERF – “zperf shell utility”
- NRF70_UTIL – “Utility shell in nRF70 driver”
- NRF_MCUMGR_SMP_CLIENT_SHELL – “MCUmg SMP client shell”
- RF_PROFILER_SHELL – “Enable RF profiler shell integration”
- NRF_PROVISIONING_SHELL – “Shell utilities”
- NXP_WIFI_SHELL – “NXP Wi-Fi shell”
- OPENTHREAD_SHELL – “OpenThread shell”
- PCIE_SHELL – “PCIe/new PCI Shell”
- PLIC_SHELL – “PLIC shell commands”
- PM_DEVICE_SHELL – “Device Power Management shell”
- POSIX_SHELL – “POSIX shell”
- POSIX_UNAME_SHELL – “Support for `uname` command”
- PSCI_SHELL – “Support for PSCI interface shell commands”
- PWM_SHELL – “PWM shell”
- REGULATOR_SHELL – “Regulator shell”
- RTC_SHELL – “RTC Shell commands”
- SENSOR_SHELL – “Sensor shell”
- SETTINGS_SHELL – “Settings shell”
- SMBUS_SHELL – “SMBus Shell”
- SPI_SHELL – “SPI Shell”
- STATS_SHELL – “Statistics Shell Command”
- SYMTAB_SHELL – “Symbol table shell commands”
- TASK_WDT_SHELL – “Task watchdog shell utilities”
- TLS_CREDENTIALS_SHELL – “TLS credentials management shell”
- UPDATEHUB_SHELL – “UpdateHub shell utilities”
- USBD_SHELL – “USB device shell”
- USBH_SHELL – “USB host shell”
- W1_SHELL – “1-Wire Shell”
- WDT_SHELL – “Watchdog (WDT) shell”
- WIFI_CREDENTIALS_SHELL – “Shell commands to manage Wi-Fi credentials”
- WIFI_ESWIFI_SHELL – “esWiFi shell”
- ZIGBEE_SHELL – “Enable Zigbee Shell”
- ZTEST_SHELL – “Ztest with shell support”
What is your favorite shell in Zephyr? Are there any we missed? Let us know in the comments / forum post below!
No comments yet! Start the discussion at forum.golioth.io