gokul/acoustic-guitar-tuner
This code defines the physical pin configuration and footprint layouts for the ATtiny1616 microcontroller and a matching tactile switch component, including their key hardware connection points, dimensions, and 3D models.
- Version
- 1.0.10
- License
- unset
- Stars
- 1
firmware/acoustic_tuner_attiny1616/acoustic_tuner_attiny1616.ino
/*
* Mini Acoustic Guitar Tuner — ATtiny1616 / megaTinyCore 2.6.11
* 8 kHz sampling and normalized autocorrelation for standard EADGBe tuning.
*/
#include <Arduino.h>
#include <math.h>
#include "pitch_detector.h"
// Factory calibration: multiply by reference frequency / displayed frequency.
// The internal RC clock must be calibrated before a +/-4-cent claim is valid.
constexpr float SAMPLE_CLOCK_SCALE = 1.0f;
constexpr uint16_t SAMPLE_RATE = 8000;
constexpr uint16_t SAMPLE_COUNT = 256;
constexpr uint16_t SAMPLE_PERIOD_US = 1000000UL / SAMPLE_RATE;
constexpr uint8_t AUDIO_PIN = PIN_PA2;
constexpr uint8_t MODE_PIN = PIN_PB0;
constexpr uint8_t NOTE_LEDS[] = {
PIN_PA4, PIN_PA5, PIN_PA6, PIN_PA7, PIN_PB5, PIN_PB4
};
constexpr uint8_t FLAT_LED = PIN_PB3;
constexpr uint8_t TUNE_LED = PIN_PB1;
constexpr uint8_t SHARP_LED = PIN_PB2;
constexpr float STRING_HZ[] = { 82.4069f, 110.0f, 146.832f, 195.998f, 246.942f, 329.628f };
constexpr float IN_TUNE_CENTS = 4.0f;
int16_t samples[SAMPLE_COUNT];
void allLedsOff() {
for (uint8_t pin : NOTE_LEDS) digitalWrite(pin, LOW);
digitalWrite(FLAT_LED, LOW);
digitalWrite(TUNE_LED, LOW);
digitalWrite(SHARP_LED, LOW);
}
void captureSamples() {
uint32_t next = micros();
int32_t sum = 0;
for (uint16_t i = 0; i < SAMPLE_COUNT; ++i) {
while ((int32_t)(micros() - next) < 0) {}
const int16_t value = analogRead(AUDIO_PIN);
samples[i] = value;
sum += value;
next += SAMPLE_PERIOD_US;
}
const int16_t mean = sum / SAMPLE_COUNT;
for (uint16_t i = 0; i < SAMPLE_COUNT; ++i) samples[i] -= mean;
}
float estimateFrequency() {
return tuner::estimate(samples) * SAMPLE_CLOCK_SCALE;
}
uint8_t nearestString(float frequency) {
uint8_t best = 0;
float smallestError = 100000.0f;
for (uint8_t i = 0; i < 6; ++i) {
const float cents = fabsf(1731.234049f * logf(frequency / STRING_HZ[i]));
if (cents < smallestError) {
smallestError = cents;
best = i;
}
}
return best;
}
void showTuning(float frequency) {
allLedsOff();
if (frequency <= 0.0f) return;
const uint8_t stringIndex = nearestString(frequency);
digitalWrite(NOTE_LEDS[stringIndex], HIGH);
const float cents = 1731.234049f * logf(frequency / STRING_HZ[stringIndex]);
if (cents < -IN_TUNE_CENTS) digitalWrite(FLAT_LED, HIGH);
else if (cents > IN_TUNE_CENTS) digitalWrite(SHARP_LED, HIGH);
else digitalWrite(TUNE_LED, HIGH);
}
void setup() {
analogReadResolution(10);
pinMode(AUDIO_PIN, INPUT);
pinMode(MODE_PIN, INPUT_PULLUP);
for (uint8_t pin : NOTE_LEDS) pinMode(pin, OUTPUT);
pinMode(FLAT_LED, OUTPUT);
pinMode(TUNE_LED, OUTPUT);
pinMode(SHARP_LED, OUTPUT);
allLedsOff();
}
void loop() {
if (digitalRead(MODE_PIN) == LOW) {
// Sequence one LED at a time to avoid a nine-LED current pulse on CR2032.
const uint8_t indicators[] = {NOTE_LEDS[0], NOTE_LEDS[1], NOTE_LEDS[2],
NOTE_LEDS[3], NOTE_LEDS[4], NOTE_LEDS[5], FLAT_LED, TUNE_LED, SHARP_LED};
allLedsOff();
do {
for (uint8_t pin : indicators) {
digitalWrite(pin, HIGH);
delay(150);
digitalWrite(pin, LOW);
}
} while (digitalRead(MODE_PIN) == LOW);
allLedsOff();
delay(100);
return;
}
captureSamples();
showTuning(estimateFrequency());
}