//#include <U8g2lib.h>
//#include "U8glib.h"
#define ENCODER_CLK 2
#define ENCODER_DT 3
#define ENCODER_BTN 4
const int pinR = 11;
const int pinG = 10;
const int pinB = 9;
int pwmR = 0;
int pwmG = 0;
int pwmB = 0;
int t = 50; //for fade delay
int selectedItem = 0;
int colorIndex = 0;
//U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK); // For Wokwi testing.
//U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK); // For hardware version.
// Set the time since the push button was last pressed (for debouncing):
long int btnLastPressed = 0;
// Last value for the rotary encoder:
int lastPos = 128;
volatile int encoderPos = 128; // readEncoder() updates this value.
/* When the Arduino starts, both lastPos and encoderPos are the same. Rotating the encoder
changes the value of encoderPos so they no longer match, which the Arduino can detect in
the main loop and check which direction it's rotating. */
// ISR for the rotary encoder:
void readEncoder() {
static unsigned long lastInterruptTime = 0;
unsigned long interruptTime = millis();
int dtValue = digitalRead(ENCODER_DT);
// 10 millisecond delay for debounce:
if (interruptTime - lastInterruptTime > 10) {
if (dtValue == HIGH) {
encoderPos++;
}
if (dtValue == LOW) {
encoderPos--;
}
// Set a virtual min/max for the encoder value, between 0 and 255:
encoderPos = min(255, max(0, encoderPos));
lastInterruptTime = interruptTime;
}
}
// Function to change between colour channels:
// Function to update the value of the channel:
void setColor(int colorIndex, int encoderPos) {
int pwmVal = 128;
if (encoderPos != lastPos) {
if (encoderPos > lastPos) {
pwmVal++;
} else {
pwmVal--;
}
lastPos = encoderPos; // Update the lastPos with the current encoderPos.
}
int pin;
if (colorIndex == 1) {
pin = pinR;
} else if (colorIndex == 2) {
pin = pinB;
} else if (colorIndex == 3) {
pin = pinG;
}
analogWrite(pin, pwmVal);
}
// Function to update the OLED display:
void fade() {
for(int i = 0; i <= 255; i++){
analogWrite(pinR, i); // fade up
analogWrite(pinG, 255 - i); // fade down
analogWrite(pinB, 0); // do nothing
delay(t);
}
for(int i = 0; i <= 255; i++){
analogWrite(pinR, 255 - i); // fade down
analogWrite(pinG, 0); // do nothing
analogWrite(pinB, i); // fade up
delay(t);
}
for(int i = 0; i <= 255; i++){
analogWrite(pinR, 0); // do nothing
analogWrite(pinG, i); // fade up
analogWrite(pinB, 255 - i); // fade down
delay(t);
}
}
/*
void setRGB(int pwmR, int pwmG, int pwmB)
{
analogWrite(pinR, pwmR);
analogWrite(pinG, pwmG);
analogWrite(pinB, pwmB);
}
*/
void setup() {
Serial.begin(9600);
pinMode(ENCODER_BTN, INPUT_PULLUP);
pinMode(ENCODER_CLK, INPUT);
pinMode(ENCODER_DT, INPUT);
attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), readEncoder, FALLING);
pinMode(pinR, OUTPUT);
pinMode(pinG, OUTPUT);
pinMode(pinB, OUTPUT);
//u8g.setFont(u8g_font_tpssb);
//u8g.setColorIndex(1); // Set colour to white.
//u8g.print("Hello. The current value of the encoder is ");
//u8g.println(encoderPos);
Serial.print("Hello. The current value of the encoder is ");
Serial.println(encoderPos);
Serial.print("Selected Option - ");
Serial.println(selectedItem);
}
void loop() {
/*
u8g.firstPage();
do {
/* It appears that writing the value of variables to the display requires first putting those values into
a buffer, then sending that buffer to the display. More research is needed on why this is the case, and how
to send data from different sources to the display.
enum {bufSize=9};
char buf[bufSize];
snprintf(buf, bufSize, "%d", encoderPos);
u8g.drawStr(40, 25, "Encoder");
u8g.drawStr(55, 40, buf);
u8g.drawFrame(0, 0, 128, 10);
u8g.drawBox(2, 2, map(encoderPos, 0, 255, 0, 124), 6);
} while ( u8g.nextPage() );
*/
// Make the encoder push button do something, but only if it hasn't been pushed in the last 300 milliseconds.
if (digitalRead(ENCODER_BTN) == LOW && millis() - btnLastPressed > 300) {
btnLastPressed = millis();
Serial.print("Button Pressed. Selected Option ");
Serial.println(selectedItem);
selectedItem++;
Serial.print("Encoder Position ");
Serial.println(encoderPos);
}
// If encoderPos and lastPos are different, that means the encoder has moved:
/*if (encoderPos != lastPos) {
/* If the value of encoderPos is greater than the value of lastPos, write "CW:", otherwise write "CCW".
The syntax here is a shorthand for a basic if statement, see "ternary operator".
e.g. "condition ? do this : else do this"
Serial.print(encoderPos > lastPos ? "CW: \t" : "CCW: \t");
Serial.println(encoderPos); // Write the position as a value to the serial monitor for now.
lastPos = encoderPos; // Update the lastPos with the current encoderPos.
//pwmR = encoderPos;
}*/
//setRGB(0, 255, 255);
//fade();
if (selectedItem > 3) {
selectedItem = 0;
}
switch (selectedItem) {
case 1:
setColor(1, encoderPos);
break;
case 2:
setColor(2, encoderPos);
break;
case 3:
setColor(3, encoderPos);
break;
}
}
/* Some pseudo code
Instead of making the menu set the value, make the encoder set the value separately, and the screen just show the value of the variable.
Have a look at https://wokwi.com/projects/304919215794553409 for an example of adjusting three values using an encoder.
*/