# Getting Started with Zig ESP-IDF Sample A complete guide to building ESP32 firmware using Zig and ESP-IDF. ## Table of Contents - [Prerequisites](#prerequisites) - [Installation](#installation) - [Quick Start](#quick-start) - [Project Structure](#project-structure) - [Building Your First Project](#building-your-first-project) - [Working with Components](#working-with-components) - [Examples](#examples) - [Troubleshooting](#troubleshooting) - [Next Steps](#next-steps) --- ## Prerequisites ### Required Software - **Python 3.12+** (for ESP-IDF tools) - **Git** (for cloning repositories) - **CMake 3.16+** (bundled with ESP-IDF) - **Ninja** or **Make** (bundled with ESP-IDF) - **Zig compiler** (optional - will be auto-downloaded if not found) ### Supported Operating Systems - **Linux** (Ubuntu 20.04+, Debian, Fedora, Arch) - **macOS** (10.15+) - **Windows** (10/11 with PowerShell or WSL2) - **Nix/NixOS** (via `flake.nix`) ### Supported ESP32 Targets | Architecture | Targets | |--------------|---------| | **RISC-V** | ESP32-C2, C3, C5, C6, C61, H2, H21, H4, P4 | | **Xtensa** | ESP32, ESP32-S2, ESP32-S3 | --- ## Installation ### Step 1: Install ESP-IDF **Option A: Standard Installation** ```bash # Clone ESP-IDF git clone --recursive https://github.com/espressif/esp-idf.git cd esp-idf # Linux/macOS: ./install.sh # Windows (PowerShell): .\install.ps1 # Windows (Command Prompt): install.bat ``` **Option B: Using Nix Flakes** (Linux/macOS) ```bash # Enter development environment with all dependencies nix develop # Or use direnv for automatic activation echo "use flake" > .envrc direnv allow ``` **Option C: ESP-IDF installer** (Windows/macOS) Download from: https://dl.espressif.com/dl/esp-idf/ ### Step 2: Set up ESP-IDF Environment Every time you open a new terminal, activate ESP-IDF: ```bash # Linux/macOS: . $HOME/esp/esp-idf/export.sh # Windows (PowerShell): . $HOME/esp/esp-idf/export.ps1 # Windows (Command Prompt): %USERPROFILE%\esp\esp-idf\export.bat ``` ### Step 3: Clone This Project ```bash git clone https://github.com/kassane/zig-esp-idf-sample.git cd zig-esp-idf-sample ``` ### Step 4: Verify Installation ```bash idf.py --version # Expected output: ESP-IDF v5.x.x or v6.x.x ``` --- ## Quick Start ### 1. Set Your Target Device ```bash # For ESP32-C6 (RISC-V) idf.py set-target esp32c6 # For ESP32-S3 (Xtensa) idf.py set-target esp32s3 # For ESP32 (original, Xtensa) idf.py set-target esp32 ``` ### 2. Build the Project ```bash idf.py build ``` **What happens during build:** - ✅ CMake detects your target and configures the build - ✅ Zig toolchain is automatically downloaded (if needed) - ✅ C bindings are generated from ESP-IDF headers - ✅ Target-specific patches are applied - ✅ Zig code is compiled to object files - ✅ Everything is linked with ESP-IDF libraries - ✅ Firmware binaries are generated ### 3. Flash to Device ```bash # Connect your ESP32 via USB, then: idf.py -p PORT flash # Find your port: # Linux: /dev/ttyUSB0 or /dev/ttyACM0 # macOS: /dev/cu.usbserial-* # Windows: COM3, COM4, etc. # Example: idf.py -p /dev/ttyUSB0 flash ``` ### 4. Monitor Output ```bash idf.py -p PORT monitor # Or combine flash + monitor: idf.py -p PORT flash monitor # Exit monitor: Ctrl+] ``` --- ## Project Structure ``` zig-esp-idf-sample/ ├── build.zig # Zig build script (compiles Zig code) ├── build.zig.zon # Zig package manifest ├── CMakeLists.txt # Root CMake config ├── sdkconfig # ESP-IDF configuration (generated) ├── sdkconfig.defaults # Default SDK config ├── sdkconfig.defaults.esp32 # ESP32-specific defaults ├── partitions_matter.csv # Custom partition table for Matter (3 MB app, 4 MB flash) ├── dependencies.lock # Component version lock ├── wokwi.toml # Wokwi simulator config │ ├── main/ │ ├── CMakeLists.txt # Main component config │ ├── placeholder.c # Minimal C entry point (required by CMake) │ ├── matter_wrappers.cpp # C++ shims for esp_matter C++ API (activated when component present) │ ├── app.zig # Main Zig application entry │ ├── idf_component.yml # Component dependencies │ ├── Kconfig.projbuild # Project configuration options │ └── examples/ │ ├── gpio-blink.zig # Toggle LED on GPIO2 │ ├── uart-echo.zig # UART echo │ ├── i2c-scan.zig # I2C bus scan │ ├── wifi-station.zig # WiFi station │ ├── http-server.zig # HTTP server │ ├── ble-gatt-server.zig # BLE GATT server │ ├── smartled-rgb.zig # WS2812B LED strip │ ├── dsp-math.zig # DSP/FFT operations │ └── matter-light.zig # Matter On/Off Light │ ├── imports/ # Zig API wrappers and bindings │ ├── idf.zig # Main ESP-IDF facade module │ ├── idf-sys.zig # Generated C bindings (auto-generated) │ ├── sys.zig # Re-exports idf-sys │ ├── error.zig # esp_err_t → Zig error mapping │ ├── logger.zig # std.log integration (espLogFn) │ ├── version.zig # ESP-IDF version info (ver) │ ├── heap.zig # HeapCapsAllocator, MultiHeapAllocator │ ├── bootloader.zig # Partition/bootloader control │ ├── gpio.zig # GPIO wrapper │ ├── wifi.zig # WiFi station/AP/scan │ ├── uart.zig # UART driver │ ├── i2c.zig # I2C master │ ├── spi.zig # SPI master (+ SDSPI) │ ├── i2s.zig # I2S audio (STD, PDM, TDM) │ ├── http.zig # HTTP server + client │ ├── mqtt.zig # MQTT client │ ├── lwip.zig # lwIP sockets, DNS, SNTP │ ├── crc.zig # ESP-ROM CRC-8/16/32 │ ├── bluetooth.zig # Bluedroid BLE │ ├── nimble.zig # NimBLE BLE (compile-time guarded) │ ├── now.zig # ESP-NOW protocol │ ├── nvs.zig # NVS flash key-value storage │ ├── partition.zig # Partition table operations │ ├── sleep.zig # Deep/light sleep + wakeup │ ├── event.zig # ESP event loop │ ├── wdt.zig # Task watchdog timer │ ├── rtos.zig # FreeRTOS tasks/queues/semaphores/timers │ ├── pcnt.zig # Pulse counter (pulse) │ ├── phy.zig # Wireless PHY / RF calibration │ ├── segger.zig # Segger SystemView profiling │ ├── led-strip.zig # LED strip — requires espressif/led_strip │ ├── dsp.zig # DSP/FFT — requires espressif/esp-dsp │ ├── hosted.zig # ESP-Hosted coexistence — requires espressif/esp_hosted │ ├── wifi_remote.zig # WiFi remote — requires espressif/esp_wifi_remote │ ├── timer.zig # High-resolution esp_timer (one-shot + periodic) │ ├── ledc.zig # LED PWM controller (duty, fade) │ ├── twai.zig # TWAI/CAN bus driver │ ├── pm.zig # Power management locks │ ├── pthread.zig # POSIX threads (FreeRTOS-backed) │ └── panic.zig # Zig panic handler │ ├── include/ # C headers for binding generation │ ├── stubs.h # Core ESP-IDF headers (input to zig translate-c) │ ├── wifi_stubs.h # WiFi macro wrappers │ ├── bt_stubs.h # Bluetooth macro wrappers │ ├── matter_stubs.h # C wrapper interface for esp_matter (C++ component) │ ├── matter_closure_patch.h # GCC 14 C++23 fix for closure-control cluster │ └── bindings.h # Additional bindings │ ├── cmake/ # CMake build system scripts │ ├── zig-config.cmake # Main Zig configuration │ ├── zig-download.cmake # Auto-download Zig toolchain │ ├── zig-runner.cmake # Helper functions for Zig │ ├── bindings.cmake # Binding generation │ ├── extra-components.cmake # Managed component detection │ └── patch.cmake # Post-processing patches │ ├── patches/ # Binding fixes for translate-c issues │ ├── *.zig │ ├── docs/ # Documentation │ ├── build-internals.md # Build system details │ ├── build-scheme.png # Build flow diagram │ └── zig-xtensa.md # Xtensa toolchain info │ └── flake.nix # Nix development environment ``` --- ## Building Your First Project ### Example 1: Hello World ```zig const std = @import("std"); const idf = @import("esp_idf"); comptime { @export(&main, .{ .name = "app_main" }); } fn main() callconv(.c) void { log.info("Hello from Zig on ESP32!", .{}); log.info("Zig version: {s}", .{@import("builtin").zig_version_string}); // Show memory info var heap = idf.heap.HeapCapsAllocator.init(null); // default: MALLOC_CAP_DEFAULT log.info("Free heap: {} bytes", .{heap.freeSize()}); // Sleep forever while (true) { idf.rtos.Task.delayMs(1000); } } // overwrite zig std_options config pub const std_options: std.Options = .{ .logFn = idf.log.espLogFn, }; // rename log instance const log = std.log.scoped(.@"esp-idf"); // overwrite std panic-handler pub const panic = idf.esp_panic.panic; ``` **Build and run:** ```bash idf.py build flash monitor ``` ### Example 2: Blinking LED **Create `main/examples/blink.zig`:** ```zig const std = @import("std"); const idf = @import("esp_idf"); const LED_PIN = .@"18"; // Change to your LED pin comptime { @export(&main, .{ .name = "app_main" }); } fn main() callconv(.c) void { // Configure GPIO as output idf.gpio.Direction.set(LED_PIN, .output) catch { log.err("Failed to configure GPIO", .{}); return; }; log.info("Blinking LED on GPIO {d}", .{LED_PIN}); while (true) { // LED ON idf.gpio.Level.set(LED_PIN, 1) catch {}; log.info("LED ON", .{}); idf.rtos.Task.delayMs(1000); // LED OFF idf.gpio.Level.set(LED_PIN, 0) catch {}; log.info("LED OFF", .{}); idf.rtos.Task.delayMs(1000); } } // overwrite zig std_options config pub const std_options: std.Options = .{ .logFn = idf.log.espLogFn, }; // rename log instance const log = std.log.scoped(.blink); // overwrite std panic-handler pub const panic = idf.esp_panic.panic; ``` --- ## Working with Components ### Adding Managed Components Components are specified in `main/idf_component.yml`: **Add a component:** ```bash # Use idf.py command: idf.py add-dependency espressif/led_strip # then idf.py reconfigure ``` ### Using LED Strip Component **The build system automatically:** 1. ✅ Detects components in `dependencies.lock` 2. ✅ Adds include paths to binding generation 3. ✅ Generates `HAS_*` defines (e.g., `HAS_LED_STRIP=1`) 4. ✅ Includes headers in `include/stubs.h` conditionally 5. ✅ Makes APIs available in Zig via `idf.*` modules **1. Ensure LED strip is in `main/idf_component.yml`:** ```yaml dependencies: espressif/led_strip: "*" ``` **2. Reconfigure:** ```bash idf.py reconfigure ``` **3. Check provided example:** The example in [examples/smartled-rgb.zig](../main/examples/smartled-rgb.zig) demonstrates: - Configuring WS2812B LED strip on GPIO 2 - Setting individual pixel colors - Refreshing the display - Blinking pattern ### DSP basics (FFT + math) [examples/dsp-math.zig](../main/examples/dsp-math.zig) - Generates a sine tone (freq = 0.2 normalized) - Applies Hann window - Does FFT (1024 points, float32, radix-2) - Computes power spectrum in dB - Prints ASCII plot of the spectrum (64×10 chars, -120 to +40 dB) ### Using WiFi **Use the WiFi station example:** [examples/wifi-station.zig](../main/examples/wifi-station.zig) **Configure WiFi credentials:** ```bash idf.py menuconfig # Navigate to: Example Configuration # Set WiFi SSID and Password ``` Or edit [main/Kconfig.projbuild](../main/Kconfig.projbuild) to change default values. ### Available Wrapper APIs The project provides comprehensive Zig wrappers in `imports/`: | Module | Description | Notes | |--------|-------------|-------| | `idf.gpio` | GPIO control | Any target | | `idf.wifi` | WiFi station/AP/scan | Not on H2/H4/P4 | | `idf.bt` | Bluedroid BLE | Requires `CONFIG_BT_ENABLED` | | `idf.nimble` | NimBLE BLE | Requires `CONFIG_BT_NIMBLE_ENABLED` | | `idf.heap` | HeapCapsAllocator, MultiHeapAllocator | Any target | | `idf.rtos` | FreeRTOS tasks/queues/semaphores/timers | Any target | | `idf.nvs` | NVS flash key-value storage | Any target | | `idf.partition` | Partition table operations | Any target | | `idf.sleep` | Deep/light sleep + wakeup sources | Any target | | `idf.event` | ESP event loop | Any target | | `idf.wdt` | Task watchdog timer | Any target | | `idf.bl` | Bootloader/partition control | Any target | | `idf.i2c` | I2C master | Any target | | `idf.spi` | SPI master + SDSPI | Any target | | `idf.uart` | UART driver | Any target | | `idf.i2s` | I2S audio (STD, PDM, TDM) | Any target | | `idf.http` | HTTP server (httpd) + client | Any target | | `idf.mqtt` | MQTT client | Any target | | `idf.lwip` | lwIP sockets, DNS, SNTP | Any target | | `idf.crc` | ESP-ROM CRC-8/16/32 | Any target | | `idf.esp_now` | ESP-NOW protocol | Any target | | `idf.pulse` | Pulse counter (PCNT) | Any target | | `idf.phy` | Wireless PHY / RF calibration | Any target | | `idf.segger` | Segger SystemView profiling | Any target | | `idf.ver` | ESP-IDF version info | Any target | | `idf.led` | LED strip WS2812B | Requires `espressif/led_strip` | | `idf.dsp` | DSP/FFT operations | Requires `espressif/esp-dsp` | | `idf.esp_hosted` | ESP-Hosted coexistence | Requires `espressif/esp_hosted` | | `idf.wifi_remote` | WiFi via slave MCU | Requires `espressif/esp_wifi_remote` | | `idf.timer` | High-resolution esp_timer | Any target | | `idf.ledc` | LED PWM controller | Any target | | `idf.twai` | TWAI/CAN bus driver | Any target | | `idf.pm` | Power management locks | Any target | | `idf.pthread` | POSIX threads (FreeRTOS-backed) | Any target | | `idf.matter` | Matter/CHIP protocol | Requires `espressif/esp_matter` | | `idf.log` | std.log integration (`espLogFn`) | Any target | | `idf.err` | esp_err_t → Zig error mapping | Any target | | `idf.sys` | Raw C bindings | Direct ESP-IDF API access | --- ## Examples ### FreeRTOS Tasks ```zig const idf = @import("esp_idf"); const std = @import("std"); // overwrite std panic-handler pub const panic = idf.esp_panic.panic; // overwrite std.log pub const std_options: std.Options = .{ .logFn = idf.log.espLogFn, }; export fn myTask(_: ?*anyopaque) callconv(.c) void { const log = std.log.scoped(.task); while (true) { log.info("Task running!", .{}); idf.rtos.Task.delayMs(1000); } } export fn app_main() callconv(.c) void { _ = idf.rtos.Task.create(myTask, "my_task", 2048, null, 5) catch @panic("Failed to create task"); // Main task continues... while (true) { idf.rtos.Task.delayMs(1000); } } ``` ### Using Custom Allocators ```zig const idf = @import("esp_idf"); const std = @import("std"); export fn app_main() callconv(.c) void { var arena = std.heap.ArenaAllocator.init(std.heap.c_allocator); defer arena.deinit(); const allocator = arena.allocator(); var list: std.ArrayList(u32) = .empty; defer list.deinit(allocator); list.append(allocator, 10) catch {}; list.append(allocator, 20) catch {}; list.append(allocator, 30) catch {}; // ... } ``` ### Accessing Raw C APIs When wrappers aren't available, use raw bindings: ```zig const idf = @import("esp_idf"); const std = @import("std"); const sys = idf.sys; const log = std.log.scoped(.@"esp-idf"); export fn app_main() callconv(.c) void { var mac_addr: [6]u8 = undefined; // Direct ESP-IDF C API call const result = sys.esp_efuse_mac_get_default(&mac_addr); if (result != sys.ESP_OK) { std.log.err("Failed to get MAC address", .{}); return; } log.info("MAC: {X:0>2}:{X:0>2}:{X:0>2}:{X:0>2}:{X:0>2}:{X:0>2}", .{ mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5], }); } pub const std_options: std.Options = .{ .logFn = idf.log.espLogFn, }; ``` --- ## Troubleshooting ### Build Errors **"zig: command not found"** - The Zig toolchain should auto-download. Check `build/zig-relsafe-*` directory - Or install manually: https://ziglang.org/download/ - For Nix users: `nix develop` **"Component not found: espressif__led_strip"** ```bash # Check idf_component.yml cat main/idf_component.yml # Reconfigure to download components idf.py reconfigure ``` **"translate-c failed"** - Check `include/stubs.h` and `include/wifi_stubs.h` for syntax errors - Ensure managed components exist in `managed_components/` - Check that `HAS_*` defines match components in `cmake/extra-components.cmake` **Binding generation errors** - The `patches/` directory contains fixes for common `translate-c` issues - If you see struct layout errors, a patch likely needs updating - Check `cmake/patch.cmake` for how patches are applied ### Flash Errors **"Serial port not found"** ```bash # Linux: Add user to dialout group sudo usermod -a -G dialout $USER # Log out and back in # Check available ports ls /dev/tty* # Windows: Check Device Manager for COM port ``` **"Failed to connect to ESP32"** - Hold BOOT button while connecting - Try different USB cable/port (must be data cable, not power-only) - Verify target matches your device: `idf.py set-target esp32c6` - Check USB drivers are installed (CP210x or CH340) ### Runtime Errors **"Guru Meditation Error: Core panic"** - Check stack size (increase in `xTaskCreate`, default 2048 often too small) - Verify GPIO pins match your hardware - Enable debug build: `idf.py menuconfig` → Compiler options → Debug (-Og) - Check `sdkconfig` for panic handler settings **"Out of memory" / Heap errors** - Use arena allocators: `std.heap.ArenaAllocator` - Check available heap: `heap.freeSize()` - Reduce allocations in hot paths - Consider enabling PSRAM if available - Check `idf.py size-components` for memory usage **WiFi not connecting** - Verify SSID and password in menuconfig - Check WiFi country code matches your region - Ensure 2.4GHz WiFi (5GHz not supported) - Check WiFi credentials don't contain special characters **Matter example: "app partition is too small"** The Matter binary is ~2.2 MB and requires a 4 MB flash chip and custom partition table: ```bash # Ensure sdkconfig has 4 MB flash and custom partition: # CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y # CONFIG_PARTITION_TABLE_CUSTOM=y # CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_matter.csv" # If sdkconfig doesn't reflect sdkconfig.defaults changes, update it: idf.py reconfigure idf.py build -DCONFIG_ZIG_EXAMPLE_MATTER_LIGHT=y ``` **Matter example: IDF version incompatibility** - `espressif/esp_matter` 1.4.x requires IDF **v5.x** (tested with v5.5) - Not compatible with IDF v6.x (depends on `json`/`mqtt` components removed in v6) - Check your IDF version: `idf.py --version` --- ## Next Steps ### Learn More - **Zig Language:** https://ziglang.org/documentation/master/ - **ESP-IDF Docs:** https://docs.espressif.com/projects/esp-idf/ - **Project Wiki:** - [Build Internals](./build-internals.md) - [Xtensa Toolchain](./zig-xtensa.md) ### Explore Examples All examples are in `main/examples/`: - `gpio-blink.zig` - Toggle an LED on GPIO2 (any target) - `uart-echo.zig` - Read UART1 and echo back - `i2c-scan.zig` - Scan I2C bus for devices - `wifi-station.zig` - Connect to a WiFi AP - `http-server.zig` - Serve a web page over WiFi - `ble-gatt-server.zig` - BLE peripheral with GATT notifications (requires `CONFIG_BT_ENABLED`) - `smartled-rgb.zig` - WS2812B LED strip control (requires `espressif/led_strip`) - `dsp-math.zig` - FFT + power spectrum via DSP (requires `espressif/esp-dsp`) - `matter-light.zig` - Matter On/Off Light device (requires `espressif/esp_matter`, IDF v5.x, 4 MB flash) ### Configuration ```bash # Interactive configuration menu idf.py menuconfig # Key settings to explore: # - Component config → FreeRTOS → Tick rate (Hz) # - Component config → ESP System Settings → Panic handler behavior # - Partition Table → Choose partition scheme # - Example Configuration → WiFi credentials # - Compiler options → Optimization level ``` ### Testing with Wokwi Simulator The project includes `wokwi.toml` for simulation: 1. Install Wokwi CLI: https://docs.wokwi.com/wokwi-ci/idf-wokwi-usage 2. Edit `wokwi.toml` to match your project 3. Run: `idf.py wokwi --timeout 30000` or use VSCode Extension * More info: https://docs.wokwi.com/vscode/getting-started ### Advanced Topics - **Custom Components:** Create reusable Zig modules in `imports/` - **WiFi & Networking:** HTTP servers, WebSockets, mDNS - **OTA Updates:** Over-the-air firmware updates - **Bluetooth:** BLE advertising, GATT services - **Deep Sleep:** Ultra-low power modes - **File Systems:** SPIFFS, FAT, LittleFS - **Cryptography:** Hardware-accelerated crypto ### Development Tips **Use `sdkconfig.defaults` for version control:** - Base config: `sdkconfig.defaults` - Target-specific: `sdkconfig.defaults.esp32` - Don't commit `sdkconfig` (it's generated) **Debugging:** ```bash # Monitor with timestamps idf.py monitor --timestamps # Filter logs by tag idf.py monitor --print-filter "tag:app" # Save logs to file idf.py monitor | tee output.log ``` **Clean builds when needed:** ```bash # Clean Zig cache rm -rf .zig-cache # Full CMake clean idf.py fullclean # Regenerate bindings idf.py reconfigure ``` ### Contributing Found a bug or want to contribute? - **GitHub:** https://github.com/kassane/zig-esp-idf-sample - **Issues:** https://github.com/kassane/zig-esp-idf-sample/issues - **Pull Requests:** See [CONTRIBUTING.md](../CONTRIBUTING.md) --- ## Quick Reference ### Common Commands ```bash # Setup idf.py set-target esp32c6 # Set target device idf.py reconfigure # Regenerate build config / update deps # Build idf.py build # Build project idf.py clean # Clean build files idf.py fullclean # Full clean (including config) # Flash idf.py -p PORT flash # Flash firmware idf.py -p PORT monitor # Monitor serial output idf.py -p PORT flash monitor # Flash and monitor idf.py -p PORT app-flash # Flash app only (faster) # Config idf.py menuconfig # Interactive configuration idf.py size # Show binary sizes idf.py size-components # Component size breakdown # Dependencies idf.py add-dependency PKG # Add managed component # Help idf.py --help # Show all commands idf.py --list-targets # List supported targets ``` ### File Locations - **Main app:** `main/app.zig` - **Examples:** `main/examples/*.zig` - **Dependencies:** `main/idf_component.yml` - **Configuration:** `sdkconfig`, `sdkconfig.defaults*` - **Bindings:** `imports/idf-sys.zig` (auto-generated) - **Wrappers:** `imports/*.zig` - **Build scripts:** `cmake/*.cmake` ### Pin Configuration Check your board's pinout: - **GPIO Pins:** Varies by model (ESP32: 0-39, ESP32-C6: 0-30) - **Built-in LED:** Often GPIO 2, 8, or 18 - **UART:** Usually GPIO 1 (TX), GPIO 3 (RX) - **I2C:** SDA/SCL pins vary by board - **SPI:** MOSI/MISO/CLK pins vary by board ### Hardware Requirements - **ESP32 Development Board** (any supported variant) - **USB Cable** (must be data cable, not power-only) - **LEDs/Components** (optional, for examples) - **LED Strip WS2812B** (optional, for smartled-rgb example) --- ## Support - **Documentation:** [docs/](.) - **Issues:** https://github.com/kassane/zig-esp-idf-sample/issues - **Discussions:** https://github.com/kassane/zig-esp-idf-sample/discussions