nand_flash_bdl

| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |

# SPI NAND Flash — FatFS via BDL (Block Device Layer)

This example is like [examples/nand_flash](../nand_flash/README.md), but it mounts FatFS using ESP-IDF’s generic BDL path:

- **`spi_nand_flash_init_with_layers()`** builds **Flash BDL → WL BDL** (Dhara wear leveling).
- **`esp_vfs_fat_bdl_mount()`** / **`esp_vfs_fat_bdl_unmount()`** connect VFS + FatFS to that WL block device.

Canonical source: [`main/spi_nand_flash_bdl_example_main.c`](main/spi_nand_flash_bdl_example_main.c).

## Use in your own project

### 1. Add the component dependency

```bash
idf.py add-dependency "espressif/spi_nand_flash_fatfs"
```

Or add to your project's `idf_component.yml`:

```yaml
dependencies:
  espressif/spi_nand_flash_fatfs:
    version: "*"
```

This also resolves `espressif/spi_nand_flash` transitively.

### 2. Declare the component in CMake

In your main component's `CMakeLists.txt`:

```cmake
idf_component_register(SRCS "main.c"
                       INCLUDE_DIRS "."
                       PRIV_REQUIRES spi_nand_flash_fatfs)
```

### 3. Required headers

Match the includes in [`main/spi_nand_flash_bdl_example_main.c`](main/spi_nand_flash_bdl_example_main.c):

```c
#include <assert.h>
#include <stdio.h>
#include <string.h>

#include "esp_blockdev.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_vfs_fat.h"
#include "esp_vfs_fat_nand.h"   /* esp_vfs_fat_nand_bdl_format (optional) */
#include "driver/spi_master.h"
#include "soc/spi_pins.h"
#include "spi_nand_flash.h"
```

### 4. Initialization flow

Follow the same order as `example_spi_nand_setup()` / `app_main()` in the example source:

1. **SPI bus** — `spi_bus_initialize()` with `spi_bus_config_t` (MOSI, MISO, CLK, WP, HD GPIOs).
2. **SPI device** — `spi_bus_add_device()` with `spi_device_interface_config_t` (clock, CS, mode, `flags`).
3. **NAND driver** — `spi_nand_flash_init_with_layers()` with `spi_nand_flash_config_t` (returns WL `esp_blockdev_handle_t`).
4. **Optional pre-format** — `esp_vfs_fat_nand_bdl_format()` with `esp_vfs_fat_mount_config_t` (same config you will use for mount).
5. **FAT mount** — `esp_vfs_fat_bdl_mount()` with `esp_vfs_fat_mount_config_t`.
6. **File I/O** — standard `fopen()` / `fprintf()` / `fgets()` on paths under the mount point (e.g. `/nandflash/hello.txt`).
7. **Cleanup** — `esp_vfs_fat_bdl_unmount()`, then `wl_bdl->ops->release(wl_bdl)`, `spi_bus_remove_device()`, `spi_bus_free()` (see `example_spi_nand_teardown()`).

Default pin macros (`HOST_ID`, `PIN_MOSI`, etc.) are defined at the top of the example source. Wiring matches [examples/nand_flash](../nand_flash/README.md).

### 5. `spi_nand_flash_config_t`

Populated in `example_spi_nand_setup()` before `spi_nand_flash_init_with_layers()`:

| Field | Description |
|-------|-------------|
| `device_handle` | `spi_device_handle_t` returned by `spi_bus_add_device()` |
| `io_mode` | `SPI_NAND_IO_MODE_SIO` in this example; also `DIO`, `DOUT`, `QIO`, or `QOUT` |
| `flags` | `SPI_DEVICE_HALFDUPLEX` for half-duplex (required for DIO/DOUT/QIO/QOUT); `0` for full-duplex SIO. This value has to match the half-duplex flag in `spi_device_interface_config_t.flags` |
| `gc_factor` | Optional wear-leveling GC tuning; omit or set `0` for the driver default |

Full struct documentation is in [`spi_nand_flash.h`](../../../spi_nand_flash/include/spi_nand_flash.h). The same config type is used for legacy `spi_nand_flash_init_device()`.

### 6. Mount configuration

```c
const esp_vfs_fat_mount_config_t mount_config = {
    .max_files = 4,
    .format_if_mount_failed = false,  // or true to format on first mount failure
    // Cluster size used when formatting. Must be a power of 2 between the
    // logical sector size and 128 * sector size. Larger values improve
    // sequential throughput; smaller values waste less space for small files.
    // 16 KiB matches this example (and common ESP-IDF FatFs samples);
    // use 0 to default to one sector per cluster.
    .allocation_unit_size = 16 * 1024,
};

#ifdef CONFIG_EXAMPLE_FORMAT_BEFORE_MOUNT
ESP_ERROR_CHECK(esp_vfs_fat_nand_bdl_format(wl_bdl, &mount_config));
#endif
ESP_ERROR_CHECK(esp_vfs_fat_bdl_mount("/nandflash", wl_bdl, &mount_config));
```

In this example project, `format_if_mount_failed` and pre-mount format are controlled by `CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED` and `CONFIG_EXAMPLE_FORMAT_BEFORE_MOUNT` in menuconfig.

### 7. Prerequisites

- **ESP-IDF 6.1+** (`esp_vfs_fat_bdl_mount` / `diskio_bdl`).
- Enable **`CONFIG_NAND_FLASH_ENABLE_BDL=y`** (Component config → SPI NAND Flash). When BDL is on, **`spi_nand_flash_init_device()` is not available**.
- See also [`spi_nand_flash_fatfs` component README](../../README.md#requirements-read-first).

## Requirements

- **ESP-IDF 6.1 or newer** (`esp_vfs_fat_bdl_mount` / `diskio_bdl`; NAND BDL itself needs IDF 6.0+).
- **`CONFIG_NAND_FLASH_ENABLE_BDL=y`** (set in this example’s `sdkconfig.defaults`). When BDL is enabled, **`spi_nand_flash_init_device()` is not available**; use the layered API as shown here.

## Hardware

Same wiring as [examples/nand_flash](../nand_flash/README.md) (SPI bus + external SPI NAND).

## Configuration

- **`CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED`**: if enabled, the volume is formatted when mount fails (same idea as the legacy example).
- **`CONFIG_EXAMPLE_FORMAT_BEFORE_MOUNT`**: if enabled, the example calls **`esp_vfs_fat_nand_bdl_format()`** (from `spi_nand_flash_fatfs`) with the same **`esp_vfs_fat_mount_config_t`** (cluster size, one/two FATs) *before* **`esp_vfs_fat_bdl_mount()`**, so you can force a layout that matches your mount options without depending on mount-time formatting. CI enables this via `sdkconfig.ci`.
- **`sdkconfig.defaults`** sets **`CONFIG_ESP_TASK_WDT_EN=n`** so long format/mount work on NAND is less likely to hit the task watchdog.

## Build and run

```bash
cd examples/nand_flash_bdl
idf.py set-target esp32
idf.py -p PORT flash monitor
```

Expected log sequence: WL BDL geometry, optional “Pre-mount format finished”, mount, “Opening file” / “File written” / “Reading file” / “Read from file:”, unmount, “Done”.

## How this differs from `nand_flash`

| Example        | NAND init                         | FatFS mount                    | `CONFIG_NAND_FLASH_ENABLE_BDL` |
| -------------- | --------------------------------- | ------------------------------ | ------------------------------ |
| `nand_flash`   | `spi_nand_flash_init_device()`    | `esp_vfs_fat_nand_mount()`     | **Must be off**                |
| `nand_flash_bdl` | `spi_nand_flash_init_with_layers()` | `esp_vfs_fat_bdl_mount()`   | **Must be on** (this example)  |

Mount uses ESP-IDF’s **`esp_vfs_fat_bdl_mount()`**. Optional pre-format uses **`esp_vfs_fat_nand_bdl_format()`** from this component (same `f_mkfs` layout as IDF’s BDL mount path).

To create a project from this example, run:

idf.py create-project-from-example "espressif/spi_nand_flash_fatfs=1.1.1:nand_flash_bdl"

or download archive (~7.17 KB)