mirror of
https://github.com/KevinMidboe/oled_workout_tracker.git
synced 2025-12-29 05:11:07 +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
|
## Hardware
|
||||||
- OLED screen: Displays the current number of repetitions.
|
- OLED screen: Displays the current number of repetitions.
|
||||||
- Potentiometer: Setting value to display.
|
- Rotary encoder: Count steps in both directions and has a button used to change modes.
|
||||||
- Button: Change from repetition view mode to a menu view using `digitalPinToInterrupt`.
|
|
||||||
|
|
||||||
## Software
|
## 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.
|
- 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)
|
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
|
||||||
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
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 counter = 0;
|
||||||
int buttonModePin = 2;
|
int currentStateCLK;
|
||||||
int potPin = 2;
|
int lastStateCLK;
|
||||||
|
String currentDir ="";
|
||||||
|
unsigned long lastButtonPress = 0;
|
||||||
|
|
||||||
|
bool mode = false;
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
|
|
||||||
// Setup button interrupt for changing modes
|
// Set encoder pins as inputs
|
||||||
pinMode(buttonModePin, INPUT_PULLUP);
|
pinMode(CLK,INPUT);
|
||||||
attachInterrupt(digitalPinToInterrupt(2), changeMode, FALLING);
|
pinMode(DT,INPUT);
|
||||||
|
pinMode(SW, INPUT_PULLUP);
|
||||||
|
|
||||||
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
|
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
|
||||||
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
|
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
|
||||||
@@ -50,6 +56,9 @@ void setup() {
|
|||||||
for(;;); // Don't proceed, loop forever
|
for(;;); // Don't proceed, loop forever
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read the initial state of CLK
|
||||||
|
lastStateCLK = digitalRead(CLK);
|
||||||
|
|
||||||
// Clear the buffer
|
// Clear the buffer
|
||||||
display.clearDisplay();
|
display.clearDisplay();
|
||||||
display.setTextColor(SSD1306_WHITE);
|
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) {
|
void drawHeader(String textToDisplay) {
|
||||||
display.setTextSize(1);
|
display.setTextSize(1);
|
||||||
display.setCursor(2,0);
|
display.setCursor(2,0);
|
||||||
@@ -83,68 +120,44 @@ void drawText(String textToDisplay) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void drawPushups(int count) {
|
void drawPushups(int count) {
|
||||||
Serial.print("refreshing display w/ value: ");
|
|
||||||
Serial.println(count);
|
|
||||||
|
|
||||||
display.clearDisplay();
|
display.clearDisplay();
|
||||||
drawHeader("Daily push-ups:");
|
drawHeader("Daily push-ups:");
|
||||||
drawText(String(count));
|
drawText(String(count));
|
||||||
display.display();
|
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;
|
if (mode == false) {
|
||||||
int currentPos;
|
drawPushups(counter);
|
||||||
bool active;
|
} else {
|
||||||
|
displayMenu();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPos = getPotValue();
|
// Remember last button press event
|
||||||
if (currentPos != pos) {
|
lastButtonPress = millis();
|
||||||
lastEvent = currentTime;
|
|
||||||
pos = currentPos;
|
|
||||||
drawPushups(pos);
|
|
||||||
}
|
|
||||||
delay(10);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String menuLookup[4] = { "daily", "weekly", "goal" };
|
String menuLookup[4] = { "daily", "weekly", "goal" };
|
||||||
|
void displayMenu() {
|
||||||
void displayMenuSelect() {
|
|
||||||
currentPos = analogRead(potPin) / 257; // split in four (1028/4)
|
|
||||||
|
|
||||||
if (pos == currentPos) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = currentPos;
|
|
||||||
display.clearDisplay();
|
display.clearDisplay();
|
||||||
display.setTextSize(1);
|
display.setTextSize(1);
|
||||||
display.setCursor(0,0);
|
display.setCursor(0,0);
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
if (pos == i) {
|
if (counter == i) {
|
||||||
display.print("- ");
|
display.print("- ");
|
||||||
} else {
|
} else {
|
||||||
display.print(" ");
|
display.print(" ");
|
||||||
@@ -154,26 +167,28 @@ void displayMenuSelect() {
|
|||||||
display.display();
|
display.display();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool beingPressed = false;
|
void displayCounterValue() {
|
||||||
void changeMode() {
|
if (readEncoderAndUpdateCounter()) {
|
||||||
if (beingPressed == true) {
|
drawPushups(counter);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Serial.println("CHANGING MODE");
|
delay(1);
|
||||||
|
}
|
||||||
|
|
||||||
beingPressed = true;
|
int menuPos = 0;
|
||||||
mode = !mode;
|
void displayMenuSelect() {
|
||||||
delay(800);
|
if (readEncoderAndUpdateCounter()) {
|
||||||
beingPressed = false;
|
displayMenu();
|
||||||
|
}
|
||||||
|
delay(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
// displayFromSerialInput();
|
// displayFromSerialInput();
|
||||||
|
|
||||||
if (mode == false) {
|
if (mode == false) {
|
||||||
displayNumberWithPotValue();
|
displayCounterValue();
|
||||||
} else {
|
} else {
|
||||||
displayMenuSelect();
|
displayMenuSelect();
|
||||||
}
|
}
|
||||||
delay(10);
|
checkButtonState();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user