#include "VikTronome.h"
#include <LiquidCrystal.h>
#define LED 13
#define ENCODER_CLK 2
#define ENCODER_DT 3
#define ENCODER_BTN 4
#define SELECT_BTN 6
#define MAX_BEATS_STEPS 9
#define NOTE_DURATION_MIN 1
#define NOTE_DURATION_MAX 2
#define SOUND_MIN 1
#define SOUND_MAX 9
#define MENU_TEMPO 0
#define MENU_BEATS 1
#define MENU_NOTE_DURATION 2
#define MENU_SOUND 3
VikTronome metronome;
LiquidCrystal lcd(12, 11, 10, 9, 8, 7);
int lastClk = HIGH;
bool isOn = false;
byte step = 0;
byte totalSteps = 4;
byte sound = 8;
byte noteDuration = NOTE_QUARTERS;
byte menuSelect = 0;
byte customCharArrow[] = {
B01000,
B01100,
B11110,
B11111,
B11111,
B11110,
B01100,
B01000
};
byte customCharSpeaker[] = {
B00001,
B00011,
B01111,
B01111,
B01111,
B00011,
B00001,
B00000
};
byte customCharNoteEights[] = {
B00001,
B00011,
B00101,
B01001,
B01001,
B01011,
B11011,
B11000
};
byte customCharNoteQuarter[] = {
B00011,
B00010,
B00010,
B00010,
B00010,
B00110,
B01110,
B00100
};
void handleTickCallback() {
if(!isOn) return;
step++;
if(step > totalSteps - 1) step = 0;
digitalWrite(LED, digitalRead(LED) ^ 1);
refreshScreen(true);
}
void setup() {
pinMode(LED, OUTPUT);
pinMode(ENCODER_CLK, INPUT);
pinMode(ENCODER_DT, INPUT);
pinMode(ENCODER_BTN, INPUT_PULLUP);
pinMode(SELECT_BTN, INPUT_PULLUP);
metronome.setup();
// Serial.begin(9600);
// metronome.setTempo(40);
// setup LCD
lcd.begin(16, 2);
lcd.createChar(1, customCharArrow);
lcd.createChar(2, customCharSpeaker);
lcd.createChar(3, customCharNoteQuarter);
lcd.createChar(4, customCharNoteEights);
lcd.setCursor(0, 0);
lcd.print("VikTronome");
lcd.setCursor(0, 1);
lcd.print("v0.2");
delay(2000);
refreshScreen(false);
}
void refreshScreen(bool playTone) {
lcd.clear();
// draw tempo
lcd.setCursor(1, 0);
lcd.print((String)"BPM: " + metronome.tempo);
// handle tone
if(playTone) {
soundBeats();
}
// draw beats
drawBeats();
// draw notes
lcd.setCursor(13, 0);
lcd.write(4);
lcd.write(":");
switch(noteDuration) {
case NOTE_QUARTERS:
lcd.write(3);
break;
case NOTE_EIGHTS:
lcd.write(4);
break;
}
// draw sound
lcd.setCursor(13, 1);
lcd.write(2);
lcd.write(":");
lcd.print(sound);
// draw menu
drawMenu();
}
void drawBeats() {
lcd.setCursor(1, 1);
lcd.print(totalSteps + (String)":");
for(byte i = 0; i < totalSteps; i++) {
if(step == i && isOn) {
if(step == 0) {
lcd.write(255);
} else {
lcd.write(219);
}
} else {
lcd.write(".");
}
}
}
void soundBeats() {
lcd.setCursor(1, 1);
lcd.print(totalSteps + (String)":");
for(byte i = 0; i < totalSteps; i++) {
if(step == i && isOn) {
if(step == 0) {
tone(5, 400 * sound + 200, 20);
} else {
tone(5, 300 * sound + 200, 20);
}
} else {
lcd.write(".");
}
}
}
void drawMenu() {
switch(menuSelect) {
case MENU_TEMPO:
lcd.setCursor(0, 0);
lcd.write(1);
break;
case MENU_BEATS:
lcd.setCursor(0, 1);
lcd.write(1);
break;
case MENU_NOTE_DURATION:
lcd.setCursor(12, 0);
lcd.write(1);
break;
case MENU_SOUND:
lcd.setCursor(12, 1);
lcd.write(1);
break;
}
}
void loop() {
int newClk = digitalRead(ENCODER_CLK);
if (newClk != lastClk) {
// There was a change on the CLK pin
lastClk = newClk;
int dtValue = digitalRead(ENCODER_DT);
if (newClk == LOW && dtValue == HIGH) {
handleRotaryUp();
refreshScreen(false);
} else if (newClk == LOW && dtValue == LOW) {
handleRotaryDown();
refreshScreen(false);
}
delay(5);
}
// handle button
if (digitalRead(SELECT_BTN) == LOW) {
isOn = !isOn;
step = 0;
metronome.reset();
refreshScreen(true);
delay(300);
}
// handle menu
if(digitalRead(ENCODER_BTN) == LOW) {
menuSelect++;
if(menuSelect > 3) menuSelect = 0;
refreshScreen(false);
delay(300);
}
}
void handleRotaryUp() {
switch(menuSelect) {
case MENU_TEMPO:
metronome.addOneBPM();
break;
case MENU_BEATS:
addTotalSteps();
break;
case MENU_NOTE_DURATION:
addNoteDuration();
break;
case MENU_SOUND:
addSoundStep();
break;
}
}
void handleRotaryDown() {
switch(menuSelect) {
case MENU_TEMPO:
metronome.subtractOneBPM();
break;
case MENU_BEATS:
subtractTotalSteps();
break;
case MENU_NOTE_DURATION:
subtractNoteDuration();
break;
case MENU_SOUND:
subtractSoundStep();
break;
}
}
void addNoteDuration() {
if(noteDuration < NOTE_DURATION_MAX) {
noteDuration++;
} else {
noteDuration = NOTE_DURATION_MIN;
}
metronome.setNoteDuration(noteDuration);
metronome.reset();
step = 0;
refreshScreen(true);
}
void subtractNoteDuration() {
if(noteDuration > NOTE_DURATION_MIN) {
noteDuration--;
} else {
noteDuration = NOTE_DURATION_MAX;
}
step = 0;
metronome.setNoteDuration(noteDuration);
metronome.reset();
refreshScreen(true);
}
void addSoundStep() {
if(sound < SOUND_MAX) {
sound++;
}
}
void subtractSoundStep() {
if(sound > SOUND_MIN) {
sound--;
}
}
void addTotalSteps() {
if(totalSteps / NOTE_QUARTERS < MAX_BEATS_STEPS) {
totalSteps++;
}
totalSteps = totalSteps / NOTE_QUARTERS;
}
void subtractTotalSteps() {
if(totalSteps > 1) {
totalSteps--;
}
totalSteps = totalSteps / NOTE_QUARTERS;
}
ISR(TIMER1_COMPA_vect) {
handleTickCallback();
}