מכשיר קשר ESP32 — לדבר בלי אינטרנט
קוד לקריאה ולהעתקה
מעלים את הקוד שנבדק
הקוד כאן לקריאה ולהעתקה בלבד — אי אפשר לשנות אותו בטעות.
#include <Arduino.h>
#include <ESP_I2S.h>
#include <WiFi.h>
#include <esp_now.h>
#include <esp_wifi.h>
// החיווט חייב להתאים למדריך.
constexpr int MIC_SCK = 25;
constexpr int MIC_WS = 33;
constexpr int MIC_SD = 32;
constexpr int AMP_BCLK = 26;
constexpr int AMP_LRC = 27;
constexpr int AMP_DIN = 14;
constexpr int PTT_PIN = 4;
constexpr uint32_t SAMPLE_RATE = 16000;
constexpr uint8_t ESPNOW_CHANNEL = 6;
constexpr size_t FRAME_SAMPLES = 100; // 200 bytes — safely below an ESP-NOW packet.
struct AudioPacket {
int16_t samples[FRAME_SAMPLES];
};
I2SClass Microphone(I2S_NUM_0);
I2SClass Speaker(I2S_NUM_1);
QueueHandle_t receivedAudio;
volatile bool radioReady = true;
const uint8_t broadcastAddress[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
void onReceive(const esp_now_recv_info_t *, const uint8_t *data, int length) {
if (length != static_cast<int>(sizeof(AudioPacket))) return;
AudioPacket packet;
memcpy(&packet, data, sizeof(packet));
xQueueSend(receivedAudio, &packet, 0);
}
void onSent(const esp_now_send_info_t *, esp_now_send_status_t) {
radioReady = true;
}
bool beginRadio() {
WiFi.mode(WIFI_STA);
WiFi.disconnect();
if (esp_wifi_set_channel(ESPNOW_CHANNEL, WIFI_SECOND_CHAN_NONE) != ESP_OK) return false;
if (esp_now_init() != ESP_OK) return false;
if (esp_now_register_recv_cb(onReceive) != ESP_OK) return false;
if (esp_now_register_send_cb(onSent) != ESP_OK) return false;
esp_now_peer_info_t peer = {};
memcpy(peer.peer_addr, broadcastAddress, sizeof(broadcastAddress));
peer.channel = ESPNOW_CHANNEL;
peer.ifidx = WIFI_IF_STA;
peer.encrypt = false;
return esp_now_add_peer(&peer) == ESP_OK;
}
bool beginAudio() {
Microphone.setPins(MIC_SCK, MIC_WS, -1, MIC_SD);
Speaker.setPins(AMP_BCLK, AMP_LRC, AMP_DIN);
const bool micReady =
Microphone.begin(I2S_MODE_STD, SAMPLE_RATE, I2S_DATA_BIT_WIDTH_32BIT,
I2S_SLOT_MODE_MONO, I2S_STD_SLOT_LEFT);
const bool speakerReady =
Speaker.begin(I2S_MODE_STD, SAMPLE_RATE, I2S_DATA_BIT_WIDTH_16BIT,
I2S_SLOT_MODE_MONO, I2S_STD_SLOT_BOTH);
return micReady && speakerReady;
}
void setup() {
Serial.begin(115200);
pinMode(PTT_PIN, INPUT_PULLUP);
receivedAudio = xQueueCreate(4, sizeof(AudioPacket));
if (!receivedAudio || !beginAudio() || !beginRadio()) {
Serial.println("SETUP ERROR — check the wiring and restart");
while (true) delay(1000);
}
Serial.println("READY — hold PTT to talk");
}
void loop() {
if (digitalRead(PTT_PIN) == LOW) {
int32_t raw[FRAME_SAMPLES];
AudioPacket packet = {};
const size_t bytes = Microphone.readBytes(
reinterpret_cast<char *>(raw), sizeof(raw));
const size_t samples = min(bytes / sizeof(raw[0]), FRAME_SAMPLES);
for (size_t i = 0; i < samples; ++i) {
const int32_t scaled = raw[i] >> 13;
packet.samples[i] =
static_cast<int16_t>(constrain(scaled, -32768L, 32767L));
}
if (radioReady) {
radioReady = false;
if (esp_now_send(broadcastAddress,
reinterpret_cast<const uint8_t *>(&packet),
sizeof(packet)) != ESP_OK) {
radioReady = true;
}
}
return;
}
AudioPacket packet;
if (xQueueReceive(receivedAudio, &packet, pdMS_TO_TICKS(5)) == pdTRUE) {
Speaker.write(packet.samples, sizeof(packet.samples));
}
}