#include #include #include #include #include "globals.h" #include "Out.h" #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 #define SCREEN_ADDRESS 0x3C #define CLK 2 #define DT 3 U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); byte play = 1; byte BPM = 100; unsigned long minute = 60000000L; byte ppqn = 96; unsigned long period = (minute / BPM) / ppqn; volatile boolean beatToggle = false; Out out1(4); Out out2(5); Out out3(6); Out out4(7); Out out5(8); Out out6(9); Out out7(10); Out out8(11); byte btnPin = 13; byte btnState = 0; volatile long rPotPos = 0; unsigned long rPotLastUpdate = 0; const unsigned long rPotThrottle = 50; unsigned long screenLastUpdate = 0; const unsigned long screenThrottle = 200; byte forceScreenUpdate = 1; void setup() { Serial.begin(9600); pinMode(btnPin, INPUT_PULLUP); // rotary encoder set up pinMode(CLK, INPUT_PULLUP); pinMode(DT, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(CLK), updateEncoder, FALLING); // screen setup u8g2.begin(); // display.clearDisplay(); // display.setTextSize(2); // display.setTextColor(WHITE); // display.setCursor(0, 0); // // String bpmString = "BPM: "; // String bpmCalculatedString = bpmString + BPM; // display.println(bpmCalculatedString); // // display.display(); // init timer Timer1.initialize(1000000); Timer1.attachInterrupt(clock); Timer1.setPeriod(period); // init outputs out1.setLen(period); out2.setLen(period); out3.setLen(period); out4.setLen(period); out5.setLen(period); out6.setLen(period); out7.setLen(period); out8.setLen(period); // manual setup out1.setDiv(8, 0); out1.setWidth(50); out2.setDiv(4, 0); out2.setWidth(50); out3.setDiv(2, 0); out3.setWidth(50); out4.setDiv(1); out4.setWidth(50); out5.setDiv(2); out5.setWidth(50); out6.setDiv(4); out6.setWidth(50); out7.setDiv(8); out7.setWidth(50); out8.setDiv(16); out8.setWidth(50); } void clock(void) { if (play == 1) { beatToggle = true; } } void loop() { if (beatToggle) { out1.turnOn(); out2.turnOn(); out3.turnOn(); out4.turnOn(); out5.turnOn(); out6.turnOn(); out7.turnOn(); out8.turnOn(); beatToggle = false; } out1.turnOff(); out2.turnOff(); out3.turnOff(); out4.turnOff(); out5.turnOff(); out6.turnOff(); out7.turnOff(); out8.turnOff(); checkBtn(); checkRPot(); updateScreen(); } void updateScreen() { unsigned long currentTime = millis(); if (currentTime - screenLastUpdate >= screenThrottle && forceScreenUpdate == 1) { screenLastUpdate = currentTime; forceScreenUpdate = 0; // The firstPage()/nextPage() loop manages where the drawing commands go u8g2.firstPage(); do { // --- Drawing Commands for the CURRENT Page --- // All drawing commands must live INSIDE this do/while loop. u8g2.setFont(u8g2_font_helvR08_tf); u8g2.setCursor(0, 10); u8g2.print(F("BPM: ")); u8g2.print(BPM); // Example of dynamic data } while (u8g2.nextPage()); } } void checkBtn() { byte currentBtnState = digitalRead(btnPin); if (currentBtnState != btnState) { if (currentBtnState == 1) { play = (play == 1) ? 0 : 1; }; } btnState = currentBtnState; }; void checkRPot() { if (millis() - rPotLastUpdate >= rPotThrottle) { noInterrupts(); long currentPos = rPotPos; interrupts(); // Print position only if it has changed static long previousPos = -999; if (currentPos != previousPos) { Serial.print("Encoder Position: "); Serial.println(currentPos); if (currentPos > previousPos) { handleBPMChange(1); } else { handleBPMChange(0); } forceScreenUpdate = 1; previousPos = currentPos; } rPotLastUpdate = millis(); // Reset the timer for throttling the output } } void handleBPMChange(byte increase) { if (increase == 1) { BPM++; } else { BPM--; } period = (minute / BPM) / ppqn; Timer1.setPeriod(period); } void updateEncoder() { // The ISR should be as short and fast as possible // Check the state of the DT pin to determine direction if (digitalRead(3) == LOW) { rPotPos++; // Clockwise } else { rPotPos--; // Counter-clockwise } }