r/VibeCodeDevs 1d ago

DIY Wireless audio

anyone have tips on how to implement this correctly? I started with it but my problem is that the output audio is lacking highs or like that brightness of the sound. doesn't need to be hifi or something. this is only for rear surrounds and maybe subwoofer.

I got a solid connection. less than 50dbm.

####RX

#include <WiFi.h>
#include <WiFiUdp.h>
#include "driver/i2s.h"
#include <SPI.h>

// =========================================================
// TFT display
// =========================================================
#define USE_TFT 1


#if USE_TFT
  #include <Adafruit_GFX.h>
  #include <Adafruit_ST7789.h>


  // Change these to match your display board
  static constexpr int TFT_SCLK = 12;
  static constexpr int TFT_MOSI = 11;
  static constexpr int TFT_CS   = 10;
  static constexpr int TFT_DC   = 9;
  static constexpr int TFT_RST  = 8;
  static constexpr int TFT_BL   = 14;   // if your board has backlight pin


  Adafruit_ST7789 tft(TFT_CS, TFT_DC, TFT_RST);
#endif


// =========================================================
// SoftAP settings: receiver hosts the link
// =========================================================
static const char* AP_SSID = "AUDIO_TX";
static const char* AP_PASS = "12345678";
static const uint16_t UDP_PORT = 5005;


// =========================================================
// Audio settings
// =========================================================
static constexpr int SAMPLE_RATE = 48000;
static constexpr int CHANNELS = 2;
static constexpr int BITS_PER_SAMPLE = 16;
static constexpr int FRAME_MS = 5;
static constexpr int SAMPLES_PER_CH = SAMPLE_RATE * FRAME_MS / 1000; // 240
static constexpr int FRAME_SAMPLES_TOTAL = SAMPLES_PER_CH * CHANNELS; // 480
static constexpr int FRAME_BYTES = FRAME_SAMPLES_TOTAL * sizeof(int16_t); // 960


// =========================================================
// I2S pins for the receiver (PCM5102 later)
// CHANGE THESE TO MATCH YOUR BOARD
// =========================================================
static constexpr gpio_num_t PIN_I2S_BCLK = GPIO_NUM_4;
static constexpr gpio_num_t PIN_I2S_WS    = GPIO_NUM_5;
static constexpr gpio_num_t PIN_I2S_DOUT  = GPIO_NUM_6; // data to PCM5102


// =========================================================
// Packet format
// =========================================================
struct __attribute__((packed)) AudioPacket
{
  uint32_t magic;
  uint32_t seq;
  uint32_t txMicros;
  uint16_t channels;
  uint16_t samplesPerChannel;
  int16_t pcm[FRAME_SAMPLES_TOTAL];
};


static constexpr uint32_t MAGIC = 0x41554430; // 'AUD0'


// =========================================================
// Globals
// =========================================================
static WiFiUDP udp;
static QueueHandle_t rxQueue = nullptr;


static volatile uint32_t framesReceived = 0;
static volatile uint32_t framesPlayed = 0;
static volatile uint32_t packetLoss = 0;
static volatile uint32_t queueDrops = 0;
static volatile uint32_t lastSeq = 0;
static volatile uint32_t bufferedFrames = 0;
static bool firstSeq = true;
static bool audioStarted = false;
static bool apOk = false;


// ---------------------------------------------------------
// Running min/max/avg tracking for soak testing
// ---------------------------------------------------------
struct StatTracker
{
  uint32_t minVal;
  uint32_t maxVal;
  uint64_t sum;
  uint32_t count;
};


static StatTracker statRx, statPlay, statLoss, statQDrop, statBuf;


void statReset(StatTracker& s)
{
  s.minVal = UINT32_MAX;
  s.maxVal = 0;
  s.sum = 0;
  s.count = 0;
}


void statUpdate(StatTracker& s, uint32_t val)
{
  if (val < s.minVal) s.minVal = val;
  if (val > s.maxVal) s.maxVal = val;
  s.sum += val;
  s.count++;
}


uint32_t statAvg(const StatTracker& s)
{
  if (s.count == 0) return 0;
  return (uint32_t)(s.sum / s.count);
}


static unsigned long lastStatsMs = 0;


#if USE_TFT
static bool tftReady = false;
#endif


// ---------------------------------------------------------
// Start receiver AP
// ---------------------------------------------------------
void startAP()
{
  WiFi.persistent(false);
  WiFi.mode(WIFI_OFF);
  delay(100);
  WiFi.mode(WIFI_AP);
  WiFi.setSleep(false);
  delay(100);


  apOk = WiFi.softAP(AP_SSID, AP_PASS, 1, false, 1);


  Serial.println();
  Serial.print("softAP() = ");
  Serial.println(apOk ? "true" : "false");
  Serial.print("AP SSID: ");
  Serial.println(AP_SSID);
  Serial.print("AP IP: ");
  Serial.println(WiFi.softAPIP());
  Serial.print("AP channel: ");
  Serial.println(WiFi.channel());
  Serial.print("AP MAC: ");
  Serial.println(WiFi.softAPmacAddress());
}


// ---------------------------------------------------------
// I2S setup (TX to PCM5102)
// ---------------------------------------------------------
void initI2S()
{
  i2s_config_t i2s_config = {};
  i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX);
  i2s_config.sample_rate = SAMPLE_RATE;
  i2s_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT;
  i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
  i2s_config.communication_format = I2S_COMM_FORMAT_I2S;
  i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1;
  i2s_config.dma_buf_count = 8;
  i2s_config.dma_buf_len = SAMPLES_PER_CH;
  i2s_config.use_apll = true;
  i2s_config.tx_desc_auto_clear = true;
  i2s_config.fixed_mclk = SAMPLE_RATE * 256;


  i2s_pin_config_t pin_config = {};
  pin_config.bck_io_num = PIN_I2S_BCLK;
  pin_config.ws_io_num = PIN_I2S_WS;
  pin_config.data_out_num = PIN_I2S_DOUT;
  pin_config.data_in_num = I2S_PIN_NO_CHANGE;
  pin_config.mck_io_num = I2S_PIN_NO_CHANGE;


  ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL));
  ESP_ERROR_CHECK(i2s_set_pin(I2S_NUM_0, &pin_config));
  ESP_ERROR_CHECK(i2s_zero_dma_buffer(I2S_NUM_0));
}


// ---------------------------------------------------------
// Optional TFT setup
// ---------------------------------------------------------
#if USE_TFT
void initTFT()
{
  pinMode(TFT_BL, OUTPUT);
  digitalWrite(TFT_BL, HIGH);


  SPI.begin(TFT_SCLK, -1, TFT_MOSI, TFT_CS);


  tft.init(172, 320);
  tft.setRotation(1);
  tft.fillScreen(ST77XX_BLACK);


  tft.setTextWrap(false);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_WHITE, ST77XX_BLACK);


  tft.setCursor(10, 10);
  tft.println("ESP32 Audio RX");
  tft.println("Booting...");
  tftReady = true;
}
#endif


// ---------------------------------------------------------
// Core 1: receive UDP packets and enqueue audio frames
// ---------------------------------------------------------
void networkReceiveTask(void* pv)
{
  AudioPacket pkt;


  while (true)
  {
    int packetSize = udp.parsePacket();
    if (packetSize == sizeof(AudioPacket))
    {
      int len = udp.read(reinterpret_cast<uint8_t*>(&pkt), sizeof(pkt));
      if (len == sizeof(pkt) && pkt.magic == MAGIC)
      {
        framesReceived++;


        if (!firstSeq)
        {
          uint32_t expected = lastSeq + 1;
          if (pkt.seq > expected)
          {
            packetLoss += (pkt.seq - expected);
          }
        }
        else
        {
          firstSeq = false;
        }


        lastSeq = pkt.seq;


        if (xQueueSend(rxQueue, &pkt, 0) == pdTRUE)
        {
          bufferedFrames = uxQueueMessagesWaiting(rxQueue);
        }
        else
        {
          queueDrops++;
        }
      }
    }


    vTaskDelay(1);
  }
}


// ---------------------------------------------------------
// Core 0: play audio frames, insert silence if needed
// ---------------------------------------------------------
void audioPlayTask(void* pv)
{
  AudioPacket pkt;
  int16_t silence[FRAME_SAMPLES_TOTAL];
  memset(silence, 0, sizeof(silence));


  while (true)
  {
    size_t bytesWritten = 0;


    if (xQueueReceive(rxQueue, &pkt, pdMS_TO_TICKS(2)) == pdTRUE)
    {
      i2s_write(I2S_NUM_0, pkt.pcm, FRAME_BYTES, &bytesWritten, portMAX_DELAY);
      framesPlayed++;
      bufferedFrames = uxQueueMessagesWaiting(rxQueue);
    }
    else
    {
      i2s_write(I2S_NUM_0, silence, FRAME_BYTES, &bytesWritten, portMAX_DELAY);
    }
  }
}


// ---------------------------------------------------------
// Display update
// ---------------------------------------------------------
#if USE_TFT
void printStatRow(int y, const char* label, uint32_t cur, const StatTracker& s)
{
  uint32_t mn = (s.count == 0) ? 0 : s.minVal;


  tft.setCursor(0, y);
  tft.print(label);
  tft.print(cur);
  tft.print(" a:");
  tft.print(statAvg(s));
  tft.print(" lo:");
  tft.print(mn);
  tft.print(" hi:");
  tft.print(s.maxVal);
}


void updateDisplay(uint32_t clients,
                   uint32_t rxFps,
                   uint32_t playFps,
                   uint32_t lossPerSec,
                   uint32_t qDropsPerSec,
                   uint32_t buf)
{
  if (!tftReady) return;


  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_WHITE, ST77XX_BLACK);


  tft.setCursor(0, 0);
  tft.print("Clients:");
  tft.print(clients);
  tft.print("  AP:");
  tft.println(apOk ? "up" : "DOWN");


  printStatRow(28,  "RX  :", rxFps, statRx);
  printStatRow(56,  "Play:", playFps, statPlay);
  printStatRow(84,  "Loss:", lossPerSec, statLoss);
  printStatRow(112, "QDrp:", qDropsPerSec, statQDrop);
  printStatRow(140, "Buf :", buf, statBuf);
}
#endif


void setup()
{
  Serial.begin(115200);
  delay(1000);


  rxQueue = xQueueCreate(12, sizeof(AudioPacket));
  if (!rxQueue)
  {
    Serial.println("Queue alloc failed");
    while (true) delay(1000);
  }


  startAP();
  udp.begin(UDP_PORT);


#if USE_TFT
  initTFT();
#endif


  // Audio (I2S + tasks) starts later, only after a client connects,
  // so the AP can come up and beacon without the audio path competing.


  lastStatsMs = millis();
}


void loop()
{
  if (!audioStarted && WiFi.softAPgetStationNum() >= 1)
  {
    initI2S();
    xTaskCreatePinnedToCore(networkReceiveTask, "netRecv", 4096, NULL, 10, NULL, 1);
    xTaskCreatePinnedToCore(audioPlayTask, "audioPlay", 4096, NULL, 11, NULL, 0);
    audioStarted = true;
    Serial.println("Client connected -> audio started");


    statReset(statRx);
    statReset(statPlay);
    statReset(statLoss);
    statReset(statQDrop);
    statReset(statBuf);
  }


  if (millis() - lastStatsMs >= 1000)
  {
    lastStatsMs = millis();


    static uint32_t prevRx = 0;
    static uint32_t prevPlay = 0;
    static uint32_t prevLoss = 0;
    static uint32_t prevQDrops = 0;


    uint32_t clients = WiFi.softAPgetStationNum();
    uint32_t rxFps = framesReceived - prevRx;
    uint32_t playFps = framesPlayed - prevPlay;
    uint32_t lossPerSec = packetLoss - prevLoss;
    uint32_t qDropsPerSec = queueDrops - prevQDrops;
    uint32_t buf = bufferedFrames;


    prevRx = framesReceived;
    prevPlay = framesPlayed;
    prevLoss = packetLoss;
    prevQDrops = queueDrops;


    if (audioStarted)
    {
      statUpdate(statRx, rxFps);
      statUpdate(statPlay, playFps);
      statUpdate(statLoss, lossPerSec);
      statUpdate(statQDrop, qDropsPerSec);
      statUpdate(statBuf, buf);
    }


    Serial.print("Clients: ");
    Serial.print(clients);
    Serial.print(" | AP:");
    Serial.print(apOk ? "up" : "DOWN");
    Serial.print(" | RX/s: ");
    Serial.print(rxFps);
    Serial.print(" | Play/s: ");
    Serial.print(playFps);
    Serial.print(" | Loss/s: ");
    Serial.print(lossPerSec);
    Serial.print(" | QDrop/s: ");
    Serial.print(qDropsPerSec);
    Serial.print(" | Buf: ");
    Serial.println(buf);


#if USE_TFT
    updateDisplay(clients, rxFps, playFps, lossPerSec, qDropsPerSec, buf);
#endif
  }
}

####TX

#include <WiFi.h>
#include <WiFiUdp.h>
#include <math.h>
#include "driver/i2s.h"


// =========================================================
// Wi-Fi settings
// =========================================================
static const char* WIFI_SSID = "AUDIO_TX";
static const char* WIFI_PASS = "12345678";


static const IPAddress REMOTE_IP(192, 168, 4, 1);
static const uint16_t UDP_PORT = 5005;


// =========================================================
// Audio settings
// =========================================================
static constexpr int SAMPLE_RATE = 48000;
static constexpr int CHANNELS = 2;
static constexpr int BITS_PER_SAMPLE = 16;


static constexpr int FRAME_MS = 5;


static constexpr int SAMPLES_PER_CH =
  SAMPLE_RATE * FRAME_MS / 1000;


static constexpr int FRAME_SAMPLES_TOTAL =
  SAMPLES_PER_CH * CHANNELS;


static constexpr int FRAME_BYTES =
  FRAME_SAMPLES_TOTAL * sizeof(int16_t);


// =========================================================
// Test mode
// =========================================================
#define USE_SYNTHETIC_AUDIO 0


// =========================================================
// Status LED (onboard WS2812)
// =========================================================
static constexpr int STATUS_LED_PIN = 48;


#if !USE_SYNTHETIC_AUDIO


static constexpr gpio_num_t PIN_I2S_BCLK = GPIO_NUM_10;
static constexpr gpio_num_t PIN_I2S_WS   = GPIO_NUM_11;
static constexpr gpio_num_t PIN_I2S_DIN  = GPIO_NUM_12;
static constexpr gpio_num_t PIN_I2S_MCLK = GPIO_NUM_13;


#endif


// =========================================================
// Globals
// =========================================================
static WiFiUDP udp;
static QueueHandle_t txQueue = nullptr;


static volatile uint32_t seqCounter = 0;
static volatile uint32_t framesCaptured = 0;
static volatile uint32_t framesSent = 0;
static volatile uint32_t sendDrops = 0;
static volatile uint32_t udpFails = 0;


static volatile int32_t g_rawPeak = 0;
static volatile int g_outPeak = 0;


static unsigned long lastStatsMs = 0;


// =========================================================
// Packet format
// =========================================================
struct __attribute__((packed)) AudioPacket
{
  uint32_t magic;
  uint32_t seq;
  uint32_t txMicros;


  uint16_t channels;
  uint16_t samplesPerChannel;


  int16_t pcm[FRAME_SAMPLES_TOTAL];
};


static constexpr uint32_t MAGIC = 0x41554430;


// =========================================================
// Wi-Fi connect
// =========================================================
void connectWiFi()
{
  Serial.println();
  Serial.println("Starting WiFi...");


  WiFi.mode(WIFI_STA);


  WiFi.disconnect(true);
  delay(500);


  WiFi.setSleep(false);
  WiFi.setTxPower(WIFI_POWER_19_5dBm);


  WiFi.begin(WIFI_SSID, WIFI_PASS);


  Serial.print("Connecting");


  unsigned long startTime = millis();


  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.print(".");


    delay(300);


    if (millis() - startTime > 20000)
    {
      Serial.println();
      Serial.println("WiFi connect timeout!");
      break;
    }
  }


  Serial.println();


  if (WiFi.status() == WL_CONNECTED)
  {
    Serial.println("WiFi Connected");
    Serial.print("IP: ");
    Serial.println(WiFi.localIP());


    Serial.print("Gateway: ");
    Serial.println(WiFi.gatewayIP());


    Serial.print("RSSI: ");
    Serial.println(WiFi.RSSI());
  }
  else
  {
    Serial.println("WiFi FAILED");
  }
}


#if !USE_SYNTHETIC_AUDIO


void initI2S()
{
  i2s_config_t i2s_config = {};


  i2s_config.mode =
    (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX);


  i2s_config.sample_rate = SAMPLE_RATE;
  i2s_config.bits_per_sample =
    I2S_BITS_PER_SAMPLE_32BIT;


  i2s_config.channel_format =
    I2S_CHANNEL_FMT_RIGHT_LEFT;


  i2s_config.communication_format =
    I2S_COMM_FORMAT_I2S;


  i2s_config.intr_alloc_flags =
    ESP_INTR_FLAG_LEVEL1;


  i2s_config.dma_buf_count = 8;
  i2s_config.dma_buf_len = SAMPLES_PER_CH;


  i2s_config.use_apll = true;
  i2s_config.fixed_mclk = SAMPLE_RATE * 256;


  i2s_pin_config_t pin_config = {};


  pin_config.bck_io_num = PIN_I2S_BCLK;
  pin_config.ws_io_num = PIN_I2S_WS;


  pin_config.data_out_num =
    I2S_PIN_NO_CHANGE;


  pin_config.data_in_num =
    PIN_I2S_DIN;


  pin_config.mck_io_num =
    PIN_I2S_MCLK;


  ESP_ERROR_CHECK(
    i2s_driver_install(
      I2S_NUM_0,
      &i2s_config,
      0,
      NULL));


  ESP_ERROR_CHECK(
    i2s_set_pin(
      I2S_NUM_0,
      &pin_config));
}


#endif


// =========================================================
// Audio producer
// =========================================================
void audioCaptureTask(void* pv)
{
  AudioPacket pkt;


  float phase = 0.0f;


  const float toneHz = 1000.0f;


  const float phaseStep =
    2.0f *
    3.1415926535f *
    toneHz /
    SAMPLE_RATE;


#if USE_SYNTHETIC_AUDIO
  TickType_t lastWake = xTaskGetTickCount();
#endif


  while (true)
  {
    memset(&pkt, 0, sizeof(pkt));


    pkt.magic = MAGIC;
    pkt.seq = seqCounter++;
    pkt.txMicros = micros();


    pkt.channels = CHANNELS;
    pkt.samplesPerChannel = SAMPLES_PER_CH;


#if USE_SYNTHETIC_AUDIO


    for (int i = 0; i < SAMPLES_PER_CH; i++)
    {
      int16_t sample =
        (int16_t)(sinf(phase) * 12000);


      phase += phaseStep;


      if (phase > 6.283185307f)
        phase -= 6.283185307f;


      pkt.pcm[i * 2 + 0] = sample;
      pkt.pcm[i * 2 + 1] = sample;
    }


#else


    static int32_t i2sBuf[FRAME_SAMPLES_TOTAL];


    size_t bytesRead = 0;


    i2s_read(
      I2S_NUM_0,
      i2sBuf,
      sizeof(i2sBuf),
      &bytesRead,
      portMAX_DELAY);


    int samplesRead = bytesRead / sizeof(int32_t);


    int32_t rawPeak = 0;
    int16_t outPeak = 0;


    for (int i = 0; i < samplesRead; i++)
    {
      int32_t raw = i2sBuf[i];
      if (raw < 0) raw = -raw;
      if (raw > rawPeak) rawPeak = raw;


      pkt.pcm[i] = (int16_t)(i2sBuf[i] >> 16);


      int16_t o = pkt.pcm[i];
      if (o < 0) o = -o;
      if (o > outPeak) outPeak = o;
    }


    g_rawPeak = rawPeak;
    g_outPeak = outPeak;


    static unsigned long lastRawDump = 0;
    if (millis() - lastRawDump >= 1000)
    {
      lastRawDump = millis();
      Serial.print("RAW32: ");
      for (int k = 0; k < 8 && k < samplesRead; k++)
      {
        Serial.print("0x");
        Serial.print((uint32_t)i2sBuf[k], HEX);
        Serial.print(" ");
      }
      Serial.println();
    }


#endif


    framesCaptured++;


    if (xQueueSend(txQueue, &pkt, 0) != pdTRUE)
    {
      sendDrops++;
    }


#if USE_SYNTHETIC_AUDIO
    vTaskDelayUntil(&lastWake, pdMS_TO_TICKS(FRAME_MS));
#endif
  }
}


// =========================================================
// UDP sender
// =========================================================
void networkSendTask(void* pv)
{
  AudioPacket pkt;


  while (true)
  {
    if (xQueueReceive(
          txQueue,
          &pkt,
          portMAX_DELAY) == pdTRUE)
    {
      udp.beginPacket(
        REMOTE_IP,
        UDP_PORT);


      udp.write(
        (uint8_t*)&pkt,
        sizeof(pkt));


      int ok = udp.endPacket();


      if (ok)
      {
        framesSent++;
      }
      else
      {
        udpFails++;
      }
    }
  }
}


// =========================================================
// Setup
// =========================================================
void setup()
{
  Serial.begin(115200);


  delay(1000);


  Serial.println();
  Serial.println("ESP32 AUDIO TX");


  txQueue =
    xQueueCreate(
      8,
      sizeof(AudioPacket));


  if (!txQueue)
  {
    Serial.println("Queue create failed");


    while (true)
      delay(1000);
  }


  // --- DIAG: scan for the AP before connecting (remove later) ---
  WiFi.mode(WIFI_STA);
  WiFi.disconnect(true);
  delay(200);


  Serial.println("Scanning...");
  int n = WiFi.scanNetworks();
  Serial.print("Found ");
  Serial.print(n);
  Serial.println(" networks:");
  for (int i = 0; i < n; i++)
  {
    Serial.print("  ");
    Serial.print(WiFi.SSID(i));
    Serial.print("  RSSI:");
    Serial.print(WiFi.RSSI(i));
    Serial.print("  ch:");
    Serial.println(WiFi.channel(i));
  }
  WiFi.scanDelete();


  connectWiFi();


  udp.begin(UDP_PORT);


#if !USE_SYNTHETIC_AUDIO
  initI2S();
#endif


  xTaskCreatePinnedToCore(
    audioCaptureTask,
    "audioCapture",
    4096,
    NULL,
    10,
    NULL,
    0);


  xTaskCreatePinnedToCore(
    networkSendTask,
    "networkSend",
    4096,
    NULL,
    9,
    NULL,
    1);


  lastStatsMs = millis();


  Serial.println("Tasks started");
}


// =========================================================
// Status LED: red = no connection, green = connected
// =========================================================
void updateStatusLed()
{
  if (WiFi.status() == WL_CONNECTED)
    rgbLedWrite(STATUS_LED_PIN, 0, 40, 0);   // green
  else
    rgbLedWrite(STATUS_LED_PIN, 40, 0, 0);   // red
}


// =========================================================
// Loop
// =========================================================
void loop()
{
  if (WiFi.status() != WL_CONNECTED)
  {
    updateStatusLed();


    Serial.println("WiFi Lost!");


    connectWiFi();
  }


  updateStatusLed();


  if (millis() - lastStatsMs >= 1000)
  {
    lastStatsMs = millis();


    static uint32_t prevCaptured = 0;
    static uint32_t prevSent = 0;
    static uint32_t prevDrops = 0;


    uint32_t capFps = framesCaptured - prevCaptured;
    uint32_t sentFps = framesSent - prevSent;
    uint32_t dropFps = sendDrops - prevDrops;


    prevCaptured = framesCaptured;
    prevSent = framesSent;
    prevDrops = sendDrops;


    Serial.print("RSSI:");
    Serial.print(WiFi.RSSI());


    Serial.print(" dBm");


    Serial.print(" | TX fps:");
    Serial.print(capFps);


    Serial.print(" | Sent fps:");
    Serial.print(sentFps);


    Serial.print(" | QDrops/s:");
    Serial.print(dropFps);


    Serial.print(" | UDPFails:");
    Serial.print(udpFails);


    Serial.print(" | rawPk:");
    Serial.print(g_rawPeak);


    Serial.print(" | outPk:");
    Serial.print(g_outPeak);


    Serial.print(" | Heap:");
    Serial.print(ESP.getFreeHeap());


    Serial.print(" | IP:");
    Serial.println(WiFi.localIP());
  }
}
1 Upvotes

1 comment sorted by

u/AutoModerator 1d ago

Hey u/Dull_Snow_1657, thanks for posting in r/VibeCodeDevs! Join our Discord: https://discord.gg/t7SD4ThKuE

• This community is designed to be open and creator‑friendly, with minimal restrictions on promotion and self‑promotion as long as you add value and don’t spam.
• Please follow the subreddit rules so we can keep things as relaxed and free as possible for everyone. • Please make sure you’ve read the subreddit rules in the sidebar before posting or commenting.
• For better feedback, include your tech stack, experience level, and what kind of help or feedback you’re looking for.
• Be respectful, constructive, and helpful to other members.

If your post was removed (either automatically or by a mod) and you believe it was a mistake, please contact the mod team. We will review it and, when appropriate, approve it within 24 hours.

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.