#include <Arduino.h>
// #include <LedManager.h>
// INSERT LOGGER CPP HERE (REQUIRED FOR WOKWI ONLY) START
enum LogLevel {
UNSET,
NONE,
DEBUG,
LOG,
INFO,
WARN,
ERROR,
};
class Logger {
public:
LogLevel level = UNSET;
static Logger& getInstance(LogLevel newLevel = UNSET) {
static Logger instance(newLevel); // Single instance of the class
return instance;
}
Logger(LogLevel newLevel) {
if (newLevel != UNSET) {
this->level = newLevel;
}
else {
this->level = DEBUG;
}
}
void write(String str) {
if (Serial) {
Serial.println(str);
}
}
template<typename... Args>
void debug(const char* separator, Args&&... args) {
if (!(this->level >= DEBUG)) {
return;
}
String combinedOutput = "[DEBUG] ";
((combinedOutput += String(args) + separator), ...);
combinedOutput.remove(combinedOutput.length() - strlen(separator)); // Remove the trailing separator
this->write(combinedOutput);
}
template<typename... Args>
void log(const char* separator, Args&&... args) {
if (!(this->level >= LOG)) {
return;
}
String combinedOutput = "[LOG] ";
((combinedOutput += String(args) + separator), ...);
combinedOutput.remove(combinedOutput.length() - strlen(separator)); // Remove the trailing separator
this->write(combinedOutput);
}
template<typename... Args>
void info(const char* separator, Args&&... args) {
if (!(this->level >= INFO)) {
return;
}
String combinedOutput = "[INFO] ";
((combinedOutput += String(args) + separator), ...);
combinedOutput.remove(combinedOutput.length() - strlen(separator)); // Remove the trailing separator
this->write(combinedOutput);
}
template<typename... Args>
void warn(const char* separator, Args&&... args) {
if (!(this->level >= WARN)) {
return;
}
String combinedOutput = "[WARN] ";
((combinedOutput += String(args) + separator), ...);
combinedOutput.remove(combinedOutput.length() - strlen(separator)); // Remove the trailing separator
this->write(combinedOutput);
}
template<typename... Args>
void error(const char* separator, Args&&... args) {
if (!(this->level >= ERROR)) {
return;
}
String combinedOutput = "[ERROR] ";
((combinedOutput += String(args) + separator), ...);
combinedOutput.remove(combinedOutput.length() - strlen(separator)); // Remove the trailing separator
this->write(combinedOutput);
}
};
// INSERT LOGGER CPP HERE (REQUIRED FOR WOKWI ONLY) END
// INSERT LEDMANAGER CPP HERE (REQUIRED FOR WOKWI ONLY) START
// INSERT LEDMANAGER CPP HERE (REQUIRED FOR WOKWI ONLY) END
class RotaryEncoder {
public:
uint8_t aPin;
uint8_t bPin;
uint8_t buttonPin = -1;
using Callback = void (*)();
Callback onInterrupt;
Callback onButtonPress = nullptr;
Logger& logger;
int range[2] = { 0, 100 };
int value = 0;
// aPin (CLK) Primary pulse. Pin must support interrupt; https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/.
// bPin (DS) Secondary pulse.
// onInterrupt needs to call handleInterrupt because the fucking member methods cant be used as callbacks
RotaryEncoder(uint8_t aPin, uint8_t bPin, int range[2], uint8_t buttonPin = -1) : logger(Logger::getInstance()) {
this->aPin = aPin;
this->bPin = bPin;
if (digitalPinToPort(bPin) == NOT_A_PIN) {
this->logger.warn(" ", "bPin is not a valid pin;", bPin);
this->bPin = -1;
}
if (buttonPin > -1) {
if (digitalPinToPort(buttonPin) == NOT_A_PIN) {
this->logger.warn(" ", "A buttonPin was set but it is not a valid pin;", buttonPin);
this->buttonPin = -1;
}
else {
this->buttonPin = buttonPin;
}
}
if (range[0] > range[1]) {
this->logger.warn(" ", "lower range is bigger than upper range. Setting range to default.");
}
else {
this->range[0] = range[0];
this->range[1] = range[1];
}
// if (onInterrupt != nullptr) {
// int interrupt = digitalPinToInterrupt(aPin);
// if (interrupt == NOT_AN_INTERRUPT) {
// this->logger.warn(" ", "provided aPin", aPin, "does not support the interrupt feature.");
// this->aPin = -1;
// }
// else {
// attachInterrupt(interrupt, this->onInterrupt, RISING);
// }
// }
// else {
// this->logger.warn(" ", "onInterrupt not set");
// }
}
void setInterrupt(Callback onInterrupt) {
if (onInterrupt != nullptr) {
int interrupt = digitalPinToInterrupt(this->aPin);
this->onInterrupt = onInterrupt;
if (interrupt == NOT_AN_INTERRUPT) {
this->logger.warn(" ", "provided aPin", this->aPin, "does not support the interrupt feature.");
this->aPin = -1;
}
else {
this->logger.debug(" ", "set");
attachInterrupt(interrupt, onInterrupt, RISING);
}
}
else {
this->logger.warn(" ", "onInterrupt not set");
}
}
bool increaseValue() {
if (this->value >= this->range[1]) {
this->logger.debug(" ", "value reached the upper constraint");
return false;
}
this->value += 1;
return true;
}
bool decreaseValue() {
if (this->range[0] >= this->value) {
this->logger.debug(" ", "value reached the lower constraint");
return false;
}
this->value -= 1;
return true;
}
void handleButton() {
if (this->buttonPin == -1) {
this->logger.warn(" ", "handleButton is being called without a valid buttonPin");
return;
}
}
bool handleInterrupt() {
if (this->aPin == -1 || this->bPin == -1) {
this->logger.warn(" ", "handleInterrupt is being called without a valid aPin or bPin");
return;
}
int aState = digitalRead(this->aPin);
int bState = digitalRead(this->bPin);
this->logger.debug(" - ", aState, bState);
if (aState != bState) {
return this->increaseValue();
}
else {
return this->decreaseValue();
}
}
};
Logger logger(DEBUG);
int encoderRange[2] = { 0, 100 };
RotaryEncoder encoder1(2, 5, encoderRange);
void onRotate1() {
bool didChange = encoder1.handleInterrupt();
Serial.println(encoder1.value);
// TODO midi
}
void setup() {
Serial.begin(9600);
encoder1.setInterrupt(onRotate1);
// onRotate(range);
// for (int i = 0; i < 4; i++) {
// const int idx = i;
// RotaryEncoder encoder(2, -3, range);
// }
// RotaryEncoder encoder(2, -3, onRotate, range);
// RotaryEncoder rotary(15,2, onRotate);
// RotaryEncoder rotary(15,2, nullptr);
// LedManager ledManager(2,3,4);
// uint8_t myArray[] = {1, 16};
// ledManager.turnOnLEDs(myArray, sizeof(myArray) / sizeof(myArray[0]));
}
void loop() {
// put your main code here, to run repeatedly:
}