esp32s3 驱动pcm5102a 的 wav播放器,mqtt控制

发布于:2025-08-14 ⋅ 阅读:(14) ⋅ 点赞:(0)

mqtt 命令:

1.音量: mosquitto_pub -h   192.168.101.233  -t  esp32/volume  -m  0.54

2.停止播放:mosquitto_pub -h   192.168.101.233  -t  esp32/control  -m stop

3.选曲:mosquitto_pub -h   192.168.101.233  -t  esp32/play  -m  20.wav

4.全部播放:mosquitto_pub -h   192.168.101.233  -t  esp32/playall  -m on

接着发:mqtt门窗传感器制作和代码,mqtt 智能开关和代码,mqtt 控制的esp32cam 摄像头和代码 

准备写自制一个家庭智能系统的过程,从deepin安装开始,接着idf安装的过程。

也考虑拍视频上传抖音,准备中

home assistant   控制已完成的部分esp32硬件

代码中sd卡的spi读取频率非常重要,太大太小都影响音质。由于设置错误一直破音排查了好久。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"

#include "esp_log.h"
#include "esp_err.h"
#include "esp_vfs_fat.h"

#include "driver/i2s_std.h"
#include "driver/sdspi_host.h"
#include "sdmmc_cmd.h"
#include  <dirent.h> 
#include "esp_wifi.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "mqtt_client.h"
#include <math.h>
#define TAG "WAV_PLAYER"

// SD 卡 SPI 引脚
#define PIN_NUM_MISO 2
#define PIN_NUM_MOSI 3
#define PIN_NUM_CLK  4
#define PIN_NUM_CS   1

#define SZ 48000

// PCM5102A I2S 引脚
#define I2S_BCK_IO   17
#define I2S_WS_IO    8
#define I2S_DATA_IO  18

// 音量控制变量(由 MQTT 控制)
float g_volume = 0.5f;

char current_song[272] = "/sdcard/7.wav";  // 默认歌曲
bool should_play = true;
bool stop_playback = false;
bool pause_playback = false;
bool play_all=false;
// 采样率和高通滤波器参数
#define SAMPLE_RATE     48000.0f
#define HPF_CUTOFF_FREQ 100.0f


static i2s_chan_handle_t tx_chan;

// WAV 文件头结构体
typedef struct {
    char riff[4];
    uint32_t chunk_size;
    char wave[4];
    char fmt[4];
    uint32_t fmt_len;
    uint16_t audio_format;
    uint16_t num_channels;
    uint32_t sample_rate;
    uint32_t byte_rate;
    uint16_t block_align;
    uint16_t bits_per_sample;
    char data[4];
    uint32_t data_size;
} wav_header_t;

// 初始化 I2S
static void init_i2s(void) {
    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
    ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, NULL));
    i2s_std_config_t std_cfg = {
        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SZ),
        .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
        .gpio_cfg = {
            .mclk = I2S_GPIO_UNUSED,
            .bclk = I2S_BCK_IO,
            .ws = I2S_WS_IO,
            .dout = I2S_DATA_IO,
            .din = I2S_GPIO_UNUSED,
        },
    };
    ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
    ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
}


// 初始化 SD 卡
static esp_err_t init_sd_card(void) {
    esp_err_t ret;
    sdmmc_card_t *card;
    const char mount_point[] = "/sdcard";

    ESP_LOGI(TAG, "Initializing SD card...");

   
    sdmmc_host_t host = SDSPI_HOST_DEFAULT();
    host.slot = SPI2_HOST;
    host.max_freq_khz = 10000;


   spi_bus_config_t bus_cfg = {
        .mosi_io_num = PIN_NUM_MOSI,
        .miso_io_num = PIN_NUM_MISO,
        .sclk_io_num = PIN_NUM_CLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz =2*4096,
    };
    ret = spi_bus_initialize(host.slot, &bus_cfg, SDSPI_DEFAULT_DMA);
    if (ret != ESP_OK) return ret;

    sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
    slot_config.gpio_cs = PIN_NUM_CS;
    slot_config.host_id = host.slot;

    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        .format_if_mount_failed = false,
        .max_files = 3,
        .allocation_unit_size = 16 * 1024
    };
    ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to mount SD card: %s", esp_err_to_name(ret));
        return ret;
    }

    ESP_LOGI(TAG, "SD card mounted: %s", card->cid.name);
    return ESP_OK;
}

//----------------------------------------
static void play_wav(const char *path) {
    FILE *f = fopen(path, "rb");
    if (!f) {
        ESP_LOGE(TAG, "Failed to open file: %s", path);
        return;
    }

    // 跳过 WAV 头(44 字节)
    fseek(f, 44, SEEK_SET);

    // 注意:这里你假设了44100Hz,如果你的WAV不是这个采样率,建议解析头部取样率
    i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(44100);
    i2s_channel_disable(tx_chan);
    ESP_ERROR_CHECK(i2s_channel_reconfig_std_clock(tx_chan, &clk_cfg));
    i2s_channel_enable(tx_chan);

    uint8_t buffer[1024];
    size_t bytes_read, bytes_written;

    while ((bytes_read = fread(buffer, 1, sizeof(buffer), f)) > 0) {
        if (stop_playback) {
            ESP_LOGI(TAG, "Playback stopped");
              int16_t silence[128] = {0};  // 256字节静音
         size_t written;
       i2s_channel_write(tx_chan, silence, sizeof(silence), &written, portMAX_DELAY);

       // 可选:延时一点点,给 DAC 稳定时间
       vTaskDelay(pdMS_TO_TICKS(10));

    // 停止通道
        i2s_channel_disable(tx_chan);
            break;
        }

        int16_t *samples = (int16_t *)buffer;
        int num_samples = bytes_read / sizeof(int16_t);

        for (int i = 0; i < num_samples; i++) {
            // 音量调节,确保在有效范围内
            float adjusted = samples[i] * g_volume;
            if (adjusted > 32767.0f) adjusted = 32767.0f;
            if (adjusted < -32768.0f) adjusted = -32768.0f;
            samples[i] = (int16_t)adjusted;
        }

        i2s_channel_write(tx_chan, buffer, bytes_read, &bytes_written, portMAX_DELAY);
    }

    fclose(f);
    ESP_LOGI(TAG, "Playback finished: %s", path);
}


// WiFi事件标志
static EventGroupHandle_t wifi_event_group;
const int WIFI_CONNECTED_BIT = BIT0;

static void wifi_event_handler(void *arg, esp_event_base_t event_base,
                               int32_t event_id, void *event_data) {
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        esp_wifi_connect();
        xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT);
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

static void wifi_init_sta(void) {
    wifi_event_group = xEventGroupCreate();

    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL));

    wifi_config_t wifi_config = {
        .sta = {
            .ssid ="CMCC-6ZN9",
            .password = "2KJKWEML",
            .threshold.authmode = WIFI_AUTH_WPA2_PSK,
            .pmf_cfg = {
                .capable = true,
                .required = false,
            },
        },
    };

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "wifi_init_sta finished.");

    // 等待连接完成
    EventBits_t bits = xEventGroupWaitBits(wifi_event_group,
                                           WIFI_CONNECTED_BIT,
                                           pdFALSE,
                                           pdFALSE,
                                           portMAX_DELAY);

    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "Connected to AP SSID:%s","CMCC");
    } else {
        ESP_LOGE(TAG, "Failed to connect to SSID:%s","CMCC");
    }
}

// MQTT事件回调
static esp_mqtt_client_handle_t mqtt_client = NULL;

static void mqtt_event_handler(void *handler_args, esp_event_base_t base,
                               int32_t event_id, void *event_data) {
    esp_mqtt_event_handle_t event = event_data;
    switch ((esp_mqtt_event_id_t)event_id) {
    case MQTT_EVENT_CONNECTED:
        ESP_LOGI(TAG, "MQTT Connected");
        esp_mqtt_client_subscribe(mqtt_client, "esp32/volume", 0);
	esp_mqtt_client_subscribe(mqtt_client, "esp32/control", 0);
       esp_mqtt_client_subscribe(mqtt_client, "esp32/play", 0);
      esp_mqtt_client_subscribe(mqtt_client, "esp32/playall", 0);
        break;
    case MQTT_EVENT_DISCONNECTED:
        ESP_LOGW(TAG, "MQTT Disconnected");
        break;
    case MQTT_EVENT_DATA:
        ESP_LOGI(TAG, "MQTT Data Received: topic=%.*s data=%.*s",
                 event->topic_len, event->topic,
                 event->data_len, event->data);

        if (strncmp(event->topic, "esp32/volume", event->topic_len) == 0) {
            char payload[16] = {0};
            int len = event->data_len < 15 ? event->data_len : 15;
            memcpy(payload, event->data, len);

            float vol = atof(payload);
            if (vol >= 0.0f && vol <= 1.0f) {
                g_volume = vol;
                ESP_LOGI(TAG, "Volume set to %.2f via MQTT", g_volume);
            } else {
                ESP_LOGW(TAG, "Invalid volume value received: %s", payload);
            }
        } else if (strncmp(event->topic, "esp32/control", event->topic_len) == 0) {
    		char cmd[32] = {0};
    		int len = event->data_len < 31 ? event->data_len : 31;
    		memcpy(cmd, event->data, len);
             
    	//	if (strcmp(cmd, "pause") == 0) {
        		pause_playback = true;
        //		ESP_LOGI(TAG, "Playback paused");
    	//	} else if (strcmp(cmd, "resume") == 0) {
        		pause_playback = false;
        //		ESP_LOGI(TAG, "Playback resumed");
    	//	} else
             if (strcmp(cmd, "stop") == 0) {
        		stop_playback = true;
        		ESP_LOGI(TAG, "Playback stopped");
    		}
	}else if (event->topic_len == strlen("esp32/play") &&
         strncmp(event->topic, "esp32/play", event->topic_len) == 0) {

    char song[56] = {0};
    int len = event->data_len < 55 ? event->data_len : 55;
    memcpy(song, event->data, len);
    song[len] = '\0';

    snprintf(current_song, sizeof(current_song), "/sdcard/%s", song);
    stop_playback = true;   // 停掉当前播放
    should_play = true;     // 标记准备播放新曲目
    ESP_LOGI(TAG, "Switching to song: %s", current_song);
}else if (strncmp(event->topic, "esp32/playall", event->topic_len) == 0) {
    		char cmd[32] = {0};
    		int len = event->data_len < 31 ? event->data_len : 31;
    		memcpy(cmd, event->data, len);
             if (strcmp(cmd, "on") == 0) {
        		play_all = true;
			should_play = false;
                   stop_playback = true;
                   pause_playback = false;
        		ESP_LOGI(TAG, "Playall music");
            }
}
//---------------------------------------------
        break;
    default:
        break;
    }
}

// MQTT启动函数
static void mqtt_app_start(void) {
    esp_mqtt_client_config_t mqtt_cfg = {
        .broker.address.uri = "mqtt://192.168.101.233",
    };

    mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
    esp_mqtt_client_register_event(mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
    esp_mqtt_client_start(mqtt_client);
}

void app_main(void) {
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(init_sd_card());
    wifi_init_sta();

    init_i2s();

    mqtt_app_start();
    while (1) {
 	   if (should_play) {
      		  should_play = false;
      		  stop_playback = false;
      		  pause_playback = false;

      		  play_wav(current_song);
   	 }

  	  if (play_all) {
   		 play_all = false;
   		 should_play = false;
   		 stop_playback = false;
   		 pause_playback = false;

    		DIR *dir = opendir("/sdcard");
    		if (dir == NULL) {
        		ESP_LOGE(TAG, "Failed to open /sdcard directory");
   			 } else {
       			 struct dirent *entry;
       			 while ((entry = readdir(dir)) != NULL) {
          			  if (entry->d_type == DT_REG) {
            			    const char *name = entry->d_name;
             			   size_t len = strlen(name);
             			   if (len > 4 && strcasecmp(&name[len - 4], ".wav") == 0) {
                  			  snprintf(current_song, sizeof(current_song), "/sdcard/%s", name);
                   			 ESP_LOGI(TAG, "Playing file: %s", current_song);
                   			 play_wav(current_song);

                   			 if (stop_playback) {
                       			 ESP_LOGW(TAG, "Stopped during play all");
                       			 break;
                  			  }
             		       	}
          	  		}
       		 }
        		closedir(dir);
        		ESP_LOGI(TAG, "Play all finished.");
    			}
	}

   
    vTaskDelay(pdMS_TO_TICKS(100));  // 避免占满 CPU
    }


}


网站公告

今日签到

点亮在社区的每一天
去签到