Added the zig_main project to software for zig based implementation of code for robot

This commit is contained in:
2026-05-05 20:14:04 +12:00
parent 7d752f2534
commit f21f909a71
83 changed files with 13631 additions and 0 deletions

View File

@@ -0,0 +1,341 @@
## How does the mixin build-system work?
#### Intro
ESP-IDF uses the `idf.py` script as a wrapper around CMake. It's responsible for creating the build environment, running CMake to generate build files, and using Ninja to build the project.
The Zig build system (`build.zig`) is a wrapper around the Zig compiler and integrates with ESP-IDF's CMake infrastructure.
For more details about Zig commands, see [doc/zig-xtensa](zig-xtensa.md)
#### Building this project
After cloning this project, you need to install ESP-IDF and set up the environment:
1. **Install ESP-IDF** by following the official guide:
- Clone ESP-IDF repository:
```bash
git clone --recursive https://github.com/espressif/esp-idf.git
```
- Run the installation script:
- Windows: `install.bat` or `install.ps1`
- POSIX: `./install.sh`
2. **Set up the ESP-IDF environment variables:**
- Windows: run `export.bat` or `./export.ps1`
- POSIX: `. ./export.sh`
Once the environment is set up, you can build the project using this scheme:
![Build Scheme](build-scheme.png)
3. **Set the target ESP device** (if not already set):
```bash
idf.py set-target <esp-device>
```
**Supported targets:**
- RISC-V: `esp32c2`, `esp32c3`, `esp32c5`, `esp32c6`, `esp32c61`, `esp32c61eco0`, `esp32h2`, `esp32h21`, `esp32h4`, `esp32s31`, `esp32p4`, `esp32p4eco4`
- Xtensa: `esp32`, `esp32s2`, `esp32s3`
4. **Add managed components** (optional):
```bash
idf.py add-dependency espressif/led_strip
idf.py add-dependency espressif/esp-dsp
```
Managed components are automatically detected during build and their headers are included in the Zig bindings.
5. **Build the project:**
```bash
idf.py build
```
This will:
- Configure CMake and detect managed components
- Download/use appropriate Zig toolchain (espressif or upstream)
- Generate `idf-sys.zig` bindings via `translate-c`
- Apply target-specific patches
- Build Zig code into object files
- Link everything with ESP-IDF components
6. **Flash the firmware to your device:**
```bash
idf.py -p PORT flash
```
Replace `PORT` with your device's serial port (e.g., `COM3` on Windows or `/dev/ttyUSB0` on Linux)
7. **Monitor the device output:**
```bash
idf.py monitor
```
**Additional useful commands:**
- Clean the project: `idf.py clean`
- Full clean and rebuild: `idf.py fullclean`
- Build and flash in one command: `idf.py -p PORT flash monitor`
- Show all targets: `idf.py --list-targets`
- Configure project: `idf.py menuconfig`
- Reconfigure (refresh component detection): `idf.py reconfigure`
---
### Current role of `build.zig`
Since the latest refactors (post [#37](https://github.com/kassane/zig-esp-idf-sample/pull/37) and related CMake changes), `build.zig` is **focused exclusively on Zig code compilation**:
**What `build.zig` handles:**
- Defines Zig modules:
- `esp_idf` → High-level facade module that re-exports safe wrappers from `imports/*.zig` (gpio, wifi, heap, rtos, led, etc.)
- `sys` → Low-level module that imports the generated `idf-sys.zig` (raw C bindings)
- Collects and compiles Zig source files
- Uses pre-generated includes and dependencies from CMake
- Generates object files (`app_zig.o`) that CMake links with ESP-IDF
**What has moved to CMake:**
- Searching and linking IDF object files and `.a` libraries → handled by ESP-IDF's CMake system
- Collecting include paths from IDF components → automatic via `zig-config.cmake`
- Running `translate-c` on `stubs.h` to generate `idf-sys.zig` → fully automated in `cmake/` scripts
- Applying target-specific patches → handled by `cmake/patch.cmake`
- Detecting and including managed components → automatic detection in `zig-config.cmake`
- Toolchain selection (espressif vs upstream Zig) → automatic based on target architecture
**This separation makes the project cleaner:**
- **CMake** handles the complex C/ESP-IDF world (toolchain, bindings generation, component management)
- **Zig** handles only modern, safe, comptime-friendly code compilation
---
### Managed Components Integration
The build system automatically detects managed components installed via `idf.py add-dependency`:
**Add extra-components like:**
- `espressif/led_strip` → `HAS_LED_STRIP` define
- `espressif/esp-dsp` → `HAS_ESP_DSP` define
- `espressif/esp_bsp_devkit` → `HAS_ESP_BSP_DEVKIT` define
**How it works:**
1. CMake detects components in `managed_components/` directory
2. Adds component include paths to `INCLUDE_DIRS`
3. Generates preprocessor defines (e.g., `HAS_LED_STRIP=1`)
4. Passes defines to `translate-c` for binding generation
5. Headers are conditionally included in `include/stubs.h`:
```c
#if HAS_LED_STRIP
#include "led_strip.h"
#endif
```
**To add a new managed component:**
1. Run: `idf.py add-dependency vendor/component`
2. Add detection in `extra-components.cmake`:
```cmake
check_managed_component("Component Name" "vendor" "component" "HAS_COMPONENT")
```
3. Add conditional include in `include/stubs.h`
4. Use in Zig via the wrapped API in `imports/`
---
### Toolchain Selection
The build system intelligently selects the appropriate Zig toolchain:
**RISC-V targets (all variants including H4/P4):**
- Works with both **upstream Zig** and **Espressif's Zig fork**
- Upstream Zig: uses generic `riscv32-freestanding-none` with feature flags (e.g. `+m+a+c+zicsr+zifencei`)
- Espressif fork: uses named CPU models (e.g. `esp32c6`, `esp32p4`) with exact feature sets
- Build system auto-detects which toolchain is in use and selects the right CPU model
**Xtensa targets (ESP32, ESP32-S2, ESP32-S3):**
- Always uses **Espressif's Zig fork** (Xtensa support not in upstream)
The toolchain is automatically downloaded and cached in `build/zig-relsafe-*` if not found.
---
### Advanced: Build System Internals
For advanced users who want to understand or modify the build system:
**Key CMake files:**
- `cmake/zig-config.cmake` → Main configuration, target detection, component discovery
- `cmake/zig-download.cmake` → Automatic Zig toolchain download
- `cmake/zig-runner.cmake` → Helper functions for running Zig commands
- `cmake/bindings.cmake` → get esp-rs/esp-idf-sys bindings header
- `cmake/extra-components.cmake` → helper to add more components
- `cmake/patch.cmake` → Post-processing patches for generated bindings
**Bindings generation flow:**
1. CMake collects all IDF component include paths
2. Detects managed components and adds their paths
3. Runs `zig translate-c` on `include/stubs.h` with all includes
4. Generates `imports/idf-sys.zig` with raw C bindings
5. Applies target-specific patches (ESP32-H2, H4, P4)
6. Zig code imports via `@import("sys")` or high-level `@import("esp_idf")`
**Zig module structure:**
```
imports/
├── idf-sys.zig # Generated C bindings (don't edit manually)
├── esp_idf.zig # Main facade module
├── gpio.zig # GPIO wrapper
├── wifi.zig # WiFi wrapper
├── heap.zig # Heap allocators
├── rtos.zig # FreeRTOS wrappers
├── led-strip.zig # LED strip wrapper (requires HAS_LED_STRIP)
└── ... # Other high-level wrappers
```
**In your Zig code:**
```zig
const idf = @import("esp_idf");
const sys = idf.sys; // Access raw C bindings if needed
const led = idf.led; // Use wrapped APIs (recommended)
```
This architecture allows safe, idiomatic Zig code while maintaining full access to ESP-IDF's C APIs when necessary.
### Build Scheme Graph
```
┌─────────────────────────────────────────────────────────────────────────┐
│ ESP-IDF Build System │
└─────────────────────────────────────────────────────────────────────────┘
┌───────────────┐
│ idf.py │
│ (Python) │
└───────┬───────┘
┌───────────────┴───────────────┐
▼ ▼
┌───────────────┐ ┌──────────────────┐
│ set-target │ │ add-dependency │
│ (esp32c6) │ │ (managed_comps) │
└───────┬───────┘ └────────┬─────────┘
│ │
└──────────────┬──────────────┘
┌─────────────────┐
│ CMake │
│ Configure │
└────────┬────────┘
┌─────────────────────────┼─────────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌─────────────────────┐ ┌──────────────────┐
│ ESP-IDF │ │ zig-config.cmake │ │ Component │
│ Components │ │ • Detect target │ │ Detection │
│ (.a libs) │ │ • Find toolchain │ │ (managed_comps) │
└──────┬───────┘ │ • Collect includes │ └────────┬─────────┘
│ │ • Check components │ │
│ └──────────┬──────────┘ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ bindings.cmake │ │
│ │ • Build INCLUDE │◄────────────┘
│ │ list with │
│ │ managed_comps │
│ │ • Generate defines │
│ │ (HAS_COMP_NAME) │
│ └──────────┬──────────┘
│ │
│ ┌──────────▼──────────┐
│ │ zig translate-c │
│ │ stubs.h → │
│ │ idf-sys.zig │
│ └──────────┬──────────┘
│ │
│ ┌──────────▼──────────┐
│ │ patch.cmake │
│ │ • Fix bitfields │
│ │ • Target patches │
│ │ (H2, H4, P4) │
│ └──────────┬──────────┘
│ │
│ ▼
│ ┌─────────────────────┐
│ │ build.zig │◄─────────┐
│ │ • Import idf-sys │ │
│ │ • Define modules: │ │
│ │ - esp_idf │ │
│ │ - sys │ │
│ │ • Compile Zig │ │
│ │ sources │ │
│ └──────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ Zig Compiler │ │
│ │ (upstream or │ │
│ │ espressif fork) │ │
│ └──────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ app_zig.o │ │
│ │ (Zig object) │ │
│ └──────────┬──────────┘ │
│ │ │
└───────────────────────┼─────────────────────┘
┌─────────────────────┐
│ Ninja / Make │
│ Link all objects │
│ + ESP-IDF libs │
└──────────┬──────────┘
┌─────────────────────┐
│ ELF Binary │
│ ├─ bootloader.bin │
│ ├─ partition.bin │
│ └─ app.bin │
└──────────┬──────────┘
┌──────────▼──────────┐
│ idf.py flash │
│ (esptool.py) │
└──────────┬──────────┘
┌──────────┐
│ ESP32 │
│ Device │
└──────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ Key Data Flows │
├─────────────────────────────────────────────────────────────────────────┤
│ 1. Component Discovery: CMake → managed_components/ → HAS_* defines │
│ 2. Binding Generation: stubs.h + includes → translate-c → idf-sys.zig │
│ 3. Zig Compilation: build.zig → zig build-obj → app_zig.o │
│ 4. Final Link: ESP-IDF .a libs + app_zig.o → firmware.elf │
└─────────────────────────────────────────────────────────────────────────┘
```
### Led-dtrip component e.g:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Managed Components Flow │
├─────────────────────────────────────────────────────────────────────────┤
│ idf.py add-dependency espressif/led_strip │
│ ↓ │
│ managed_components/espressif__led_strip/ │
│ ↓ │
│ zig-config.cmake detects component │
│ ↓ │
│ Adds: -DHAS_LED_STRIP=1 -I.../espressif__led_strip/include │
│ ↓ │
│ stubs.h: #if HAS_LED_STRIP → #include "led_strip.h" │
│ ↓ │
│ translate-c generates bindings │
│ ↓ │
│ Zig code: const led = @import("esp_idf").led; │
└─────────────────────────────────────────────────────────────────────────┘
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

View File

@@ -0,0 +1,820 @@
# 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

View File

@@ -0,0 +1,184 @@
# Zig for Xtensa (Esp32, Esp8266 and Esp32-S series)
Like [esp-rs](https://github.com/espressif/rust-esp32-example/blob/main/docs/rust-on-xtensa.md), forked zig toolchain uses **LLVM codegen** for xtensa target.
**Current version:**
- **Zig**: v0.16.0 ([bootstrap fork](https://github.com/kassane/zig-espressif-bootstrap))
- **LLVM**: v21.1.0 ([espressif-fork](https://github.com/espressif/llvm-project))
### Commands
**Zig command line interface:**
- `build-lib`: build static-lib or shared-lib (add `-dynamic` flag);
- `build-obj`: build object file, like `clang/gcc -c`.
- `build-exe`: build executable
- `build`: build-system mode, need `build.zig`.
**Clang command line interface:**
- `zig cc`: clang CLI
- `zig c++`: clang++ CLI (uses `llvm-libc++` + `llvm-libunwind` by default)
**Note:** Zig toolchain does not change `libclang` codegen. However, the default config uses `-fsanitize=undefined`.
### Targets available
```bash
$> zig build-lib --show-builtin -target xtensa-freestanding-none -mcpu=(empty or any text)
info: available CPUs for architecture 'xtensa':
cnl
esp32
esp32s2
esp32s3
esp8266
generic
error: unknown CPU: ''
```
**Note:** Freestanding targets are not listed on `zig targets | jq .libc` (triple-targets)
#### CPU Features
Similar to [Targets available](#targets-available) command, add `-mcpu` or `-Dcpu=` (need `build.zig`).
- `+` add feature
- `-` remove feature
**Note:** For show feature list add `+`/`-` without feature name
**e.g.:**
```bash
$> zig build-lib --show-builtin -target xtensa-freestanding-none -mcpu=esp32+
info: available CPU features for architecture 'xtensa':
bool: Enable Xtensa Boolean extension
clamps: Enable Xtensa CLAMPS option
coprocessor: Enable Xtensa Coprocessor option
dcache: Enable Xtensa Data Cache option
debug: Enable Xtensa Debug option
density: Enable Density instructions
dfpaccel: Enable Xtensa Double Precision FP acceleration
div32: Enable Xtensa Div32 option
esp32s2ops: Support Xtensa esp32-s2 ISA extension
esp32s3ops: Support Xtensa esp32-s3 ISA extension
exception: Enable Xtensa Exception option
extendedl32r: Enable Xtensa Extended L32R option
forced_atomics: Assume that lock-free native-width atomics are available
fp: Enable Xtensa Single FP instructions
hifi3: Enable Xtensa HIFI3 instructions
highpriinterrupts: Enable Xtensa HighPriInterrupts option
highpriinterrupts_level3: Enable Xtensa HighPriInterrupts Level3
highpriinterrupts_level4: Enable Xtensa HighPriInterrupts Level4
highpriinterrupts_level5: Enable Xtensa HighPriInterrupts Level5
highpriinterrupts_level6: Enable Xtensa HighPriInterrupts Level6
highpriinterrupts_level7: Enable Xtensa HighPriInterrupts Level7
interrupt: Enable Xtensa Interrupt option
loop: Enable Xtensa Loop extension
mac16: Enable Xtensa MAC16 instructions
minmax: Enable Xtensa MINMAX option
miscsr: Enable Xtensa Miscellaneous SR option
mul16: Enable Xtensa Mul16 option
mul32: Enable Xtensa Mul32 option
mul32high: Enable Xtensa Mul32High option
nsa: Enable Xtensa NSA option
prid: Enable Xtensa Processor ID option
regprotect: Enable Xtensa Region Protection option
rvector: Enable Xtensa Relocatable Vector option
s32c1i: Enable Xtensa S32C1I option
sext: Enable Xtensa Sign Extend option
threadptr: Enable Xtensa THREADPTR option
timers1: Enable Xtensa Timers 1
timers2: Enable Xtensa Timers 2
timers3: Enable Xtensa Timers 3
windowed: Enable Xtensa Windowed Register option
error: unknown CPU feature: ''
```
#### Target info (builtin)
**Note:** If like syntax-highlighting use `| bat -p -l zig` pipeline command or save this output as `builtin.zig` and open on your code editor.
```bash
$> zig build-lib --show-builtin -target xtensa-freestanding-none -mcpu=esp32s3
```
```zig
const std = @import("std");
/// Zig version. When writing code that supports multiple versions of Zig, prefer
/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.
pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable;
pub const zig_version_string = "0.16.0-xtensa-dev.2287+eb3f16db5";
pub const zig_backend = std.builtin.CompilerBackend.stage2_llvm;
pub const output_mode: std.builtin.OutputMode = .Lib;
pub const link_mode: std.builtin.LinkMode = .static;
pub const unwind_tables: std.builtin.UnwindTables = .async;
pub const is_test = false;
pub const single_threaded = false;
pub const abi: std.Target.Abi = .none;
pub const cpu: std.Target.Cpu = .{
.arch = .xtensa,
.model = &std.Target.xtensa.cpu.esp32s3,
.features = std.Target.xtensa.featureSet(&.{
.bool,
.clamps,
.coprocessor,
.dcache,
.debug,
.density,
.div32,
.esp32s3ops,
.exception,
.fp,
.highpriinterrupts,
.highpriinterrupts_level7,
.interrupt,
.loop,
.mac16,
.minmax,
.miscsr,
.mul16,
.mul32,
.mul32high,
.nsa,
.prid,
.regprotect,
.rvector,
.s32c1i,
.sext,
.threadptr,
.timers3,
.windowed,
}),
};
pub const os: std.Target.Os = .{
.tag = .freestanding,
.version_range = .{ .none = {} },
};
pub const target: std.Target = .{
.cpu = cpu,
.os = os,
.abi = abi,
.ofmt = object_format,
.dynamic_linker = .none,
};
pub const object_format: std.Target.ObjectFormat = .elf;
pub const mode: std.builtin.OptimizeMode = .Debug;
pub const link_libc = false;
pub const link_libcpp = false;
pub const have_error_return_tracing = true;
pub const valgrind_support = false;
pub const sanitize_thread = false;
pub const fuzz = false;
pub const position_independent_code = false;
pub const position_independent_executable = false;
pub const strip_debug_info = false;
pub const code_model: std.builtin.CodeModel = .default;
pub const omit_frame_pointer = false;
```