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
}
}