213 lines
5.3 KiB
C++
213 lines
5.3 KiB
C++
//
|
|
// Cavaliere Heartbeat
|
|
//
|
|
|
|
#include <EEPROM.h>
|
|
#include <Encoder.h> // Encoder by Paul Stoffregen
|
|
|
|
#include <Adafruit_SSD1306.h>
|
|
Adafruit_SSD1306 display(128, 64, &Wire, 4);
|
|
|
|
#define audio 12 // Pin for 3,5 Jack
|
|
#define led 13 // Pin for LED
|
|
#define rot_clk 4 // Rotary encoder CLK pin
|
|
#define rot_dt 3 // Rotary encoder DT pin
|
|
#define rot_sw 2 // Rotary encoder SW pin, 1 = not pushed, 0 = pushed
|
|
#define triggerLengthMS 50 // How long the trigger should stay on in milliseconds when triggerLength = 1
|
|
#define bpm_value_min 80 // 20 bpm is minimum (80 / 4)
|
|
#define bpm_value_max 1200 // 300 bpm is maximum (300 * 4)
|
|
#define offset_x 5 // Display offset
|
|
#define offset_y 5 // Display offset
|
|
|
|
int bpm; // BPM, who'd have thought
|
|
int bpms; // BPM to milliseconds
|
|
int eeprom_bpm; // BPM stored in EEPROM
|
|
int bpm_value; // Later bpm * 4 because of the 4 rotary encoder steps
|
|
int triggerLength = 2; // 1 = triggerLengthMS, 2 = half of bpm
|
|
int stayOnTime; // How long the trigger should stay on in milliseconds when triggerLength = 2
|
|
unsigned long currentTime; // Timing stuff
|
|
unsigned long previousTime = 0; // Timing stuff
|
|
int state = 0; // Prevent trigger flood
|
|
int rot_sw_state = 0; // Rotary encoder SW state
|
|
int rot_sw_temp = 0; // Some dumb temp shit
|
|
long rot_old_pos = 0; // Don't ask
|
|
|
|
// Pins for the rotary encoder up and down
|
|
Encoder rot_enc(rot_dt, rot_clk);
|
|
|
|
void setup() {
|
|
Serial.begin(9600);
|
|
|
|
// Read stored BPM data
|
|
if (EEPROM.read(0) != 1) {
|
|
// No BPM data stored, let's initialize it with 120 BPM
|
|
EEPROM.write(0, 1); // Set byte 0 to 1
|
|
EEPROM.write(1, 1); // 1 x 100
|
|
EEPROM.write(2, 2); // 2 x 10
|
|
EEPROM.write(3, 0); // 0 x 1
|
|
bpm = 120;
|
|
bpm_value = bpm * 4;
|
|
bpms = 60000 / bpm;
|
|
Serial.println("No stored BPM found. Initialized with 120 BPM.");
|
|
} else {
|
|
// BPM data stored
|
|
int a = EEPROM.read(1);
|
|
int b = EEPROM.read(2);
|
|
int c = EEPROM.read(3);
|
|
eeprom_bpm = (a * 100) + (b * 10) + (c * 1);
|
|
bpm = eeprom_bpm;
|
|
bpm_value = bpm * 4;
|
|
bpms = 60000 / bpm;
|
|
Serial.print("Stored BPM data found: ");
|
|
Serial.println(eeprom_bpm);
|
|
}
|
|
|
|
// Display
|
|
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
|
|
display.setTextColor(SSD1306_WHITE);
|
|
displayUpdate(bpm, bpm);
|
|
|
|
// Calculate how long the LED should stay on
|
|
if (triggerLength == 1) {
|
|
stayOnTime = triggerLengthMS;
|
|
} else if (triggerLength == 2) {
|
|
stayOnTime = bpms / 2;
|
|
}
|
|
|
|
// Enable inputs and outputs
|
|
pinMode(audio, OUTPUT);
|
|
pinMode(led, OUTPUT);
|
|
pinMode(rot_clk, INPUT);
|
|
pinMode(rot_dt, INPUT);
|
|
pinMode(rot_sw, INPUT);
|
|
}
|
|
|
|
void loop() {
|
|
// Beat stuff
|
|
currentTime = millis();
|
|
|
|
if (currentTime - previousTime >= bpms) {
|
|
triggerOn();
|
|
previousTime = currentTime;
|
|
state = 1;
|
|
}
|
|
|
|
if (currentTime - previousTime >= stayOnTime && state == 1) {
|
|
triggerOff();
|
|
state = 0;
|
|
}
|
|
|
|
// Rotary encoder stuff
|
|
long rot_new_pos = rot_enc.read();
|
|
|
|
if (rot_new_pos != rot_old_pos) {
|
|
if (rot_new_pos > rot_old_pos) {
|
|
bpm_value++;
|
|
if (bpm_value > bpm_value_max) {
|
|
bpm_value = bpm_value_max;
|
|
}
|
|
}
|
|
|
|
if (rot_new_pos < rot_old_pos) {
|
|
bpm_value--;
|
|
if (bpm_value < bpm_value_min) {
|
|
bpm_value = bpm_value_min;
|
|
}
|
|
}
|
|
|
|
displayUpdate(bpm, bpm_value / 4);
|
|
|
|
rot_old_pos = rot_new_pos;
|
|
}
|
|
|
|
// Rotary encoder button
|
|
rot_sw_state = digitalRead(rot_sw);
|
|
|
|
if (rot_sw_state == 1) {
|
|
rot_sw_temp = 0;
|
|
}
|
|
|
|
if (rot_sw_state == 0 && rot_sw_temp == 0) {
|
|
rot_sw_push();
|
|
rot_sw_temp = 1;
|
|
}
|
|
}
|
|
|
|
void triggerOn() {
|
|
Serial.print("Trigger ON at ");
|
|
Serial.print(currentTime);
|
|
Serial.println(" milliseconds");
|
|
|
|
digitalWrite(audio, HIGH);
|
|
digitalWrite(led, HIGH);
|
|
}
|
|
|
|
void triggerOff() {
|
|
Serial.print("Trigger OFF at ");
|
|
Serial.print(currentTime);
|
|
Serial.println(" milliseconds");
|
|
|
|
digitalWrite(audio, LOW);
|
|
digitalWrite(led, LOW);
|
|
}
|
|
|
|
void rot_sw_push() {
|
|
// Recalculate BPM stuff
|
|
bpm = bpm_value / 4;
|
|
bpms = 60000 / bpm;
|
|
if (triggerLength == 2) {
|
|
stayOnTime = bpms / 2;
|
|
}
|
|
|
|
Serial.print("BPM set to ");
|
|
Serial.println(bpm);
|
|
|
|
displayUpdate(bpm, bpm);
|
|
|
|
// Save new BPM to EEPROM
|
|
int a = bpm / 100;
|
|
int b = (bpm - (a * 100)) / 10;
|
|
int c = (bpm - (a * 100) - (b * 10));
|
|
EEPROM.write(1, a);
|
|
EEPROM.write(2, b);
|
|
EEPROM.write(3, c);
|
|
}
|
|
|
|
int displayUpdate(int bpm, int bpm2) {
|
|
display.clearDisplay();
|
|
|
|
display.setTextSize(1);
|
|
display.setCursor(0 + offset_x, 0 + offset_y);
|
|
display.println("cavaliere HEARTBEAT");
|
|
|
|
display.setTextSize(2);
|
|
display.setCursor(0 + offset_x, 18 + offset_y);
|
|
display.println("BPM");
|
|
display.setCursor(0 + offset_x, 40 + offset_y);
|
|
display.println("NEW");
|
|
|
|
String bpmToStr;
|
|
String bpm2ToStr;
|
|
|
|
// Leading whitespace when bpm < 100
|
|
display.setCursor(68 + offset_x, 18 + offset_y);
|
|
if (bpm < 100) {
|
|
bpmToStr = " " + String(bpm);
|
|
} else {
|
|
bpmToStr = String(bpm);
|
|
}
|
|
display.println(bpmToStr);
|
|
|
|
// Leading whitespace when bpm2 < 100
|
|
display.setCursor(68 + offset_x, 40 + offset_y);
|
|
if (bpm2 < 100) {
|
|
bpm2ToStr = " " + String(bpm2);
|
|
} else {
|
|
bpm2ToStr = String(bpm2);
|
|
}
|
|
display.println(bpm2ToStr);
|
|
|
|
display.display();
|
|
return 0;
|
|
}
|