// // Cavaliere Heartbeat // #include #include // Encoder by Paul Stoffregen #include 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; }