מכשיר קשר ESP32 — לדבר בלי אינטרנט

קוד לקריאה ולהעתקה

מעלים את הקוד שנבדק

הקוד כאן לקריאה ולהעתקה בלבד — אי אפשר לשנות אותו בטעות.

walkie_talkie_3x.ino cpp
#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));
  }
}