Skip to main content

Kconfig

Kconfig is the configuration system Zephyr inherited from the Linux kernel. It controls which drivers, subsystems, and features are compiled into your firmware.

Every Zephyr feature — I2C, BLE, USB, logging — is gated behind a CONFIG_ symbol. If CONFIG_I2C=n, the I2C driver is not compiled and takes zero flash space.



Your project's prj.conf​

prj.conf
# Enable the I2C bus and sensor subsystem (for the SHT30)
CONFIG_I2C=y
CONFIG_SENSOR=y

# Logging
CONFIG_LOG=y
CONFIG_LOG_DEFAULT_LEVEL=3 # 0=off 1=err 2=warn 3=info 4=debug


How are Kconfig values resolved?​

Zephyr merges multiple Kconfig sources in order:

  1. SoC defaults — from the chip's Kconfig.defconfig
  2. Board defaults — from the board's <board>_defconfig (e.g. efz_esp32s3_procpu_defconfig)
  3. Your prj.conf — your application overrides
  4. Driver Kconfig — for every symbol you enable (e.g. CONFIG_SENSOR=y), every matching sensor driver in the tree becomes available. The devicetree decides which one actually compiles in.

Values in prj.conf override everything upstream. If you set CONFIG_LOG_DEFAULT_LEVEL=4, it overrides the board default.

info

After a build, cat build/zephyr/.config shows every resolved Kconfig value — the definitive record of what is compiled into your firmware.



Dependency-driven Kconfig — the SHT30 example​

Some symbols have depends on — if a driver requires another subsystem, you must enable the full chain or the build will fail.

To understand what a driver requires, read its Kconfig file directly. For the SHT30 (driven by Zephyr's SHT3x driver) at zephyr/drivers/sensor/sensirion/sht3xd/Kconfig:

menuconfig SHT3XD
bool "SHT3xD Temperature and Humidity Sensor"
default y
depends on DT_HAS_SENSIRION_SHT3XD_ENABLED
select I2C
select CRC
help
Enable driver for SHT3xD temperature and humidity sensors.

Three things to notice:

  • default y — SHT3XD is auto-enabled the moment its dependency is met. You don't have to write CONFIG_SHT3XD=y yourself (the course sample does anyway, so the dependency is visible when you read prj.conf).
  • depends on DT_HAS_SENSIRION_SHT3XD_ENABLED — this symbol is auto-generated by Zephyr when it finds an enabled DTS node with compatible = "sensirion,sht3xd" — in your overlay on the DevKitC, or already in the board DTS on the EFZ-ESP32S3. The devicetree triggers the Kconfig.
  • select I2C / select CRC — the driver forces on the I2C subsystem (the SHT30 only speaks I2C) and the CRC library it uses to check every measurement.

This is why CONFIG_SENSOR=y is enough to bring up the SHT30 — everything driver-specific resolves automatically from the devicetree. Here's what a real build for the EFZ-ESP32S3 resolved (build/zephyr/.config, filtered):

build/zephyr/.config
CONFIG_DT_HAS_SENSIRION_SHT3XD_ENABLED=y
CONFIG_I2C=y
CONFIG_SENSOR=y
CONFIG_SHT3XD=y
CONFIG_SHT3XD_TRIGGER_NONE=y
CONFIG_SHT3XD_REPEATABILITY_HIGH=y
CONFIG_SHT3XD_PERIODIC_MODE=y
CONFIG_SHT3XD_MPS_1=y
CONFIG_CRC=y

Every SHT3XD_* line there is a default you never typed.



Anatomy of a Kconfig file​

A Kconfig file is a plain-text description of available config symbols, written in a small declarative language. The real SHT3x Kconfig file is a good tour — most of the keywords you'll meet appear in it. An excerpt:

drivers/sensor/sensirion/sht3xd/Kconfig (excerpt)
menuconfig SHT3XD
bool "SHT3xD Temperature and Humidity Sensor"
default y
depends on DT_HAS_SENSIRION_SHT3XD_ENABLED
select I2C
select CRC
help
Enable driver for SHT3xD temperature and humidity sensors.

if SHT3XD

choice SHT3XD_MEASUREMENT_MODE
prompt "Measurement mode"
default SHT3XD_PERIODIC_MODE

config SHT3XD_SINGLE_SHOT_MODE
bool "single shot"

config SHT3XD_PERIODIC_MODE
bool "periodic data acquisition"

endchoice

choice SHT3XD_MPS
prompt "Measurements per second"
default SHT3XD_MPS_1
depends on SHT3XD_PERIODIC_MODE
help
Number of measurements per second.

config SHT3XD_MPS_05
bool "0.5"

config SHT3XD_MPS_1
bool "1"

# ... SHT3XD_MPS_2, _4, _10

endchoice

config SHT3XD_THREAD_PRIORITY
int "Thread priority"
depends on SHT3XD_TRIGGER_OWN_THREAD
default 10
help
Priority of thread used by the driver to handle interrupts.

endif # SHT3XD

Five things to notice:

  • menuconfig vs config — menuconfig SHT3XD doubles as a collapsible group header in menuconfig. Children sit under it visually.
  • if SHT3XD … endif — applies an implicit depends on SHT3XD to every symbol inside. Sub-options disappear entirely if SHT3XD is n.
  • choice … endchoice — defines a mutually-exclusive group. Exactly one of SHT3XD_SINGLE_SHOT_MODE / SHT3XD_PERIODIC_MODE may be y. The SHT3XD_MPS choice adds its own depends on: a measurement rate only makes sense in periodic mode.
  • Indentation is convention, not syntax — Zephyr's Kconfig files use a tab for attributes and a tab plus two spaces for help text.
  • A Kconfig file rarely lives alone — each subsystem has its own Kconfig, and source lines higher up in the tree pull them together.

The same file also has a SHT3XD_REPEATABILITY choice (_LOW / _MEDIUM / _HIGH, default high) and a SHT3XD_TRIGGER_MODE choice. The two thread-based trigger modes carry depends on $(dt_compat_any_has_prop,$(DT_COMPAT_SENSIRION_SHT3XD),alert-gpios) — they only exist if your devicetree wires the sensor's ALERT pin. Neither course board does, so only SHT3XD_TRIGGER_NONE is available: devicetree reaching into Kconfig again.

Try it

Switch the SHT30 to single-shot mode — the sensor then sleeps between reads instead of measuring once a second on its own. Add to prj.conf:

CONFIG_SHT3XD_SINGLE_SHOT_MODE=y

Rebuild and check build/zephyr/.config: CONFIG_SHT3XD_PERIODIC_MODE is now unset, and every CONFIG_SHT3XD_MPS_* line has vanished — its depends on is no longer met.



File-level directives​

These structure the file itself:

DirectivePurpose
config NAMEDeclare a single symbol (CONFIG_NAME)
menuconfig NAMEDeclare a symbol that also acts as a collapsible menu header in menuconfig
choice … endchoiceMutually-exclusive group — exactly one option may be y
menu "Title" … endmenuGroup symbols under a labeled section in the UI
if EXPR … endifApply a shared depends on to all enclosed symbols
source "path/Kconfig"Include another Kconfig file (absolute from ZEPHYR_BASE)
rsource "Kconfig"Same as source, but relative to the current file
comment "Text"Static text shown in menuconfig (e.g. section dividers)
mainmenu "Title"Root menu title (only valid in the top-level Kconfig)


Config block attributes​

These appear inside a config or menuconfig block:

AttributeExampleWhat it does
typebool / int / hex / string / tristateOne per block. Determines what values are valid.
prompt "Text"bool "Enable LED strip" (combined form)User-facing label shown in menuconfig. Without a prompt, the symbol is hidden and only settable via defaults or select.
default VALUEdefault 0x200 if FOOInitial value. Multiple default lines pick the first matching condition.
depends on EXPRdepends on I2C && !ZEPHYR_QEMUSymbol disappears unless EXPR is true. The symbol cannot be y if its dependencies aren't met.
select OTHERselect I2SWhen this symbol is y, force OTHER to y — ignores OTHER's own depends on. Use sparingly.
imply OTHERimply USB_KEYBOARDSuggest OTHER=y, but respect its dependencies and allow the user to override. The polite version of select.
range LOW HIGHrange 0 99Constrain int/hex to a valid range.
help(multi-line block)Documentation shown in menuconfig and the Kconfig HTML reference.
visible if EXPRvisible if EXPERTHide the prompt unless EXPR is true, but still let defaults apply.


select vs imply vs depends on — the one thing that trips people up​

These three keywords all relate one symbol to another, and getting them wrong is the most common source of "why is this driver not building" headaches.

DirectionRespects target's depends on?Override-able?
depends on"I need X to be on"n/a (it's the constraint)n/a
select"Force X to be on"No — can cause invalid configsNo
imply"Suggest X be on"YesYes — user can set X=n

Use depends on for hard requirements (HW driver requires its bus subsystem to be selectable). Use imply when you'd like a feature on by default but it's not critical. Reach for select only when the target symbol has no user-facing prompt and exists solely to be turned on by other symbols.

warning

selecting a symbol that has its own depends on can produce a config where the target is y but its dependency is n — Kconfig will warn but still build, and you'll get cryptic link errors. If you find yourself doing this, the right answer is almost always depends on instead.



The Kconfig file family in a Zephyr build​

You won't write a Kconfig file very often — but you'll encounter half a dozen related files. Knowing which is which saves a lot of time:

FileLives inPurpose
KconfigEvery subsystem and driver directoryDefines symbols (declarative — the schema)
Kconfig.defconfigBoards and SoCsSets defaults for invisible symbols — invisible to menuconfig but applied at build time
<board>_defconfigboards/<vendor>/<board>/Initial values for visible board symbols (the board's "factory settings")
prj.confYour app rootYour config overrides — what you actually edit day-to-day
boards/<board>.confYour app root, optionalBoard-specific overrides that merge with prj.conf only when building for that board
sysbuild.confYour app root, Sysbuild onlyCross-image settings (e.g. SB_CONFIG_BOOTLOADER_MCUBOOT=y)
.configbuild/zephyr/ (generated)The fully-resolved result of merging everything above

Precedence (later wins): SoC Kconfig.defconfig → board Kconfig.defconfig → <board>_defconfig → prj.conf → boards/<board>.conf → CLI -DCONFIG_* flags.



Common Kconfig types​

Symbol typeExampleMeaning
boolCONFIG_SHT3XD=yEnable/disable a feature
intCONFIG_SHT3XD_THREAD_PRIORITY=10Numeric value
stringCONFIG_BOOT_BANNER_STRING="my fw"String value
hexCONFIG_SRAM_SIZE=0x40000Hex value
info

Reference: Kconfig — Tips and Best Practices and the Kconfig setting guide in the Zephyr docs.



Browsing available symbols​

# The terminal (ncurses) interface
west build -t menuconfig

# Or the graphical version (requires Python and tkinter)
west build -t guiconfig

Both show the full tree of available symbols with their current values and help text.



Next: actually read the SHT30​

The four layers are now in place — devicetree, binding, Kconfig, and the in-tree driver they pull in. Time to use it. The next page, Sensors, calls sensor_sample_fetch against the SHT30 you've just configured.