当前位置: 首页 > news >正文

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

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.0fstatic 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");//	} elseif (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}}

http://www.xdnf.cn/news/1291681.html

相关文章:

  • Flutter网络请求实战:Retrofit+Dio完美解决方案
  • 微服务单元测试组件
  • 在CentOS 7上配置Android USB网络共享方式的方法
  • Linux的进程信号
  • ASP.NET 上传文件安全检测方案
  • 设计秒杀系统从哪些方面考虑
  • 微软正式将GPT-5接入Microsoft Copilot Studio(国际版)
  • 【物联网】基于树莓派的物联网开发【26】——树莓派开启串口并配置串口助手Minicom
  • jvm学习笔记之jvm的生命周期和发展历程
  • Ansible 实操笔记:Playbook 与变量管理
  • dubbo应用之门面设计模式
  • 《Python学习之基础语法2:掌握程序流程控制的艺术》
  • 101、【OS】【Nuttx】【周边】文档构建渲染:reStructuredText 格式
  • 【C语言强化训练16天】--从基础到进阶的蜕变之旅:Day3
  • C++多态:理解面向对象的“一个接口,多种实现”
  • 《AVL树的原理与C++实现:详解平衡二叉搜索树的高效构建与操作》
  • 旧版MinIO的安装(windows)、Spring Boot 后端集成 MinIO 实现文件存储(超详细,带图文)
  • 使用 6 种方法将文件从 Android 无缝传输到iPad
  • [Linux]学习笔记系列 -- [arm][process]
  • WPF 开发的瑞士军刀:Prism 框架从入门到精通指南
  • C++写文件,open函数的参数in、out、ate、app、trunc等标志分别是什么作用?
  • C++ 面向对象四大特性:面试深度解析
  • 河南萌新联赛2025第五场 - 信息工程大学
  • IDEA创建一个VUE项目
  • C# 微软依赖注入 (Microsoft.Extensions.DependencyInjection) 详解
  • 数据分析项目----幸福感挖掘和预测
  • Python实战教程:PDF文档自动化编辑与图表绘制全攻略
  • PyTorch生成式人工智能——基于Transformer实现文本转语音
  • SeaTunnel MCP Server 入选《中国信通院开源商业产品及企业典型案例集(2025)》
  • 袖珍手持气象仪的用途