ESP32_BT Firmware
載入中...
搜尋中...
無符合項目
main.cpp 檔案參考文件

ESP32 藍牙錄音(使用 SPP + I2S) 更多...

#include "BluetoothSerial.h"
#include <driver/i2s.h>

查看本檔案的原始碼.

函式

int16_t compress_log12 (int16_t sample)
 μ-law 壓縮的自訂 12-bit 版本
uint8_t compress_log4 (int16_t sample)
 將 16-bit PCM 壓到 4-bit(0~15),作為輔助通道
void i2sInit ()
 初始化 I2S
void i2s_adc (void *arg)
 I2S 讀取與壓縮傳輸的背景任務
void i2sTask (void *pvParameters)
 I2S 主任務(負責啟動與釋放 I2S 驅動)
void startRecording ()
 開始錄音的控制接口
void finishRecording ()
 停止錄音的控制接口
void resetFlags ()
 重置狀態旗標(連線中斷時呼叫)
void btCallback (esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
 Bluetooth SPP 事件callback.
void initBT (const char *name="Recorder")
 啟動藍牙並註冊 callback
void setup ()
 系統初始化

詳細描述

ESP32 藍牙錄音(使用 SPP + I2S)

此程式負責透過經典藍牙(SPP)接收控制指令, 並啟動 / 停止 I2S 音訊擷取任務。

  • 裝置名稱:Recorder
  • 指令:
    • START → 啟動錄音
    • STOP → 停止錄音
  • 音訊格式:16kHz、16bit × 2 聲道(32bit)

定義在 main.cpp 檔.

函式說明文件

◆ compress_log12()

int16_t compress_log12 ( int16_t sample)
inline

μ-law 壓縮的自訂 12-bit 版本

基於 μ-law 曲線 (μ=255) 對 16-bit PCM 進行非線性壓縮, 將輸出範圍限制在 -2047~2047(約 12-bit)。 用於降低傳輸資料量但保留主要動態範圍。

定義在 main.cpp 檔案之第 43 行.

43 {
44 float x = (float)sample / 32768.0f;
45
46 float y = copysignf(log1pf(MU * fabsf(x)) / log1pf(MU), x);
47
48 int log12 = (int)(y * 2047.0f);
49
50 if (log12 > 2047) log12 = 2047;
51 if (log12 < -2047) log12 = -2047;
52
53 return (int16_t)log12;
54}

◆ compress_log4()

uint8_t compress_log4 ( int16_t sample)
inline

將 16-bit PCM 壓到 4-bit(0~15),作為輔助通道

定義在 main.cpp 檔案之第 59 行.

59 {
60 float x = fabsf((float)sample) / 32768.0f;
61
62 float y = log1pf(MU * x) / log1pf(MU);
63
64 int log4 = (int)(y * 15.0f + 0.5f);
65 if (log4 > 15) log4 = 15;
66
67 return (uint8_t)log4;
68}

◆ i2sInit()

void i2sInit ( )

初始化 I2S

  • channel_format = RIGHT_LEFT(L/R 交錯)
  • bits_per_sample = 32(承載 L/R 各 16-bit)

定義在 main.cpp 檔案之第 76 行.

76 {
77 i2s_config_t i2s_config = {
78 .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
79 .sample_rate = I2S_SAMPLE_RATE,
80 .bits_per_sample = i2s_bits_per_sample_t(I2S_SAMPLE_BITS),
81 .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
82 .communication_format = I2S_COMM_FORMAT_STAND_I2S,
83 .intr_alloc_flags = 0,
84 .dma_buf_count = 8,
85 .dma_buf_len = 64,
86 .use_apll = 1
87 };
88
89 i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
90
91 const i2s_pin_config_t pin_config = {
92 .bck_io_num = I2S_SCK,
93 .ws_io_num = I2S_WS,
94 .data_out_num = -1,
95 .data_in_num = I2S_IN
96 };
97
98 i2s_set_pin(I2S_PORT, &pin_config);
99}

◆ i2s_adc()

void i2s_adc ( void * arg)

I2S 讀取與壓縮傳輸的背景任務

  • 以 32-bit word 讀回:兩個 word 構成一個 (L,R) 框
  • 取出 L/R 的高 16 位後,分別做 12-bit 與 4-bit 的壓縮封包
  • 經 SPP 送出

定義在 main.cpp 檔案之第 108 行.

108 {
109 size_t bytes_read;
110
111 while (1) {
112 i2s_read(I2S_PORT, (void*) i2s_read_buff, I2S_READ_LEN, &bytes_read, portMAX_DELAY);
113
114 if (isRecording) {
115 int32_t* buffer32 = (int32_t*)i2s_read_buff;
116 int samples = bytes_read / sizeof(int32_t);
117
118 int j = 0;
119 for (int i = 0; i < samples; i+=2) {
120 int16_t left = buffer32[i] >> 16;
121 int16_t right = buffer32[i + 1] >> 16;
122
123 int16_t left12 = compress_log12(left);
124 uint8_t right4 = compress_log4(right);
125
126 flash_write_buff[j++] = left12 & 0xFF;
127 flash_write_buff[j++] = ((left12 >> 8) & 0x0F) | (right4 << 4);
128 }
129
130 SerialBT.write((const byte*) flash_write_buff, j);
131 } else {
132 vTaskDelay(10 / portTICK_PERIOD_MS);
133 }
134 }
135
136 vTaskDelete(NULL);
137}
uint8_t compress_log4(int16_t sample)
將 16-bit PCM 壓到 4-bit(0~15),作為輔助通道
Definition main.cpp:59
int16_t compress_log12(int16_t sample)
μ-law 壓縮的自訂 12-bit 版本
Definition main.cpp:43

◆ i2sTask()

void i2sTask ( void * pvParameters)

I2S 主任務(負責啟動與釋放 I2S 驅動)

定義在 main.cpp 檔案之第 140 行.

140 {
141 while (true) {
142 if (i2sActive) {
143 Serial.println("I2S Initialized!");
144 i2sInit();
145 xTaskCreate(i2s_adc, "i2s_adc", 2048, NULL, 1, &taskHandler);
146 while (i2sActive) {
147 vTaskDelay(100 / portTICK_PERIOD_MS);
148 }
149 Serial.println("I2S released!");
150 i2s_driver_uninstall(I2S_PORT);
151 vTaskDelete(taskHandler);
152 } else {
153 vTaskDelay(100 / portTICK_PERIOD_MS);
154 }
155 }
156}
void i2sInit()
初始化 I2S
Definition main.cpp:76
void i2s_adc(void *arg)
I2S 讀取與壓縮傳輸的背景任務
Definition main.cpp:108

◆ startRecording()

void startRecording ( )

開始錄音的控制接口

定義在 main.cpp 檔案之第 159 行.

159 {
160 Serial.println(" *** Recording Start ***");
161 digitalWrite(LED, HIGH);
162 isRecording = true;
163}

◆ finishRecording()

void finishRecording ( )

停止錄音的控制接口

定義在 main.cpp 檔案之第 166 行.

166 {
167 if (!isRecording) return;
168
169 Serial.println(" *** Recording End ***");
170 digitalWrite(LED, LOW);
171 isRecording = false;
172}

◆ resetFlags()

void resetFlags ( )

重置狀態旗標(連線中斷時呼叫)

定義在 main.cpp 檔案之第 175 行.

175 {
176 isRecording = false;
177 i2sActive = false;
178}

◆ btCallback()

void btCallback ( esp_spp_cb_event_t event,
esp_spp_cb_param_t * param )

Bluetooth SPP 事件callback.

  • OPEN:切為 non-discoverable、允許 I2S 任務
  • DATA:解析 START/STOP 指令,控制錄音狀態
  • CLOSE:恢復 discoverable、重置旗標

定義在 main.cpp 檔案之第 187 行.

187 {
188 if (event == ESP_SPP_SRV_OPEN_EVT) {
189 Serial.println("Client Connected!");
190 esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_NON_DISCOVERABLE);
191 i2sActive = true;
192 }
193 else if (event == ESP_SPP_DATA_IND_EVT) {
194 Serial.printf("ESP_SPP_DATA_IND_EVT len=%d handle=%d\n", param->data_ind.len, param->data_ind.handle);
195
196 if (param->data_ind.len > 0) {
197 int dataLen = param->data_ind.len;
198 char textArray[dataLen + 1];
199 strncpy(textArray, (const char*)param->data_ind.data, dataLen);
200 textArray[dataLen] = '\0';
201 String textString = textArray;
202 textString.trim();
203
204 Serial.printf("*** Text String: %s\n", textString.c_str());
205
206 if (textString.equals("START")) {
208 }
209 else if (textString.equals("STOP")) {
211 }
212 }
213 }
214 else if (event == ESP_SPP_CLOSE_EVT){
215 Serial.println("Client Disconnected!");
216 esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
217 resetFlags();
218 }
219}
void finishRecording()
停止錄音的控制接口
Definition main.cpp:166
void resetFlags()
重置狀態旗標(連線中斷時呼叫)
Definition main.cpp:175
void startRecording()
開始錄音的控制接口
Definition main.cpp:159

◆ initBT()

void initBT ( const char * name = "Recorder")

啟動藍牙並註冊 callback

參數
name藍牙裝置名稱(預設為 "Recorder")

定義在 main.cpp 檔案之第 224 行.

224 {
225 if (!SerialBT.begin(name)) {
226 Serial.println("An error occurred initializing Bluetooth");
227 ESP.restart();
228 return;
229 }
230
231 Serial.printf("Bluetooth Initialized (%s)\n", name);
232 SerialBT.register_callback(btCallback);
233 Serial.println("======================================================");
234 Serial.printf("Device '%s' started, now you can pair it with Bluetooth\n", name);
235 Serial.println("======================================================");
236}
void btCallback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
Bluetooth SPP 事件callback.
Definition main.cpp:187

◆ setup()

void setup ( )

系統初始化

定義在 main.cpp 檔案之第 239 行.

239 {
240 Serial.begin(115200);
241 pinMode(LED, OUTPUT);
242 digitalWrite(LED, LOW);
243
244 i2s_read_buff = (char*) calloc(I2S_READ_LEN, sizeof(char));
245 flash_write_buff = (uint8_t*) calloc(I2S_READ_LEN, sizeof(char));
246
247 initBT("Recorder"); // 想改藍牙名稱可以改這裡
248 xTaskCreate(i2sTask, "I2S Task", 4096, NULL, 1, NULL);
249}
void i2sTask(void *pvParameters)
I2S 主任務(負責啟動與釋放 I2S 驅動)
Definition main.cpp:140
void initBT(const char *name="Recorder")
啟動藍牙並註冊 callback
Definition main.cpp:224