如何在两个 ESP32 MCU 之间使用 ESP Now 发送音频数据包?

我将麦克风和扬声器连接到 2 个 ESP32,并且能够使用 ESP32 从麦克风听到扬声器再现的我的声音。我也能够在他们之间发送音频信号,但我无法理解口语。我认为这是因为我正在从analogRead 函数发送当前值,并且由于 ESP Now 链接的数据速率低于 adc 采样率,因此传输中会丢失很多信息。

我尝试使用下面的代码创建一个包含 1000 个值的数组并一次发送一个数组,但它对我不起作用。感谢您的帮助,我需要在 2 个 ESP32 之间发送更高质量的音频。

#include <esp_now.h>
#include <WiFi.h>

// REPLACE WITH THE MAC Address of your receiver
uint8_t broadcastAddress[] = {0x7C,0x9E,0xBD,0x47,0x92,0x4C}; //MAC Address

float A;
const int Max = 1000;

//Define Sent Message
typedef struct test_message {
  float A[Max];
} test_message;

test_message tx; //Sent Message
test_message rx; // Received Message

// Transmitting Callback
void OnDataSent(const uint8_t *mac_addr,esp_now_send_status_t status) {
  char macStr[18];
  // Copies the sender mac address to a string
  snprintf(macStr,sizeof(macStr),"%02x:%02x:%02x:%02x:%02x:%02x:",mac_addr[0],mac_addr[1],mac_addr[2],mac_addr[3],mac_addr[4],mac_addr[5]);
}

// Receiving Callback
void OnDataRecv(const uint8_t * mac,const uint8_t *incomingData,int len) {
  memcpy(&rx,incomingData,sizeof(rx));
}

void setup() {
  Serial.begin(115200);
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);
  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
   // Serial.println("Error initializing ESP-NOW");
    return;
  esp_now_register_recv_cb(OnDataRecv);
  }
  // Once ESPNow is successfully Init,we will register for Send CB to
  // get the status of Trasnmitted packet
  esp_now_register_send_cb(OnDataSent);
  
  // Register peer
  esp_now_peer_info_t peerInfo;
  memcpy(peerInfo.peer_addr,broadcastAddress,6);
  peerInfo.channel = 0;
  peerInfo.encrypt = false;
  
  // Add peer        
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
   // Serial.println("Failed to add peer");
    return;
  }
  // Register for a callback function that will be called when data is received
  esp_now_register_recv_cb(OnDataRecv);
}

void loop() {
  for (int i=0; i<Max; i++){
  tx.A[i] = analogRead(35);
  }
  esp_err_t result = esp_now_send(0,(uint8_t *) &tx,sizeof(test_message));
  for (int i=0; i<Max; i++){
  dacWrite(25,rx.A[i]);
  Serial.println(rx.A[i]);
  }
}
hustkazz 回答:如何在两个 ESP32 MCU 之间使用 ESP Now 发送音频数据包?

有很多问题。


输入的第一阶段存储垃圾值

OnDataRecv 中,您使用 memcpyuint8_t 数组复制到 float 数组。那行不通。这是UB(未定义的行为)和糟糕“类型双关语”。

这只是将“垃圾”值放入输出缓冲区。

抛弃float [到处] 以支持uint8_t。您的 ADC [可能] 只有 8 位,因此执行 float 无济于事,而且实际上不太准确,因为并非所有 8 位值都有积分 {{ 1}} 个值。


第一阶段输入盲目复制固定数量的数据

float 中,您忽略了 OnDataRecv 的值并盲目复制了 1000 个 [len] 样本。您正在将垃圾值附加到输出缓冲区。

您需要兑现部分转账。例如,在连续调用 float 时,OnDataRecv 参数可能会有所不同。例如,一次调用可能是 50,下一次调用可能是 23,等等。

您可能需要一个环形队列来累积数据。


发送时...

当您将输出缓冲区发送到远程系统时,您[盲目地] 发送了 1000 个样本。

同样,您必须兑现部分转账。

您的 ADC [可能] 的采样率为 48K [8 位采样](例如)。但是,您的串行端口 [以 110,000 波特] 只能发送约 11,000 字节/秒。

因此,您需要下采样从 48,000 字节到 10,000 字节左右。

一个简单的方法是每四个字节发送一次。或者,您可以取每个原始四字节序列的平均值。


可能需要进行一些压缩...

您可以先将连续样本的差异复制到另一个缓冲区。使用差异可能会使生成的缓冲区更具压缩性。

然后,您可以使用 LZW 压缩来压缩差异缓冲区。或者,使用算术压缩方案,如自适应霍夫曼或 CABAC,或其他一些自适应 [标准] 音频压缩方案。

此外,人耳具有对数响应。所以,另一种选择是做 len 文件可以做的 [以及电话公司过去做的 ;-)] 并发送 8 位 mulaw 或 A-law 样本


您对发送方所做的任何事情都必须在接收方进行逆转

因此,由于您可能想要发送可变长度的数据包,因此您需要在描述音频数据长度的数据之前发送一个 .wav,以及用于给定数据包的任何压缩方案.

我还会添加一些时间戳,以便接收器可以输出精确的样本数以保持精确的输出频率。

而且,我可能会添加一个 CRC,因此接收器可以拒绝/忽略任何损坏的样本(用静音替换它们)。如果将压缩数据作为损坏发送可能会导致灾难性后果,这一点尤为重要。

即使数据丢失/损坏,时间戳也可以让接收器保持同步。


更新:

另一个问题是 ESP-NOW 只允许 250 字节作为有效载荷,因此发送 1000 位以外的任何内容都会被截断。将发送 MAC 地址复制到传输回调中的局部变量然后返回的意义何在? – 罗姆基

两个优点都让我思考......

使用 struct 发送 [EOS 终止] string 数据。它适合发送二进制数据。

要发送二进制数据,我们想使用[循环外]:

Serial.println

其中 Serial.write(&rx.A[0],Alen); 是缓冲区中实际的字节数。 [每个罗姆基],我们可能需要将其上限设为 250。

最好的方法是利用 Alen 的返回值并循环部分传输:

write
本文链接:https://www.f2er.com/257.html

大家都在问