clock_master/master_clock.ino

213 lines
4.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;
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) {
BPM++;
} else {
BPM--;
}
forceScreenUpdate = 1;
previousPos = currentPos;
}
rPotLastUpdate = millis(); // Reset the timer for throttling the output
}
}
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
}
}