blogy logo
login PRIHLÁS SA
BLOG deadawp
ČLÁNKY
DISKUSIE
2
SLEDOVAŤ BLOG
PHP, Arduino programátor
deadawp



Digitálny metronóm - Arduino
pridal deadawp 31.5. 2020 o 1:22



Digitálny metronóm je elektronické zariadenie, ktoré umožňuje v pravidelných intervaloch simulovať kyvadlo a vykonávať pravidelný pulz. Každý pulz reprezentuje jednu dobu, čo štandardne reprezentuje jednu štvrťovú notu (v 4/4 takte). Digitálny metronóm definuje aj rytmus vykonaním dôrazu (prízvuku) na určitú dobu, čím je možné rytmus v ktorom metronóm tiká ihneď aj počuteľne zistiť. Využíva sa predovšetkým pri nácvikoch s rytmickými hudobnými nástrojmi, napríklad husle, harmonika.

Metronóm, ktorý som navrhrol pre platformu Arduino v minimálnej konfigurácii umožňuje modifikovať (tempo) - počet pulzov za minútu - BPM (Beats per minute) a štvrťové takty prostredníctvom používateľského vstupu tlačidlami. Metronóm využíva výpis na UART v prípade zmeny niektorého z nastavení, neobsahuje displej ani inú vizualizačnú perifériu.

Z hľadiska BPM môžeme rozdeliť tempo podľa rozsahov:

  • do 40 - Grave
  • 40 do 50 BPM - Largo
  • 52 až 63 BPM - Larghetto
  • 66 až 76 BPM - Adagio
  • 80 až 100 BPM - Andante
  • 104 až 116 BPM - Moderato
  • 120 až 152 BPM - Allegro
  • 160 až 200 BPM - Presto

Z hľadiska taktov poznáme (zvýraznené obsiahnuté v implementácii pre Arduino):

  • 2/4
  • 3/4
  • 4/4 (natívne predvolené po štarte)
  • 5/4
  • 2/2
  • 6/8
  • 7/8
  • 9/8

Osminové a pólové takty nie sú obsiahnuté v implementácii. Po stránke ovládania implementácia využíva päť hardvérových tlačidiel zapojených v režime INPUT_PULLUP (active LOW). Štyri z tlačidiel ovládajú hodnotu BPM (-10, -1, +1, +10), posledné tlačidlo slúži na zvolenie taktu 3/4, 4/4, 5/4. Nakoľko sú takty štvrťové, každý impulz (beep) z buzzera reprezentuje jednu štvrťovú dobu (notu).
Ukážkový výstup UART (Serial) monitoru z prevádzky:

Každé tlačidlo je ošetrené voči zákmitom testovaním stavu s dĺžkou 50ms, počas ktorých musí byť stav ustálený, rovnaká časová hodnota sa používa aj na dĺžku beepu z buzzera. Časovanie pulzov, tlačidiel využíva funkciu interného časovača - millis(), pričom dokážu fungovať nezávisle na sebe. 

Nakoľko je doba beepu 50 ms, je metronóm obmedzený na max. 200 BPM pre počuteľný impulzy s prestávkami, pre vyššie BPM je nutné skrátiť dĺžku beepu v programe. Posledná doba každého nastaveného taktu (s prízvukom) je reprezentovaná frekvenciou 1500 Hz, každá iná (neprízvučná) doba 1700 Hz. Buzzer dokáže prehrať signál do 20 kHz, nižšie frekvencie sú príjemnejšie na počúvanie.

Schéma zapojenia (kompatibilná pre Arduino Uno):

Viac projektov je možné nájsť na: https://arduino.php5.sk/

Programová implementácia:

//Autor: Martin Chlebovec (martinius96)
//Projekt: Metronóm - UART only
//Revízia: 29. May 2020

//Nastavenie vývodov Arduina k jednotlivým perifériám (Vstupy, výstupy)
const int BUZZER = 9;
const int LED_BLU = 8;
const int LED_RED = 7;
const int BUT_CHANGE_TICK = 6;
const int BUT_plusone = 5;
const int BUT_minusone = 4;
const int BUT_plusten = 3;
const int BUT_minusten = 2;

//Nastavenie počiatočných časových premenných s millis()
unsigned long lastDebounceTime1 = 0;
unsigned long lastDebounceTime2 = 0;
unsigned long lastDebounceTime3 = 0;
unsigned long lastDebounceTime4 = 0;
unsigned long lastDebounceTime5 = 0;
unsigned long debounceInterval = 50;
unsigned long elapsedInterval = 0;
unsigned long interval = 1000; //interval tikání v milisekundách

//Nastavenie počiatočných stavov tlačidiel
int buttonState1 = HIGH;
int buttonState2 = HIGH;
int buttonState3 = HIGH;
int buttonState4 = HIGH;
int buttonState5 = HIGH;

//Nastavenie posledných stavov tlačidiel
int lastButtonState1 = HIGH;
int lastButtonState2 = HIGH;
int lastButtonState3 = HIGH;
int lastButtonState4 = HIGH;
int lastButtonState5 = HIGH;

//Deklarácia hodnôt frekvencií pre doby + poslednú dobu, rytmus, atď...
const int freqFirst = 1700;
const int freqFourths = 1500;
int rythm = 4;
int freq = 0;
int bpm = 0;
int tick_case = 0;
int actual = 0;

//Defínicia funkcie pre výpis BPM
void showBPM()
{
  // bpm = 60000 / interval;
  Serial.print("BPM: ");
  Serial.println(bpm);
}

//Defínicia funkcie pre výpis rytmusu
void showRythm()
{
  Serial.print("Takt: ");
  Serial.print(rythm);
  Serial.println("/4");
}

//Defínicia funkcie s volaním funkcií pre výpis BPM, rytmusu
void aktualizaceDispleje()
{
  showBPM();
  showRythm();
}

void setup() {
  //počiatočné nastavenie výstupov a vstupov
  pinMode(LED_RED, OUTPUT);
  pinMode(LED_BLU, OUTPUT);
  pinMode(BUZZER, OUTPUT);
  pinMode(BUT_CHANGE_TICK, INPUT_PULLUP);
  pinMode(BUT_plusone, INPUT_PULLUP);
  pinMode(BUT_minusone, INPUT_PULLUP);
  pinMode(BUT_plusten, INPUT_PULLUP);
  pinMode(BUT_minusten, INPUT_PULLUP);
  Serial.begin(115200);

}

void loop() {
  int reading1 = digitalRead(BUT_plusone);
  int reading2 = digitalRead(BUT_minusone);
  int reading3 = digitalRead(BUT_plusten);
  int reading4 = digitalRead(BUT_minusten);
  int reading5 = digitalRead(BUT_CHANGE_TICK);

  if (reading1 != lastButtonState1) {
    lastDebounceTime1 = millis();
  }

  if (reading2 != lastButtonState2) {
    lastDebounceTime2 = millis();
  }

  if (reading3 != lastButtonState3) {
    lastDebounceTime3 = millis();
  }

  if (reading4 != lastButtonState4) {
    lastDebounceTime4 = millis();
  }

  if (reading5 != lastButtonState5) {
    lastDebounceTime5 = millis();
  }

  if ((millis() - lastDebounceTime1) >= debounceInterval) {
    if (reading1 != buttonState1) {
      buttonState1 = reading1;
      if (buttonState1 == HIGH) {
        bpm = bpm + 1; //bmp + 1
        interval = 60000 / bpm;
        freq = freqFirst;
        aktualizaceDispleje();
        digitalWrite(LED_RED, LOW);
        digitalWrite(LED_BLU, HIGH);
        tone(BUZZER, freq);
        actual = 1;
        elapsedInterval = millis();
      }
    }
  }

  if ((millis() - lastDebounceTime2) >= debounceInterval) {
    if (reading2 != buttonState2) {
      buttonState2 = reading2;
      if (buttonState2 == HIGH) {
        bpm = bpm - 1; //bmp - 1
        if (bpm < 0) {
          bpm = 0;
        }
        interval = 60000 / bpm;
        freq = freqFirst;
        aktualizaceDispleje();
        digitalWrite(LED_RED, LOW);
        digitalWrite(LED_BLU, HIGH);
        tone(BUZZER, freq);
        actual = 1;
        elapsedInterval = millis();
      }
    }
  }

  if ((millis() - lastDebounceTime3) >= debounceInterval) {
    if (reading3 != buttonState3) {
      buttonState3 = reading3;
      if (buttonState3 == HIGH) {
        bpm = bpm + 10; //bmp + 10
        interval = 60000 / bpm;
        freq = freqFirst;
        aktualizaceDispleje();
        digitalWrite(LED_RED, LOW);
        digitalWrite(LED_BLU, HIGH);
        tone(BUZZER, freq);
        actual = 1;
        elapsedInterval = millis();
      }
    }
  }

  if ((millis() - lastDebounceTime4) >= debounceInterval) {
    if (reading4 != buttonState4) {
      buttonState4 = reading4;
      if (buttonState4 == HIGH) {
        bpm = bpm - 10; //bmp - 10
        if (bpm < 0) {
          bpm = 0;
        }
        interval = 60000 / bpm;
        freq = freqFirst;
        aktualizaceDispleje();
        digitalWrite(LED_RED, LOW);
        digitalWrite(LED_BLU, HIGH);
        tone(BUZZER, freq);
        actual = 1;
        elapsedInterval = millis();
      }
    }
  }

  if ((millis() - lastDebounceTime5) >= debounceInterval) {
    if (reading5 != buttonState5) {
      buttonState5 = reading5;
      if (buttonState5 == HIGH) {
        tick_case++;
        switch (tick_case) {
          case 0:
            rythm = 4; // 4/4 takt
            break;
          case 1:
            rythm = 5; // 5/4 takt
            break;
          case 2:
            rythm = 3; // 3/4 takt
            break;
          default:
            tick_case = 0;
            rythm = 4;
            break;
        }
        aktualizaceDispleje();
      }
    }
  }

  if (freq != 0 && bpm != 0) {
    if ((millis() - elapsedInterval) >= debounceInterval) { //50ms beep
      digitalWrite(LED_RED, LOW);
      digitalWrite(LED_BLU, LOW);
      noTone(BUZZER);
    }
    if ((millis() - elapsedInterval) >= interval) {
      actual++;
      if (actual > rythm) {
        actual = 1;
      }
      if (actual < rythm) {
        freq = freqFirst;
        digitalWrite(LED_RED, LOW);
        digitalWrite(LED_BLU, HIGH);
      }
      if (actual == rythm) {
        freq = freqFourths;
        digitalWrite(LED_RED, HIGH);
        digitalWrite(LED_BLU, LOW);
      }
      tone(BUZZER, freq);
      elapsedInterval = millis();
    }
  }

  lastButtonState1 = reading1;
  lastButtonState2 = reading2;
  lastButtonState3 = reading3;
  lastButtonState4 = reading4;
  lastButtonState5 = reading5;
}



Prístupov 2048
Kvalita článku
hlasov 0

PRÍSPEVKY
SLEDOVAŤ
Prosím prihláste sa pre možnosť pridania komentáru.
Prihláste sa, alebo použite facebook login facebook login
ĎALŠIE ČLÁNKY V BLOGU
Ovládanie Arduina cez UART z počítača
[ 19.11.2020] (príspevkov 0)
ArduinoJson - knižnica pre parsovanie JS...
[ 15.11.2020] (príspevkov 0)
Overenie kradnutých vozidiel - Arduino +...
[ 11.11.2020] (príspevkov 0)
Hladinomer + Zrážkomer - Arduino / ESP82...
[ 2.11.2020] (príspevkov 0)
RFID vrátnik - update Október 2020
[ 29.10.2020] (príspevkov 0)
Digitálny potenciometer, prerušenie, deb...
[ 28.10.2020] (príspevkov 0)
Integrácia zrážkomera RG-11 do projektu ...
[ 22.10.2020] (príspevkov 0)
WiFi Termostat - ESP8266 - WiFiManager -...
[ 28.9.2020] (príspevkov 0)
Izbový termostat - Portovanie programu -...
[ 22.9.2020] (príspevkov 0)
RFID vrátnik - Ukončenie projektu
[ 9.9.2020] (príspevkov 0)