Introduction | Theory | Lab | Course Home
Now that you understand Zephyr’s concepts and architecture, let’s get hands-on experience with setting up a development environment and building your first applications.
Set up a complete Zephyr development environment and verify it works by building a sample application.
Download and install VS Code from code.visualstudio.com
Install the Zephyr extension:
If you prefer to install the SDK manually instead of using the VS Code extension’s guided process, you can follow these steps:
zephyr-sdk-*.tar.xz archive for your operating system.~/zephyr-sdk.cd ~/zephyr-sdk
./setup.sh
Test your setup by building a sample application:
cd ~/zephyrproject
west build -b qemu_x86 zephyr/samples/hello_world
west build -t run
Expected Output:
*** Booting Zephyr OS build v4.2.99 ***
Hello World! qemu_x86
Note on Versioning: The version number v4.2.99 indicates a development version of Zephyr. A .99 patch level is often used for builds from the main development branch rather than a stable release.
If you see this output, your development environment is working correctly!
Build and deploy your first application specifically for Raspberry Pi 4B hardware.
cd ~/zephyrproject
west build -b rpi_4b zephyr/samples/hello_world --pristine
The --pristine flag ensures a clean build.
Copy the kernel image:
cp build/zephyr/zephyr.bin /path/to/sdcard/kernel8.img
Add minimal config.txt to SD card:
arm_64bit=1
kernel=kernel8.img
Open terminal emulator:
# Linux/macOS
screen /dev/ttyUSB0 115200
# Or use minicom
minicom -D /dev/ttyUSB0 -b 115200
*** Booting Zephyr OS build v4.2.99 ***
Hello World! rpi_4b
Troubleshooting:
Create an interactive application that controls hardware (LED) and demonstrates Zephyr’s GPIO API.
cd ~/zephyrproject
cat zephyr/samples/basic/blinky/src/main.c
Key concepts in the code:
DT_ALIAS(led0)k_msleep)For Raspberry Pi 4B:
west build -b rpi_4b zephyr/samples/basic/blinky --pristine
cp build/zephyr/zephyr.bin /path/to/sdcard/kernel8.img
For QEMU (testing without hardware):
west build -b qemu_x86 zephyr/samples/basic/blinky --pristine
west build -t run
mkdir ~/zephyrproject/my_blinky
cd ~/zephyrproject/my_blinky
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(my_blinky)
target_sources(app PRIVATE src/main.c)
CONFIG_GPIO=y
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(main);
#define FAST_BLINK_MS 250 // Fast blink mode
#define SLOW_BLINK_MS 1500 // Slow blink mode
#define LED0_NODE DT_ALIAS(led0)
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
int main(void)
{
int ret;
bool led_state = true;
bool fast_mode = true;
int blink_count = 0;
LOG_INF("Custom Blinky Application Starting");
if (!gpio_is_ready_dt(&led)) {
LOG_ERR("Error: LED device not ready");
return 0;
}
ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
if (ret < 0) {
LOG_ERR("Error: Failed to configure LED pin");
return 0;
}
while (1) {
// Toggle LED
ret = gpio_pin_toggle_dt(&led);
if (ret < 0) {
LOG_ERR("Error: Failed to toggle LED");
return 0;
}
led_state = !led_state;
LOG_INF("LED %s (blink #%d)", led_state ? "ON " : "OFF", ++blink_count);
// Switch between fast and slow blinking every 10 blinks
if (blink_count % 10 == 0) {
fast_mode = !fast_mode;
LOG_INF("Switching to %s mode", fast_mode ? "FAST" : "SLOW");
}
k_msleep(fast_mode ? FAST_BLINK_MS : SLOW_BLINK_MS);
}
return 0;
}
west build -b rpi_4b . --pristine
For boards with buttons (like nRF52840 DK):
CONFIG_GPIO=y
#define BUTTON0_NODE DT_ALIAS(sw0)
static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET(BUTTON0_NODE, gpios);
static struct gpio_callback button_cb_data;
void button_pressed(const struct device *dev, struct gpio_callback *cb,
uint32_t pins)
{
printf("Button pressed! Toggling LED mode\n");
// Add your button handling logic here
}
Note: This challenge requires an understanding of interrupts and callbacks, which will be covered in detail in later chapters. The DT_ALIAS(sw0) macro also requires a sw0 alias to be defined in your board’s device tree file. For example:
/ {
aliases {
sw0 = &button0;
};
};
Learn to configure Zephyr applications using Kconfig and understand the impact of different settings.
cd ~/zephyrproject/my_blinky
west build -t menuconfig
This opens an interactive menu where you can:
Enable Logging: Add to prj.conf:
CONFIG_LOG=y
CONFIG_LOG_DEFAULT_LEVEL=3
Update your code to use logging:
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(main);
// In main function:
LOG_INF("Application starting");
LOG_DBG("LED state changed to %s", led_state ? "ON" : "OFF");
Adjust Stack Sizes:
CONFIG_MAIN_STACK_SIZE=2048
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1024
Enable Shell Interface:
CONFIG_SHELL=y
CONFIG_SHELL_BACKEND_SERIAL=y
Check memory usage:
west build -t ram_report
west build -t rom_report
The memory usage report is automatically generated every time you build your application. You can find this information in the build output, typically near the end. Look for a section that shows the memory usage for different regions like Flash and RAM.
Optimize for size:
CONFIG_SIZE_OPTIMIZATIONS=y
CONFIG_DEBUG=n
CONFIG_ASSERT=n
You now have hands-on experience with Zephyr development. In Chapter 3, we’ll dive deeper into the build system, explore advanced West features, and learn about project structure and organization.
Continue practicing by: