263 lines
5.1 KiB
C++
263 lines
5.1 KiB
C++
#include <SPI.h>
|
|
#include <Wire.h>
|
|
#include <U8g2lib.h>
|
|
#include <TimerOne.h>
|
|
|
|
#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;
|
|
|
|
byte currentScreen = 0;
|
|
byte editMode = 0;
|
|
int mainScreenCursorPos = 0;
|
|
byte maxMainScreenPos = 8;
|
|
|
|
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();
|
|
u8g2.setFontMode(1);
|
|
|
|
// 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;
|
|
u8g2.firstPage();
|
|
do {
|
|
// 12px - nonbold
|
|
if (mainScreenCursorPos == 0) {
|
|
u8g2.setFont(u8g2_font_7x14B_mf);
|
|
} else {
|
|
u8g2.setFont(u8g2_font_7x14_mf);
|
|
}
|
|
|
|
u8g2.setCursor(0, 15);
|
|
u8g2.print(F("BPM: "));
|
|
u8g2.print(BPM);
|
|
|
|
u8g2.setFont(u8g2_font_7x14_mf);
|
|
u8g2.setCursor(0, 45);
|
|
|
|
char buffer[5];
|
|
for (byte i = 1; i < 9; i++) {
|
|
if (i == 5) {
|
|
u8g2.setCursor(0, 60);
|
|
}
|
|
|
|
if (mainScreenCursorPos == i) {
|
|
u8g2.setFont(u8g2_font_7x14B_mf);
|
|
}
|
|
|
|
sprintf(buffer, "[%d] ", i);
|
|
u8g2.print(buffer);
|
|
|
|
if (mainScreenCursorPos == i) {
|
|
u8g2.setFont(u8g2_font_7x14_mf);
|
|
}
|
|
};
|
|
} 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);
|
|
|
|
// on home screen
|
|
if (currentScreen == 0) {
|
|
if (editMode == 0) {
|
|
if (currentPos > previousPos) {
|
|
mainScreenCursorPos++;
|
|
if (mainScreenCursorPos > maxMainScreenPos) {
|
|
mainScreenCursorPos = 0;
|
|
}
|
|
} else {
|
|
mainScreenCursorPos--;
|
|
if (mainScreenCursorPos < 0) {
|
|
mainScreenCursorPos = maxMainScreenPos;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|