ESP-DSP Series 2 | Building Efficient Digital Filters with FIR


ESP-DSP FIR Module: A Core Component in Digital Signal Processing.The FIR (Finite Impulse Response) filter is a cornerstone in digital signal processing (DSP), known for its stability and linear phase characteristics. It is widely used in audio processing, communication systems, and sensor data filtering. This article focuses on the FIR module provided by Espressif’s ESP-DSP library, demonstrating how to leverage its high performance to implement stable and practical digital filters

ESP-DSP

Principles of FIR Filtering

A key feature of FIR filters is that their output depends only on a finite number of past input samples, which guarantees system stability and allows precise linear phase implementationโ€”avoiding phase distortion. The computation involves multiplying a set of input samples with corresponding coefficients and summing the results to produce each output sample. By adjusting the filter order (number of taps) and coefficients, various effects like low-pass, high-pass, band-pass, and band-stop can be achieved.

ESP-DSP FIR Features

ESP-DSP is a high-performance DSP library designed by Espressif for chips like the ESP32 and ESP32-S3. It includes mathematical operations and filter implementations. Key features of the FIR module include:

  • Floating-point and Fixed-point Support: Provides flexibility for various performance and precision needs.
  • Decimation Support: Enables efficient downsampling for signal processing pipelines.
  • Structured Management: Uses data structures to manage filter states, including coefficients and delay lines, making initialization and processing efficient.
  • Optimized Performance: Some functions are written in assembly for maximum performance on ESP32 architectures.
  • Simple API: Initialization and processing functions are easy to useโ€”just supply the coefficients and delay line, and you’re ready to go.

Development Environment

Before starting your programming, make sure to complete the following preparations:

Project Structure

This follows a typical ESP-IDF project structure. If you use idf.py create-project or the official ESP-IDF VS Code extension, you’ll see something like this:

esp32-fir-filter/
โ”œโ”€โ”€ CMakeLists.txt
โ”œโ”€โ”€ main/
โ”‚   โ”œโ”€โ”€ CMakeLists.txt
โ”‚   โ””โ”€โ”€ fir_filter_example.c
โ””โ”€โ”€ sdkconfig
  • CMakeLists.txt: Main build configuration file
  • main/: Contains application source code
  • fir_filter_example.c: Core example demonstrating the FIR filter logic
  • sdkconfig: Auto-generated configuration file managed by menuconfig

This structure is modular, maintainable, and aligned with Espressifโ€™s best practices.

Code

This example demonstrates how to implement a simple FIR filter using the ESP-DSP library to smooth a square wave input signal.

#include <stdio.h>
#include "esp_log.h"
#include "esp_dsp.h"
#include "dsp_filter_fir.h"

#define FILTER_TAP_NUM 32
#define SIGNAL_LENGTH 128

// Define log tag for this module
static const char *TAG = "FIR_FILTER_EXAMPLE";

// Simplified low-pass filter coefficients (replace as needed)
float filter_coeffs[FILTER_TAP_NUM] = {
    0.03125, 0.03125, 0.03125, 0.03125,
    0.03125, 0.03125, 0.03125, 0.03125,
    0.03125, 0.03125, 0.03125, 0.03125,
    0.03125, 0.03125, 0.03125, 0.03125,
    0.03125, 0.03125, 0.03125, 0.03125,
    0.03125, 0.03125, 0.03125, 0.03125,
    0.03125, 0.03125, 0.03125, 0.03125,
    0.03125, 0.03125, 0.03125, 0.03125
};

float input_signal[SIGNAL_LENGTH];
float output_signal[SIGNAL_LENGTH];
float delay_line[FILTER_TAP_NUM];

dsps_fir_t fir; // FIR filter structure

void app_main(void) {
    esp_err_t ret;

    ESP_LOGI(TAG, "ESP-DSP FIR filter example started");

    // Initialize FIR filter
    ret = dsps_fir_init_f32(&fir, filter_coeffs, delay_line, FILTER_TAP_NUM);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "FIR initialization failed");
        return;
    }

    // Generate square wave input signal
    for (int i = 0; i < SIGNAL_LENGTH; i++) {
        input_signal[i] = (i % 20 < 10) ? 1.0f : -1.0f;
    }

    // Apply FIR filter
    for (int i = 0; i < SIGNAL_LENGTH; i++) {
        output_signal[i] = dsps_fir_process_f32(&fir, input_signal[i]);
        ESP_LOGD(TAG, "Input: %.2f\tOutput: %.2f", input_signal[i], output_signal[i]);
    }

    ESP_LOGI(TAG, "Filtering completed");

    // Free FIR memory (optional)
    dsps_fir_free_f32(&fir);
}

Code Explanation

  • Filter Parameters
#define FILTER_TAP_NUM 32
#define SIGNAL_LENGTH 128
  1. FILTER_TAP_NUM defines the filter order (number of taps).
  2. SIGNAL_LENGTH specifies the number of signal samples to process.
  • Initialization
    Initializes the FIR filter structure using predefined coefficients and a delay line.
dsps_fir_init_f32(&fir, filter_coeffs, delay_line, FILTER_TAP_NUM);
  • Input Signal Generation
    Creates a synthetic square wave alternating every 10 samples between +1.0 and -1.0.
input_signal[i] = (i % 20 < 10) ? 1.0f : -1.0f;
  • Filtering
    Processes each input sample through the FIR filter, printing both input and output for inspection.
output_signal[i] = dsps_fir_process_f32(&fir, input_signal[i]);
  • Resource Cleanup
    Frees internal FIR structuresโ€”optional but good practice.
dsps_fir_free_f32(&fir);

Compile and Flash

After writing the code, you can use the ESP-IDF tools to build, flash, and monitor:

In the VS Code lower-left ESP-IDF toolbar:

  • Click Build project
  • Click Flash device
  • Click Monitor device

Output and Test Results

As the FIR convolution accumulates, output values gradually smooth out the square wave transitions. Example logs:

I (0) FIR_FILTER_EXAMPLE: ESP-DSP FIR filter example started
D (10) FIR_FILTER_EXAMPLE: Input: 1.00	Output: 0.03
D (20) FIR_FILTER_EXAMPLE: Input: 1.00	Output: 0.06
D (30) FIR_FILTER_EXAMPLE: Input: 1.00	Output: 0.09
...
D (310) FIR_FILTER_EXAMPLE: Input: -1.00	Output: 0.00
D (320) FIR_FILTER_EXAMPLE: Input: -1.00	Output: -0.03
...
I (900) FIR_FILTER_EXAMPLE: Filtering completed

Each line shows a real-time filtered output, clearly demonstrating the smoothing effect of a low-pass filter.

Conclusion

This tutorial demonstrates how to implement a stable and efficient FIR filter using the ESP-DSP library on the ESP32. With simple initialization and per-sample processing, real-time signal filtering becomes straightforwardโ€”whether for noise reduction, smoothing, or frequency shaping.

FIR filters are widely used in:

  • Audio processing
  • Communication systems
  • Image filtering
  • Sensor data conditioning
  • Industrial and control systems
  • Embedded and IoT applications

With this foundation, you can expand furtherโ€”design custom filter coefficients with tools like MATLAB, Python (SciPy), or FIR Designer, and implement high-pass, band-pass, or band-stop filters. Combine FIR with FFT for more advanced preprocessing pipelines.
With this knowledge, you’re now equipped to design customized FIR filters and integrate them into your real-time DSP workflows.