11#include " ky040.h"
22#include " ../config/pins.h"
33
4+ // State table could be used, but bit manipulation is also efficient.
45RotaryEncoder::RotaryEncoder () {
5- swPin = ROTARY_SW ;
6- position = 0 ;
6+ pinCLK = ROTARY_CLK ;
7+ pinDT = ROTARY_DT ;
8+ pinSW = ROTARY_SW ;
9+
10+ lastEncoded = 0 ;
11+ encoderDelta = 0 ;
12+
13+ buttonState = HIGH ; // Input pullup defaults high
14+ lastButtonState = HIGH ;
715 buttonPressed = false ;
8- lastButtonTime = 0 ;
9- buttonPressStart = 0 ;
10- lastButtonState = false ;
11- lastSwState = HIGH ;
16+ buttonReleased = false ;
17+ longPressActive = false ;
18+
1219 lastDebounceTime = 0 ;
13- encoder = nullptr ;
20+ buttonPressTime = 0 ;
1421}
1522
1623RotaryEncoder::~RotaryEncoder () {
17- if (encoder) {
18- delete encoder;
19- }
24+ // modifying pins isn't really needed in destructor
2025}
2126
2227void RotaryEncoder::begin () {
23- // Initialize pins
24- pinMode (ROTARY_CLK , INPUT_PULLUP );
25- pinMode (ROTARY_DT , INPUT_PULLUP );
26- pinMode (swPin, INPUT_PULLUP );
28+ pinMode (pinCLK, INPUT_PULLUP );
29+ pinMode (pinDT, INPUT_PULLUP );
30+ pinMode (pinSW, INPUT_PULLUP );
31+
32+ // Read initial state
33+ int MSB = digitalRead (pinDT);
34+ int LSB = digitalRead (pinCLK);
2735
28- encoder = new KY040 (ROTARY_CLK , ROTARY_DT );
36+ // Combine to get 2-bit state (0-3)
37+ lastEncoded = (MSB << 1 ) | LSB ;
2938}
3039
3140void RotaryEncoder::update () {
32- if (!encoder) return ;
41+ // --- 1. Encoder Logic (State Machine) ---
42+ int MSB = digitalRead (pinDT);
43+ int LSB = digitalRead (pinCLK);
44+ int encoded = (MSB << 1 ) | LSB ;
3345
34- // Get rotation state and update position automatically
35- byte rotation = encoder->getRotation ();
46+ // Current state (encoded) and previous state (lastEncoded) form a 4-bit number
47+ // sum = 0b[Old_MSB][Old_LSB][New_MSB][New_LSB]
48+ int sum = (lastEncoded << 2 ) | encoded;
3649
37- if (rotation == KY040 ::CLOCKWISE ) {
38- position++;
39- } else if (rotation == KY040 ::COUNTERCLOCKWISE ) {
40- position--;
50+ // Verify validity using the "sum" transition
51+ // Valid CW transitions: 0b0010 (2), 0b1011 (11), 0b1101 (13), 0b0100 (4)
52+ // Valid CCW transitions: 0b0001 (1), 0b0111 (7), 0b1110 (14), 0b1000 (8)
53+
54+ if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011 ) {
55+ encoderDelta++;
56+ } else if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000 ) {
57+ encoderDelta--;
4158 }
59+
60+ lastEncoded = encoded;
4261
43- int reading = digitalRead (swPin);
62+ // --- 2. Button Logic (Non-blocking Debounce) ---
63+ int reading = digitalRead (pinSW);
4464 unsigned long currentTime = millis ();
45-
46- // Check if the button state has changed
47- if (reading != lastSwState ) {
65+
66+ // Reset debounce timer if reading changed (noise or actual press)
67+ if (reading != lastButtonState ) {
4868 lastDebounceTime = currentTime;
4969 }
5070
51- // Only register the change if it's been stable for the debounce delay
71+ // Check if the state has been stable for the debounce delay
5272 if ((currentTime - lastDebounceTime) > DEBOUNCE_DELAY ) {
53- // If the button state has actually changed
54- if (reading != (buttonPressed ? LOW : HIGH )) {
55- if (reading == LOW ) {
56- // Button pressed
57- buttonPressed = true ;
58- buttonPressStart = currentTime;
73+
74+ // If the state has changed
75+ if (reading != buttonState) {
76+ buttonState = reading;
77+
78+ // Logic: Button is Active LOW
79+ if (buttonState == LOW ) {
80+ // Just Pressed
81+ buttonPressTime = currentTime;
82+ longPressActive = false ; // Reset long press flag
83+ // buttonPressed = true; // If we wanted press-trigger
5984 } else {
60- // Button released
61- buttonPressed = false ;
85+ // Just Released
86+ // Only trigger release if it wasn't a long press
87+ if (!longPressActive) {
88+ buttonReleased = true ;
89+ }
90+ }
91+ }
92+
93+ // Update last stable state only after debounce confirmation?
94+ // Actually, we usually update lastButtonState immediately on change to sniff noise.
95+ // But here we want the 'logic' state.
96+
97+ // Handle Long Press while held down
98+ if (buttonState == LOW && !longPressActive) {
99+ if ((currentTime - buttonPressTime) > LONG_PRESS_DELAY ) {
100+ longPressActive = true ;
101+ // Force exit or action immediately on long press detection?
102+ // Or leave it to the getter to check
62103 }
63104 }
64105 }
65106
66- lastSwState = reading;
67- }
68-
69- int RotaryEncoder::getPosition () {
70- return position;
107+ lastButtonState = reading;
71108}
72109
73- void RotaryEncoder::resetPosition () {
74- position = 0 ;
110+ int RotaryEncoder::getDelta () {
111+ int d = encoderDelta;
112+ encoderDelta = 0 ; // Reset after reading to consume the event
113+ return d;
75114}
76115
77116bool RotaryEncoder::isButtonPressed () {
78- return buttonPressed;
117+ // Not using this for the menu, relying on release, but keeping for API drift
118+ if (buttonPressed) {
119+ buttonPressed = false ;
120+ return true ;
121+ }
122+ return false ;
79123}
80124
81125bool RotaryEncoder::isButtonReleased () {
82- bool released = lastButtonState && !buttonPressed;
83- lastButtonState = buttonPressed;
84- return released;
126+ if (buttonReleased) {
127+ buttonReleased = false ;
128+ return true ;
129+ }
130+ return false ;
85131}
86132
87133bool RotaryEncoder::isLongPress () {
88- if (buttonPressed) {
89- return (millis () - buttonPressStart) > 1000 ;
134+ // We return true continuously or once?
135+ // Usually once is safer for menus.
136+ if (longPressActive) {
137+ // To prevent spamming long press, we can reset it,
138+ // OR we can rely on the caller to handle repeating.
139+ // Let's make it return true ONCE per press-hold.
140+ longPressActive = false ; // Consume the event
141+ // Also suppress the release event that will come later
142+ return true ;
90143 }
91144 return false ;
92145}
0 commit comments