djfm
djfm

Reputation: 2468

Why don't I get any sound from my ESP32 using the esp-idf framework?

I'm hoping to make my raspiaudio muse proto play a sine wave for testing, but I only hear a very faint pulsation which doesn't even match with the output I expect.

Here's the whole code:

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "driver/i2s.h"
#include <math.h>
#include <vector>
#include <sstream>

#define I2S_BCLK    5
#define I2S_LRC    25
#define I2S_DOUT   26
#define I2S_DIN    35

/** This is necessary for the linker to find the firmware's entry point */
extern "C" {
    void app_main(void);
}

#define SAMPLE_RATE     (44100)
const i2s_port_t I2S_NUM = I2S_NUM_0;
#define PI              (3.14159265)
#define FREQUENCY       (440)
#define SAMPLES         (SAMPLE_RATE / FREQUENCY)

typedef std::vector<int16_t> VectorBuffer;

VectorBuffer generate_sine_wave(
    float sample_rate_Hz,
    float duration_s = 1.0f,
    float frequency = 440.0f,
    float amplitude = 1.0f
) {
    const auto samples_count = (int)(sample_rate_Hz * duration_s * 2);
    VectorBuffer buffer(samples_count);

    for (int i = 0; i < samples_count - 1; i++) {
        const auto sample = (int16_t)(amplitude * sin((2 * PI * i * frequency) / sample_rate_Hz));
        buffer.at(i) = sample;
        buffer.at(i + 1) = sample;
    }

    return buffer;
}

// Should play a F note for half a second every second
void i2s_task(void *pvParameter) {
    while (1) {
        auto buffer = generate_sine_wave(
            SAMPLE_RATE,
            1.0f,
            440.0f,
            floor(INT16_MAX / 3)
        );

        ESP_LOGI("I2S_TASK", "Playing sine wave of %d samples", buffer.size());

        size_t bytes_written;

        for (;;) {
            if (buffer.empty()) {
                ESP_LOGD("I2S_TASK", "Buffer is empty, good.");
                break;
            }

            esp_err_t e = i2s_write(I2S_NUM, buffer.data(), std::fmin(buffer.size(), 1024), &bytes_written, portMAX_DELAY);
            if (e != ESP_OK) {
                ESP_LOGE("I2S_TASK", "Error writing to I2S: %d", e);
                break;
            }

            if (bytes_written == 0) {
                // this should not happen
                ESP_LOGE("I2S_TASK", "No bytes written");
                break;
            }

            ESP_LOGI("I2S_TASK", "Wrote %d bytes", bytes_written);

            /* Remove too verbose logging
            std::ostringstream oss;
            for (int i = 0; i < bytes_written; i++) {
                oss << buffer.at(i) << " ";
            }
            ESP_LOGD("I2S_TASK", "Buffer: %s", oss.str().c_str());
            // */
            buffer.erase(buffer.begin(), buffer.begin() + bytes_written);
        };

        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void hello_task(void *pvParameter) {
    while (1) {
        ESP_LOGI("HELLO_TASK", "hello");
        vTaskDelay(500 / portTICK_PERIOD_MS);
    }
}

void app_main() {
    // UART configuration
    const uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
    };

    uart_param_config(UART_NUM_0, &uart_config);
    uart_driver_install(UART_NUM_0, 1024 * 2, 0, 0, NULL, 0);

    const auto mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX);

    // I2S configuration
    i2s_config_t i2s_config = {
        mode,
        .sample_rate = SAMPLE_RATE,
        .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
        .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
        .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S | I2S_COMM_FORMAT_STAND_MSB),
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
        .dma_buf_count = 3,
        .dma_buf_len = 1024,
        .use_apll = false,
        .tx_desc_auto_clear = true,
        .fixed_mclk = -1,
        .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT,
        .bits_per_chan = I2S_BITS_PER_CHAN_16BIT,
    };

    // I2S pin configuration
    i2s_pin_config_t pin_config = {
        .bck_io_num = I2S_BCLK,
        .ws_io_num = I2S_LRC,
        .data_out_num = I2S_DOUT,
        .data_in_num = I2S_DIN, // default Copilot was: I2S_PIN_NO_CHANGE // maybe 35 https://github.com/RASPIAUDIO/Simple_Bluetooth_Speaker_ESP32/blob/884c7f3700ec92dcf000dca75934860833559df7/speaker/speaker.ino#L171
    };

    // Install and start I2S driver
    const auto e_i = i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
    if (e_i != ESP_OK) {
        ESP_LOGE("APP_MAIN", "Error installing I2S driver: %d", e_i);
        return;
    }

    const auto e_p = i2s_set_pin(I2S_NUM, &pin_config);
    if (e_p != ESP_OK) {
        ESP_LOGE("APP_MAIN", "Error setting I2S pin: %d", e_p);
        return;
    }

    const auto e_s = i2s_start(I2S_NUM);
    if (e_s != ESP_OK) {
        ESP_LOGE("APP_MAIN", "Error starting I2S: %d", e_s);
        return;
    }

    // Create the hello_task
    xTaskCreate(&hello_task, "hello_task", 2048, NULL, 5, NULL);

    // Create the i2s_task
    xTaskCreate(&i2s_task, "i2s_task", 2048, NULL, 5, NULL);
}

Any idea?

Upvotes: 1

Views: 44

Answers (0)

Related Questions