mirror of
https://github.com/KevinMidboe/oled_workout_tracker.git
synced 2025-10-29 01:30:29 +00:00
Merge pull request #3 from KevinMidboe/feat/rotary-encoder
Feat/rotary encoder
This commit is contained in:
@@ -14,8 +14,7 @@ From here search for `Adafruit SSD1306`, this should prompt to also download `Ad
|
||||
|
||||
## Hardware
|
||||
- OLED screen: Displays the current number of repetitions.
|
||||
- Potentiometer: Setting value to display.
|
||||
- Button: Change from repetition view mode to a menu view using `digitalPinToInterrupt`.
|
||||
- Rotary encoder: Count steps in both directions and has a button used to change modes.
|
||||
|
||||
## Software
|
||||
- Timeout: Any pot input has 1 second of timeout since last value change. After timeout runs out the value locks and a value of ± 2 must be read to enable inputs and reset the cooldown.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
72
rotaryEncoder/rotaryEncoder.ino
Normal file
72
rotaryEncoder/rotaryEncoder.ino
Normal file
@@ -0,0 +1,72 @@
|
||||
// Rotary Encoder Inputs
|
||||
#define CLK 2
|
||||
#define DT 3
|
||||
#define SW 4
|
||||
|
||||
int counter = 0;
|
||||
int currentStateCLK;
|
||||
int lastStateCLK;
|
||||
String currentDir ="";
|
||||
unsigned long lastButtonPress = 0;
|
||||
|
||||
void setup() {
|
||||
|
||||
// Set encoder pins as inputs
|
||||
pinMode(CLK,INPUT);
|
||||
pinMode(DT,INPUT);
|
||||
pinMode(SW, INPUT_PULLUP);
|
||||
|
||||
// Setup Serial Monitor
|
||||
Serial.begin(9600);
|
||||
|
||||
// Read the initial state of CLK
|
||||
lastStateCLK = digitalRead(CLK);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
// Read the current state of CLK
|
||||
currentStateCLK = digitalRead(CLK);
|
||||
|
||||
// If last and current state of CLK are different, then pulse occurred
|
||||
// React to only 1 state change to avoid double count
|
||||
if (currentStateCLK != lastStateCLK && currentStateCLK == 1){
|
||||
|
||||
// If the DT state is different than the CLK state then
|
||||
// the encoder is rotating CCW so decrement
|
||||
if (digitalRead(DT) != currentStateCLK) {
|
||||
counter --;
|
||||
currentDir ="CCW";
|
||||
} else {
|
||||
// Encoder is rotating CW so increment
|
||||
counter ++;
|
||||
currentDir ="CW";
|
||||
}
|
||||
|
||||
Serial.print("Direction: ");
|
||||
Serial.print(currentDir);
|
||||
Serial.print(" | Counter: ");
|
||||
Serial.println(counter);
|
||||
}
|
||||
|
||||
// Remember last CLK state
|
||||
lastStateCLK = currentStateCLK;
|
||||
|
||||
// Read the button state
|
||||
int btnState = digitalRead(SW);
|
||||
|
||||
//If we detect LOW signal, button is pressed
|
||||
if (btnState == LOW) {
|
||||
//if 50ms have passed since last LOW pulse, it means that the
|
||||
//button has been pressed, released and pressed again
|
||||
if (millis() - lastButtonPress > 50) {
|
||||
Serial.println("Button pressed!");
|
||||
}
|
||||
|
||||
// Remember last button press event
|
||||
lastButtonPress = millis();
|
||||
}
|
||||
|
||||
// Put in a slight delay to help debounce the reading
|
||||
delay(1);
|
||||
}
|
||||
@@ -30,19 +30,25 @@
|
||||
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
|
||||
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
||||
|
||||
#define NUMFLAKES 10 // Number of snowflakes in the animation example
|
||||
#define CLK 2
|
||||
#define DT 3
|
||||
#define SW 4
|
||||
|
||||
// input pin definition
|
||||
int buttonModePin = 2;
|
||||
int potPin = 2;
|
||||
int counter = 0;
|
||||
int currentStateCLK;
|
||||
int lastStateCLK;
|
||||
String currentDir ="";
|
||||
unsigned long lastButtonPress = 0;
|
||||
|
||||
bool mode = false;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Setup button interrupt for changing modes
|
||||
pinMode(buttonModePin, INPUT_PULLUP);
|
||||
attachInterrupt(digitalPinToInterrupt(2), changeMode, FALLING);
|
||||
// Set encoder pins as inputs
|
||||
pinMode(CLK,INPUT);
|
||||
pinMode(DT,INPUT);
|
||||
pinMode(SW, INPUT_PULLUP);
|
||||
|
||||
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
|
||||
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
|
||||
@@ -50,6 +56,9 @@ void setup() {
|
||||
for(;;); // Don't proceed, loop forever
|
||||
}
|
||||
|
||||
// Read the initial state of CLK
|
||||
lastStateCLK = digitalRead(CLK);
|
||||
|
||||
// Clear the buffer
|
||||
display.clearDisplay();
|
||||
display.setTextColor(SSD1306_WHITE);
|
||||
@@ -70,6 +79,34 @@ void displayFromSerialInput() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean readEncoderAndUpdateCounter() {
|
||||
currentStateCLK = digitalRead(CLK);
|
||||
|
||||
// If last and current state of CLK are different, then pulse occurred
|
||||
// React to only 1 state change to avoid double count
|
||||
if (currentStateCLK != lastStateCLK && currentStateCLK == 1){
|
||||
|
||||
// If the DT state is different than the CLK state then
|
||||
// the encoder is rotating CCW so decrement
|
||||
if (digitalRead(DT) != currentStateCLK) {
|
||||
counter --;
|
||||
currentDir ="CCW";
|
||||
} else {
|
||||
// Encoder is rotating CW so increment
|
||||
counter ++;
|
||||
currentDir ="CW";
|
||||
}
|
||||
|
||||
lastStateCLK = currentStateCLK;
|
||||
return true;
|
||||
}
|
||||
lastStateCLK = currentStateCLK;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void drawHeader(String textToDisplay) {
|
||||
display.setTextSize(1);
|
||||
display.setCursor(2,0);
|
||||
@@ -83,68 +120,44 @@ void drawText(String textToDisplay) {
|
||||
}
|
||||
|
||||
void drawPushups(int count) {
|
||||
Serial.print("refreshing display w/ value: ");
|
||||
Serial.println(count);
|
||||
|
||||
display.clearDisplay();
|
||||
drawHeader("Daily push-ups:");
|
||||
drawText(String(count));
|
||||
display.display();
|
||||
}
|
||||
|
||||
int positionBuffer = 10;
|
||||
int getPotValue() {
|
||||
return analogRead(potPin) / positionBuffer;
|
||||
}
|
||||
|
||||
void checkButtonState() {
|
||||
int btnState = digitalRead(SW);
|
||||
|
||||
bool mode = false;
|
||||
//If we detect LOW signal, button is pressed
|
||||
if (btnState == LOW) {
|
||||
//if 50ms have passed since last LOW pulse, it means that the
|
||||
//button has been pressed, released and pressed again
|
||||
if (millis() - lastButtonPress > 50) {
|
||||
Serial.println("Button pressed!");
|
||||
mode = !mode;
|
||||
|
||||
int pos = 0;
|
||||
int currentPos;
|
||||
bool active;
|
||||
|
||||
unsigned long lastEvent = 0;
|
||||
unsigned long currentTime;
|
||||
const long timeout = 1000;
|
||||
void displayNumberWithPotValue() {
|
||||
currentPos = getPotValue();
|
||||
if (currentPos < pos - 2 || currentPos > pos + 2) {
|
||||
active = true;
|
||||
}
|
||||
|
||||
while (active && mode == false) {
|
||||
currentTime = millis();
|
||||
if (currentTime - lastEvent > timeout) {
|
||||
active = false;
|
||||
if (mode == false) {
|
||||
drawPushups(counter);
|
||||
} else {
|
||||
displayMenu();
|
||||
}
|
||||
}
|
||||
|
||||
currentPos = getPotValue();
|
||||
if (currentPos != pos) {
|
||||
lastEvent = currentTime;
|
||||
pos = currentPos;
|
||||
drawPushups(pos);
|
||||
}
|
||||
delay(10);
|
||||
|
||||
// Remember last button press event
|
||||
lastButtonPress = millis();
|
||||
}
|
||||
}
|
||||
|
||||
String menuLookup[4] = { "daily", "weekly", "goal" };
|
||||
|
||||
void displayMenuSelect() {
|
||||
currentPos = analogRead(potPin) / 257; // split in four (1028/4)
|
||||
|
||||
if (pos == currentPos) {
|
||||
return;
|
||||
}
|
||||
|
||||
pos = currentPos;
|
||||
void displayMenu() {
|
||||
display.clearDisplay();
|
||||
display.setTextSize(1);
|
||||
display.setCursor(0,0);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (pos == i) {
|
||||
if (counter == i) {
|
||||
display.print("- ");
|
||||
} else {
|
||||
display.print(" ");
|
||||
@@ -154,26 +167,28 @@ void displayMenuSelect() {
|
||||
display.display();
|
||||
}
|
||||
|
||||
bool beingPressed = false;
|
||||
void changeMode() {
|
||||
if (beingPressed == true) {
|
||||
return;
|
||||
void displayCounterValue() {
|
||||
if (readEncoderAndUpdateCounter()) {
|
||||
drawPushups(counter);
|
||||
}
|
||||
Serial.println("CHANGING MODE");
|
||||
delay(1);
|
||||
}
|
||||
|
||||
beingPressed = true;
|
||||
mode = !mode;
|
||||
delay(800);
|
||||
beingPressed = false;
|
||||
int menuPos = 0;
|
||||
void displayMenuSelect() {
|
||||
if (readEncoderAndUpdateCounter()) {
|
||||
displayMenu();
|
||||
}
|
||||
delay(1);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// displayFromSerialInput();
|
||||
|
||||
if (mode == false) {
|
||||
displayNumberWithPotValue();
|
||||
displayCounterValue();
|
||||
} else {
|
||||
displayMenuSelect();
|
||||
}
|
||||
delay(10);
|
||||
checkButtonState();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user