<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[[HELP] Audio From M5Stack Atom Echo Appears Sped Up &#x2F; Choppy &#x2F; Low Quality]]></title><description><![CDATA[<p dir="auto">Hi everyone,</p>
<p dir="auto">I'm working on a project using the <strong>M5Stack Atom Echo</strong> to stream audio over Wi-Fi to a Python socket server and save it as a <code>.wav</code> file. However, I’ve been consistently running into the following audio quality issues:</p>
<ul>
<li>Audio sounds <strong>sped up</strong> or <strong>accelerated</strong></li>
<li>Audio <strong>degrades with repeated runs</strong>, especially compared to the quality after a fresh flash of the device</li>
<li><strong>Low fidelity</strong> or <strong>choppy output</strong></li>
<li>Sometimes <strong>segments of the audio are missing</strong> intermittently and unpredictably</li>
</ul>
<hr />
<h3>What I’ve Tried So Far</h3>
<p dir="auto">To rule out common causes, I’ve implemented the following:</p>
<ul>
<li>Added a <strong>magic header (0xAA55)</strong> for packet boundary detection</li>
<li>Added a <strong>CRC32 checksum</strong> to verify packet integrity</li>
<li>Tracked <strong>packet sequence numbers</strong> to detect out-of-order or lost packets</li>
<li>Tuned <strong>record size to 120 samples</strong> per packet to avoid fragmentation</li>
<li>Increased <strong>DMA buffer length and count</strong> to prevent underruns</li>
<li>Verified <strong>no packet loss</strong> over Wi-Fi by checking sequence and CRC</li>
<li>Used <strong>sample rate of 17000 Hz</strong> and 16-bit mono audio</li>
</ul>
<p dir="auto">Despite these efforts, the audio still exhibits the above problems.</p>
<hr />
<h3>My Setup</h3>
<h4>Arduino (M5Stack Atom Echo)</h4>
<ul>
<li>Records mic input at 17000 Hz, 16-bit mono</li>
<li>Sends audio in chunks of 120 samples per packet (with sequence, magic, and CRC)</li>
<li>Uses a large circular buffer to avoid data loss during recording</li>
</ul>
<h4>Python Socket Server</h4>
<ul>
<li>Receives packets with proper buffering</li>
<li>Validates magic, sequence, and CRC</li>
<li>Appends valid audio frames and writes to a <code>.wav</code> file</li>
</ul>
<hr />
<h3>Sample Code</h3>
<h4>Arduino Code (M5Stack Atom Echo)</h4>
<pre><code class="language-cpp">#include &lt;M5Unified.h&gt;
#include &lt;WiFi.h&gt;
#include &lt;CRC32.h&gt;

const char* ssid = "xxx";
const char* password = "xxx";
const char* server_ip = "xx.xx.xx.xx";
const uint16_t server_port = 5000;

WiFiClient client;
CRC32 crc;

// Reduce packet size to avoid WiFi fragmentation (MTU &lt; 1400 bytes)
static constexpr size_t record_length = 120;  // Reduced from 240
static constexpr size_t record_number = 256;  // Larger buffer
static constexpr size_t record_samplerate = 17000;
static constexpr size_t record_size = record_number * record_length;

static size_t rec_record_idx = 0;
static uint32_t packet_seq = 0;
int16_t* rec_data;
bool is_connected = false;

// Packet with header (magic + seq + crc)
struct AudioPacket {
  uint16_t magic;  // 0xAA55 for boundary detection
  uint32_t seq;
  uint32_t crc;
  int16_t data[record_length];
} __attribute__((packed));

void setup() {
  M5.begin();
  Serial.begin(115200);
  M5.Speaker.end();

  // Configure mic for 16-bit mono 17000Hz
  auto mic_cfg = M5.Mic.config();
  mic_cfg.sample_rate = 17000;
  mic_cfg.stereo = true;  // Force mono
  mic_cfg.dma_buf_len = record_number;  // Larger DMA buffer to avoid underflow
  mic_cfg.dma_buf_count = 8;
  M5.Mic.config(mic_cfg);  // Apply config
  M5.Mic.begin();  // Initialize mic with default config

  // Allocate buffer
  rec_data = (int16_t*)heap_caps_malloc(record_size * sizeof(int16_t), MALLOC_CAP_8BIT);
  memset(rec_data, 0, record_size * sizeof(int16_t));

  // Wi-Fi connection with retry
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWi-Fi connected");
}

void loop() {
  // Reconnect if disconnected
  if (!client.connected()) {
    is_connected = client.connect(server_ip, server_port);
    if (is_connected) {
      Serial.println("Server connected");
      packet_seq = 0;  // Reset sequence on new connection
    } else {
      delay(1000);
      return;
    }
  }

  if (client.connected() &amp;&amp; M5.Mic.isEnabled()) {
    AudioPacket packet;
    auto data = &amp;rec_data[rec_record_idx * record_length];
    
    if (M5.Mic.record(data, record_length, record_samplerate)) {
      // Fill packet with magic number and reset CRC
      packet.magic = 0xAA55;
      packet.seq = packet_seq++;
      crc.reset();  // Critical: reset CRC for each packet
      
      memcpy(packet.data, data, record_length * sizeof(int16_t));
      packet.crc = crc.calculate((uint8_t*)&amp;packet.data, record_length * sizeof(int16_t));
      
      client.write((uint8_t*)&amp;packet, sizeof(AudioPacket));
      delay(10);
    
      if (++rec_record_idx &gt;= record_number) {
        memset(rec_data, 0, record_size * sizeof(int16_t));
        rec_record_idx = 0;
      }
    }
  }
}



</code></pre>
<h4>Python Socket Server</h4>
<pre><code class="language-python">import socket
import wave
import struct
import zlib
import numpy as np
import sounddevice as sd

HOST = '0.0.0.0'
PORT = 5000
OUTPUT_FILE = 'recorded_audio.wav'

SAMPLE_RATE = 17000
CHANNELS = 1
SAMPLE_WIDTH = 2
RECORD_LENGTH = 120  # Must match Arduino
PACKET_SIZE = 2 + 4 + 4 + (RECORD_LENGTH * 2)  # magic(2) + seq(4) + crc(4) + data

frames = []
last_seq = -1
buffer = b''

try:
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.bind((HOST, PORT))
        sock.listen(1)
        conn, addr = sock.accept()
        with conn:
            print(f"Connected by {addr}")
            while True:
                # Handle fragmented data by buffering
                buffer += conn.recv(1024)
                while len(buffer) &gt;= PACKET_SIZE:
                    # Extract complete packet
                    packet = buffer[:PACKET_SIZE]
                    buffer = buffer[PACKET_SIZE:]
                    
                    # Verify magic number
                    magic, seq, crc_received = struct.unpack('&lt;HII', packet[:10])
                    if magic != 0xAA55:
                        print("Invalid packet magic number")
                        continue
                    
                    # Verify sequence
                    if seq != last_seq + 1:
                        print(f"Sequence error: expected {last_seq + 1}, got {seq}")
                    last_seq = seq
                    
                    # Verify CRC
                    audio_data = packet[10:]
                    crc_calculated = zlib.crc32(audio_data) &amp; 0xFFFFFFFF
                    if crc_received != crc_calculated:
                        print(f"CRC error: received {crc_received}, calculated {crc_calculated}")
                        continue
                    
                    frames.append(audio_data)
except:
    # Save WAV
    with wave.open(OUTPUT_FILE, 'wb') as wf:
        wf.setnchannels(CHANNELS)
        wf.setsampwidth(SAMPLE_WIDTH)
        wf.setframerate(SAMPLE_RATE)
        wf.writeframes(b''.join(frames))
    print(f"Audio saved to {OUTPUT_FILE}")
</code></pre>
<hr />
<p dir="auto"><a href="https://drive.google.com/file/d/1kTzM0J57ikuy9w1qmc84AH1smaaUSh2a/view?usp=sharing" target="_blank" rel="noopener noreferrer nofollow ugc">Recorded Audio</a><br />
Any advice or pointers would be greatly appreciated.</p>
<p dir="auto">Thanks in advance.</p>
]]></description><link>https://community.m5stack.com/topic/7707/help-audio-from-m5stack-atom-echo-appears-sped-up-choppy-low-quality</link><generator>RSS for Node</generator><lastBuildDate>Mon, 18 May 2026 09:55:11 GMT</lastBuildDate><atom:link href="https://community.m5stack.com/topic/7707.rss" rel="self" type="application/rss+xml"/><pubDate>Wed, 23 Jul 2025 18:40:51 GMT</pubDate><ttl>60</ttl></channel></rss>