</>CoderDIY
← Quay lại danh sách
Bài viết

Máy chỉnh dây đàn guitar tự chế bằng Arduino

Tác giả: CoderDIY··Đọc bằng tiếng Anh
🎸

Máy chỉnh dây đàn guitar bán sẵn giá không đắt, nhưng tự làm một chiếc bằng Arduino là bài tập tuyệt vời để hiểu cách vi điều khiển “nghe” và phân tích âm thanh — biến sóng âm analog thành một con số tần số cụ thể rồi so sánh với nốt chuẩn. Nguyên lý ở đây (lấy mẫu âm thanh, phát hiện cao độ) cũng chính là nền tảng của nhận diện giọng nói, phân tích rung động máy móc và nhiều ứng dụng xử lý tín hiệu khác.

Nguyên lý hoạt động

  1. Micro electret thu âm thanh dây đàn đang rung, khuếch đại lên bằng một tầng op-amp đơn giản trước khi đưa vào chân ADC của Arduino.
  2. Arduino lấy mẫu tín hiệu âm thanh ở tần số đủ cao (khoảng 5-9kHz, đủ để bắt các nốt guitar tiêu chuẩn từ E2 ~82Hz đến E4 ~330Hz theo định lý lấy mẫu Nyquist).
  3. Thay vì dùng FFT tốn nhiều bộ nhớ, thuật toán tự tương quan (autocorrelation) đơn giản hơn được dùng để tìm chu kỳ lặp lại trong tín hiệu — từ chu kỳ đó suy ra tần số cơ bản của nốt nhạc.
  4. Tần số đo được so sánh với 6 tần số chuẩn của dây guitar tiêu chuẩn (E2, A2, D3, G3, B3, E4); độ lệch (tính bằng cent — đơn vị âm nhạc) được hiển thị qua dải đèn LED hoặc màn hình LCD để người chơi biết cần lên hay xuống dây.
<rect x="10" y="20" width="140" height="60" rx="10" class="box" />
<text x="80" y="45" text-anchor="middle" class="label">Lấy mẫu ADC</text>
<text x="80" y="62" text-anchor="middle" class="label">(~9kHz)</text>

<rect x="170" y="20" width="140" height="60" rx="10" class="box" />
<text x="240" y="45" text-anchor="middle" class="label">Tự tương quan</text>
<text x="240" y="62" text-anchor="middle" class="label">tìm chu kỳ lặp</text>

<rect x="330" y="20" width="140" height="60" rx="10" class="box" />
<text x="400" y="45" text-anchor="middle" class="label">Tính tần số</text>
<text x="400" y="62" text-anchor="middle" class="label">(Hz)</text>

<rect x="490" y="20" width="140" height="60" rx="10" class="box" />
<text x="560" y="45" text-anchor="middle" class="label">So với nốt chuẩn</text>
<text x="560" y="62" text-anchor="middle" class="label">→ hiển thị lệch</text>

<line x1="150" y1="50" x2="170" y2="50" class="arrow" />
<line x1="310" y1="50" x2="330" y2="50" class="arrow" />
<line x1="470" y1="50" x2="490" y2="50" class="arrow" />

<path d="M 560 80 L 560 190 L 80 190 L 80 80" class="arrow" fill="none" />
<text x="320" y="210" text-anchor="middle" class="sublabel">Lặp lại liên tục, cập nhật hiển thị theo thời gian thực</text>
Vòng lặp phát hiện cao độ: lấy mẫu âm thanh, tự tương quan tìm chu kỳ, tính tần số, so sánh và hiển thị độ lệch.

Linh kiện

  • Arduino Uno hoặc Nano
  • Micro electret (loại 2 chân hoặc 3 chân, kèm mạch khuếch đại đơn giản dùng op-amp LM386 hoặc module micro có sẵn khuếch đại)
  • Dải LED (5-7 đèn) hoặc màn hình LCD 16x2 để hiển thị mức lệch cao/thấp
  • Điện trở và tụ điện cho tầng lọc/phân cực micro (theo datasheet module micro sử dụng)
  • Vỏ hộp nhỏ để đóng gói gọn gàng, tiện mang theo khi chơi đàn
<rect x="235" y="90" width="170" height="120" rx="10" class="box" />
<text x="320" y="155" text-anchor="middle" class="label">Arduino</text>

<rect x="20" y="30" width="150" height="70" rx="10" class="box" />
<text x="95" y="70" text-anchor="middle" class="label">Micro + khuếch đại</text>

<rect x="470" y="30" width="150" height="70" rx="10" class="box" />
<text x="545" y="70" text-anchor="middle" class="label">Dải LED (5-7 đèn)</text>

<line x1="170" y1="65" x2="235" y2="120" class="wire" />
<text x="180" y="95" text-anchor="middle" class="sublabel">Out → A0</text>

<line x1="405" y1="120" x2="470" y2="65" class="wire" />
<text x="455" y="95" text-anchor="middle" class="sublabel">D2-D8</text>
Tín hiệu âm thanh sau khuếch đại đưa vào A0; các LED hiển thị mức lệch cao/thấp so với nốt chuẩn điều khiển bởi các chân digital.

Code mẫu

const int MIC_PIN = A0;
const int SAMPLE_SIZE = 256;
const float SAMPLE_RATE_HZ = 9000.0;

int samples[SAMPLE_SIZE];

// Tan so chuan 6 day guitar (E2 A2 D3 G3 B3 E4)
const float STRING_FREQS[] = {82.41, 110.00, 146.83, 196.00, 246.94, 329.63};
const char* STRING_NAMES[] = {"E2", "A2", "D3", "G3", "B3", "E4"};

void setup() {
  Serial.begin(115200);
}

float detectFrequency() {
  unsigned long intervalMicros = 1000000.0 / SAMPLE_RATE_HZ;
  for (int i = 0; i < SAMPLE_SIZE; i++) {
    samples[i] = analogRead(MIC_PIN) - 512; // tru offset DC
    delayMicroseconds(intervalMicros);
  }

  // Tu tuong quan: tim do tre (lag) co tong tich luy lon nhat
  int bestLag = -1;
  long bestCorr = 0;
  for (int lag = 20; lag < 200; lag++) { // tuong ung dai tan ~45-450Hz o 9kHz
    long corr = 0;
    for (int i = 0; i < SAMPLE_SIZE - lag; i++) {
      corr += (long)samples[i] * samples[i + lag];
    }
    if (corr > bestCorr) {
      bestCorr = corr;
      bestLag = lag;
    }
  }

  if (bestLag <= 0) return 0;
  return SAMPLE_RATE_HZ / bestLag;
}

void loop() {
  float freq = detectFrequency();
  if (freq < 60 || freq > 400) return; // ngoai dai guitar, bo qua nhieu

  // Tim day guitar gan nhat
  int closest = 0;
  float minDiff = 9999;
  for (int i = 0; i < 6; i++) {
    float diff = abs(freq - STRING_FREQS[i]);
    if (diff < minDiff) {
      minDiff = diff;
      closest = i;
    }
  }

  float cents = 1200.0 * log(freq / STRING_FREQS[closest]) / log(2.0);

  Serial.print(STRING_NAMES[closest]);
  Serial.print(" - Tan so: ");
  Serial.print(freq);
  Serial.print("Hz - Lech: ");
  Serial.print(cents);
  Serial.println(" cent");

  // cents > 0: dang cao hon chuan (can chinh xuong), cents < 0: dang thap hon (can chinh len)
}

Những lỗi thường gặp

  • Không trừ offset DC trước khi tự tương quan khiến thuật toán luôn tìm ra độ trễ sai — micro electret thường có điện áp phân cực (bias) khoảng giữa dải ADC, cần trừ đi trước khi xử lý.
  • Lấy mẫu quá thưa làm mất khả năng phân biệt các dây trầm (E2 ~82Hz) — tần số lấy mẫu cần ít nhất gấp đôi tần số cao nhất muốn bắt, nhưng thực tế nên cao hơn nhiều lần để độ chính xác tốt.
  • Nhiễu từ tiếng ồn xung quanh khiến kết quả nhảy loạn — dùng vỏ bọc micro hướng thẳng vào lỗ thoát âm của đàn, hoặc chỉ chấp nhận kết quả khi biên độ tín hiệu đủ lớn.
  • delayMicroseconds không chính xác tuyệt đối trên vòng lặp dài làm lệch tần số lấy mẫu thực tế — với ứng dụng cần độ chính xác cao hơn, nên dùng ngắt Timer thay vì delay trong vòng lặp.

Mở rộng

Thêm màn hình OLED hiển thị kim đồng hồ ảo giống tuner chuyên nghiệp, hỗ trợ nhiều kiểu tuning khác (Drop D, DADGAD), hoặc tham khảo cách xử lý và hiển thị tín hiệu analog theo thời gian thực trong bài máy hiện sóng mini bằng Arduino để debug trực quan dạng sóng âm thanh thu được.

Thẻ:#arduino#audio#music

Bình luận