- Posts: 4
- Thank you received: 0
[固件生成] 【Hardware Configuration (JSON)】 { "chip": "ESP32", "peripherals": [ { "type": "audio_a
- btt
-
Topic Author
- Offline
- New Member
-
Less
More
3 weeks 17 hours ago #44
by btt
New Topic
硬件配置(JSON格式):
原理图:
生成的固件代码:
点击这里使用固件生成器
Code:
【Hardware Configuration (JSON)】
{
"chip": "ESP32",
"peripherals": [
{
"type": "audio_amp",
"model": "TDA7492P",
"pins": {
"bclk": "GPIO17",
"lrc": "GPIO18",
"din": "GPIO16"
}
},
{
"type": "microphone",
"model": "MAX98357A",
"pins": {
"bclk": "GPIO34",
"ws": "GPIO35",
"data": "GPIO39"
}
},
{
"type": "button",
"model": null,
"pins": {
"signal_name": "GPIO0"
}
},
{
"type": "led",
"model": null,
"pins": {
"signal_name": "GPIO2"
}
}
]
}
原理图:
生成的固件代码:
Code:
```c
#include <stdio.h>
#include <string.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_gap_bt_api.h"
#include "esp_a2dp_api.h"
#include "esp_avrc_api.h"
#include "driver/gpio.h"
#include "driver/i2s.h"
static const char *TAG = "A2DP_SINK";
// Pin definitions
#define I2S_BCLK_GPIO 17
#define I2S_LRC_GPIO 18
#define I2S_DIN_GPIO 16
#define MIC_BCLK_GPIO 34 // not used in this example
#define MIC_WS_GPIO 35
#define MIC_DATA_GPIO 39
#define BUTTON_GPIO 0
#define LED_GPIO 2
// I2S configuration
#define I2S_PORT I2S_NUM_0
#define I2S_SAMPLE_RATE 44100
#define I2S_BITS_PER_SAMPLE I2S_BITS_PER_SAMPLE_16BIT
#define I2S_CHANNEL_FORMAT I2S_CHANNEL_FMT_RIGHT_LEFT
#define I2S_COMM_FORMAT I2S_COMM_FORMAT_STAND_I2S
// A2DP state
static bool a2dp_connected = false;
static bool a2dp_playing = false;
static uint8_t volume = 100;
// Function prototypes
static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param);
static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param);
static void bt_app_avrc_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param);
static void i2s_init(void);
static void i2s_write_data(const uint8_t *data, size_t len);
static void button_handler(void *arg);
static void led_update(void);
void app_main(void)
{
// Init I2S
i2s_init();
// Init GPIOs
gpio_config_t io_conf = {};
// Button: input with pull-up
io_conf.intr_type = GPIO_INTR_POSEDGE;
io_conf.pin_bit_mask = (1ULL << BUTTON_GPIO);
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = 1;
gpio_config(&io_conf);
// LED: output
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.pin_bit_mask = (1ULL << LED_GPIO);
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pull_up_en = 0;
io_conf.pull_down_en = 0;
gpio_config(&io_conf);
gpio_set_level(LED_GPIO, 0);
// Install button ISR service and handler
gpio_install_isr_service(0);
gpio_isr_handler_add(BUTTON_GPIO, button_handler, NULL);
// Initialize Bluetooth
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BTDM));
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
esp_err_t ret = esp_bt_controller_init(&bt_cfg);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "initialize controller failed: %s", esp_err_to_name(ret));
return;
}
ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "enable controller failed: %s", esp_err_to_name(ret));
return;
}
ret = esp_bluedroid_init();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "initialize bluedroid failed: %s", esp_err_to_name(ret));
return;
}
ret = esp_bluedroid_enable();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "enable bluedroid failed: %s", esp_err_to_name(ret));
return;
}
// Set device name
esp_bt_dev_set_device_name("ESP32_A2DP_Sink");
esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
// Register callbacks
esp_bt_gap_register_callback(bt_app_gap_cb);
esp_a2d_register_callback(bt_app_a2d_cb);
esp_a2d_sink_init();
esp_avrc_ct_init();
esp_avrc_ct_register_callback(bt_app_avrc_cb);
// Start bluetooth
ESP_LOGI(TAG, "A2DP Sink initialized. Waiting for connection...");
led_update();
// Main loop does nothing, everything handled by callbacks
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
static void i2s_init(void)
{
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_TX,
.sample_rate = I2S_SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE,
.channel_format = I2S_CHANNEL_FORMAT,
.communication_format = I2S_COMM_FORMAT,
.intr_alloc_flags = 0,
.dma_buf_count = 8,
.dma_buf_len = 64,
};
i2s_pin_config_t pin_config = {
.bck_io_num = I2S_BCLK_GPIO,
.ws_io_num = I2S_LRC_GPIO,
.data_out_num = I2S_DIN_GPIO,
.data_in_num = I2S_PIN_NO_CHANGE,
};
ESP_ERROR_CHECK(i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL));
ESP_ERROR_CHECK(i2s_set_pin(I2S_PORT, &pin_config));
i2s_zero_dma_buffer(I2S_PORT);
}
static void i2s_write_data(const uint8_t *data, size_t len)
{
size_t bytes_written;
esp_err_t ret = i2s_write(I2S_PORT, data, len, &bytes_written, portMAX_DELAY);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "I2S write error: %s", esp_err_to_name(ret));
}
}
static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
{
switch (event) {
case ESP_BT_GAP_AUTH_CMPL_EVT:
if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) {
ESP_LOGI(TAG, "Authentication succeeded: %s", param->auth_cmpl.device_name);
} else {
ESP_LOGE(TAG, "Authentication failed");
}
break;
default:
break;
}
}
static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
{
switch (event) {
case ESP_A2D_CONNECTION_STATE_EVT: {
esp_a2d_connection_state_t state = param->conn_stat.state;
if (state == ESP_A2D_CONNECTION_STATE_CONNECTED) {
ESP_LOGI(TAG, "A2DP connected");
a2dp_connected = true;
} else if (state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
ESP_LOGI(TAG, "A2DP disconnected");
a2dp_connected = false;
a2dp_playing = false;
}
led_update();
break;
}
case ESP_A2D_AUDIO_STATE_EVT: {
esp_a2d_audio_state_t audio_state = param->audio_stat.state;
if (audio_state == ESP_A2D_AUDIO_STATE_STARTED) {
ESP_LOGI(TAG, "Audio started");
a2dp_playing = true;
} else if (audio_state == ESP_A2D_AUDIO_STATE_STOPPED) {
ESP_LOGI(TAG, "Audio stopped");
a2dp_playing = false;
}
break;
}
case ESP_A2D_PROF_STATE_EVT: {
// Not used
break;
}
default:
break;
}
}
static void bt_app_avrc_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param)
{
switch (event) {
case ESP_AVRC_CT_CONNECTION_STATE_EVT:
break;
case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT:
break;
case ESP_AVRC_CT_META_RSP_EVT:
if (param->meta_rsp.attr_id == ESP_AVRC_MD_ATTR_PLAYBACK_STATE) {
// Handle playback state
}
break;
case ESP_AVRC_CT_CHANGE_NOTIFY_EVT:
if (param->change_ntfy.event_id == ESP_AVRC_CT_EVENT_PLAYBACK_STATUS_CHANGED) {
// Playback status changed
}
break;
default:
break;
}
}
// A2DP data callback is registered via esp_a2d_sink_register_data_callback()
// We need to provide data handler before enabling A2DP sink
// Usually called from within esp_a2d_sink_init() after setting callback.
// Let's define it as a simple function and register.
static void a2dp_data_callback(const uint8_t *data, uint32_t len)
{
if (len > 0) {
i2s_write_data(data, len);
}
}
// We need to register data callback after init, but before any connection.
// So we do it in app_main after a2d sink init.
// Actually esp_a2d_sink_register_data_callback() must be called before esp_a2d_sink_init()
// Change order: register callback before init.
// Let's adjust: in app_main, after esp_a2d_register_callback, call esp_a2d_sink_register_data_callback(a2dp_data_callback), then esp_a2d_sink_init().
// Update app_main accordingly.
// Also, we need to declare a2dp_data_callback before its use.
// Reordering: in app_main after esp_a2d_register_callback, add:
// esp_a2d_sink_register_data_callback(a2dp_data_callback);
// then esp_a2d_sink_init();
// This comment is for implementation; I will rewrite app_main with correct order.
// Button handler: simple toggle play/pause via AVRCP
static void button_handler(void *arg)
{
static TickType_t last_press = 0;
TickType_t now = xTaskGetTickCountFromISR();
if ((now - last_press) > pdMS_TO_TICKS(200)) {
last_press = now;
// Send play/pause passthrough command
esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_PRESSED);
// Release after short delay (handled by system, but we can send release immediately)
esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_RELEASED);
}
}
static void led_update(void)
{
if (a2dp_connected) {
gpio_set_level(LED_GPIO, 1); // solid on when connected
} else {
// Blink slowly
static bool led_state = false;
led_state = !led_state;
gpio_set_level(LED_GPIO, led_state ? 0 : 1); // toggle each call (call every second)
}
}
```
点击这里使用固件生成器
Please Log in or Create an account to join the conversation.
Time to create page: 0.220 seconds