From 2225b58c081b8e61e09b33adc970af668adfcb68 Mon Sep 17 00:00:00 2001 From: Grzegorz Date: Wed, 12 Feb 2025 18:34:22 +0100 Subject: [PATCH 1/2] Modify encoder logic to use interrupts; add faceplate for bigger screen; add hat for smaller encoder --- .../Faceplate-bigger-screen.stl | Bin 0 -> 75684 bytes .../hat-smaller-encoder.stl | Bin 0 -> 145084 bytes sketch_sep25a.ino | 115 ++++++++++-------- 3 files changed, 63 insertions(+), 52 deletions(-) create mode 100644 3D Parts/Bigger screen faceplate/Faceplate-bigger-screen.stl create mode 100644 3D Parts/Smaller encoder hat/hat-smaller-encoder.stl diff --git a/3D Parts/Bigger screen faceplate/Faceplate-bigger-screen.stl b/3D Parts/Bigger screen faceplate/Faceplate-bigger-screen.stl new file mode 100644 index 0000000000000000000000000000000000000000..8fc23974a87552e101836b2096e84f714685433e GIT binary patch literal 75684 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πRH-GDR4o!|=@Unf)!K>%&-+lF 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 Bry~FW literal 0 HcmV?d00001 diff --git a/sketch_sep25a.ino b/sketch_sep25a.ino index 594153c..81f0f3d 100644 --- a/sketch_sep25a.ino +++ b/sketch_sep25a.ino @@ -1,5 +1,6 @@ #include #include +#include //----------------------------------------------- Adafruit_SSD1306 display(128, 64, &Wire, D4); @@ -9,6 +10,12 @@ Adafruit_SSD1306 display(128, 64, &Wire, D4); #define DT D7 #define SW D4 + +KY040 g_rotaryEncoder(CLK,DT); +// Set a flag to indicate that the display should be updated +volatile bool update_display = false; + + //----------------------------------------------- int flowMinutes = 0; // Total flow minutes int menuIndex = 0; // 0 for UP, 1 for DOWN, 2 for Reset @@ -48,9 +55,8 @@ void setup() { //========================================================= void loop() { unsigned long currentMillis = millis(); - - // Handle rotary encoder input - handleRotaryInput(); + + handleScreenUpdate(); // Handle button presses and states handleButtonPresses(currentMillis); @@ -65,9 +71,10 @@ void loop() { //========================================================= // Initialize hardware pins and serial communication void initHardware() { - pinMode(CLK, INPUT); - pinMode(DT, INPUT); pinMode(SW, INPUT); + // Set interrupts for CLK and DT + attachInterrupt(digitalPinToInterrupt(CLK), ISR_rotaryEncoder, CHANGE); + attachInterrupt(digitalPinToInterrupt(DT), ISR_rotaryEncoder, CHANGE); Serial.begin(9600); } @@ -282,47 +289,36 @@ void successAnimation() { } //========================================================= -// Rotary Encoder Rotation Detection -int getRotation() { - static int previousCLK = digitalRead(CLK); - int currentCLK = digitalRead(CLK); - - if (currentCLK == LOW && previousCLK == HIGH && (millis() - lastRotaryTime > rotaryDebounceDelay)) { - lastRotaryTime = millis(); // Debounce - int DTValue = digitalRead(DT); // Read DT to determine direction - - previousCLK = currentCLK; // Update previous CLK for next iteration - - return (DTValue != currentCLK) ? 1 : -1; // Clockwise or counterclockwise +// ISR to handle the interrupts for CLK and DT +ICACHE_RAM_ATTR void ISR_rotaryEncoder() { + if (currentState == IDLE) { + exitIdle(); } - previousCLK = currentCLK; - return 0; // No rotation -} - -//========================================================= -// Handle rotary input for menu and countdown selection -void handleRotaryInput() { - int rotation = getRotation(); - if (rotation == 0) return; // No rotation detected + int rotation = 0; + // Process pin states for CLK and DT + switch (g_rotaryEncoder.getRotation()) { + case KY040::CLOCKWISE: + rotation = 1; + break; + case KY040::COUNTERCLOCKWISE: + rotation = -1; + break; + } + if (rotation == 0) { + return; + } - lastActivityTime = millis(); // Reset inactivity timer on any valid rotation - Serial.print(millis()); // Print the current time in milliseconds - Serial.print(" - Rotation detected, activity timer reset. Rotation: "); - Serial.println(rotation); - if (currentState == MENU) { menuIndex = (menuIndex + rotation + 3) % 3; // Update for 3 menu options: UP, DOWN, Reset - updateDisplay(); - Serial.print(millis()); // Print the current time in milliseconds - Serial.print(" - Menu option: "); Serial.println(menuOptions[menuIndex]); - } else if (currentState == SELECTING_DOWN_DURATION) { - countdownValue = max(1, countdownValue + rotation); - updateDisplay(); - Serial.print(millis()); // Print the current time in milliseconds - Serial.print(" - Countdown value: "); Serial.println(countdownValue); + update_display = true; } + else if (currentState == SELECTING_DOWN_DURATION) { + countdownValue = max(1, countdownValue + rotation); + update_display = true; + } } + //========================================================= // Handle inactivity and switch to IDLE if necessary void handleInactivity(unsigned long currentMillis) { @@ -372,21 +368,36 @@ void handleInactivity(unsigned long currentMillis) { } // Exit IDLE if any rotary or button action happens - if (currentState == IDLE && (getRotation() != 0 || buttonPressed())) { - currentState = MENU; - lastActivityTime = millis(); // Reset inactivity timer upon exiting IDLE - - // Turn the display back on if it was off - if (displayOff) { - display.ssd1306_command(SSD1306_DISPLAYON); - displayOff = false; - Serial.print(millis()); // Print the current time in milliseconds - Serial.println(" - Display turned back on."); - } + // if (currentState == IDLE && (getRotation() != 0 || buttonPressed())) { + if (currentState == IDLE && (buttonPressed())) { + exitIdle(); + } +} - updateDisplay(); +void exitIdle() { + currentState = MENU; + lastActivityTime = millis(); // Reset inactivity timer upon exiting IDLE + + // Turn the display back on if it was off + if (displayOff) { + display.ssd1306_command(SSD1306_DISPLAYON); + displayOff = false; Serial.print(millis()); // Print the current time in milliseconds - Serial.println(" - Exiting IDLE mode. Back to MENU."); + Serial.println(" - Display turned back on."); } + + updateDisplay(); + Serial.print(millis()); // Print the current time in milliseconds + Serial.println(" - Exiting IDLE mode. Back to MENU."); } +//========================================================= +// Update the display if the update_display flag is set (encoder signal) +void handleScreenUpdate() { + if (update_display) { + updateDisplay(); + cli(); + update_display = false; + sei(); + } +} \ No newline at end of file From e5f21e9217adb1471cbe0242ef94a59bb2e8460e Mon Sep 17 00:00:00 2001 From: Grzegorz Date: Sat, 22 Feb 2025 10:26:52 +0100 Subject: [PATCH 2/2] Add KY040 encoder library as a header file --- sketch_sep25a/KY040.h | 246 ++++++++++++++++++ .../sketch_sep25a.ino | 2 +- 2 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 sketch_sep25a/KY040.h rename sketch_sep25a.ino => sketch_sep25a/sketch_sep25a.ino (99%) diff --git a/sketch_sep25a/KY040.h b/sketch_sep25a/KY040.h new file mode 100644 index 0000000..0296d0a --- /dev/null +++ b/sketch_sep25a/KY040.h @@ -0,0 +1,246 @@ +/** + * Class: KY040 + * + * Description: + * Class for KY-040 rotary encoders. Without builtin pull up resistors for + * CLK/DT you have to set pinMode( ,INPUT_PULLUP) before using the class. + * The class works with or without interrupts and prevents bounces + * by ignoring invalid CLK/DT sequences + * + * License: 2-Clause BSD License + * Copyright (c) 2024 codingABI + * For details see: LICENSE.txt + * + * Valid clockwise sequence for CLK/DT: Low/High->Low/Low->High/Low->High/High + * + * @code + * 0 1 2 3 + * --+ +---- High + * CLK | | + * +---+ Low + * + * ----+ +-- High + * DT | | + * +---+ Low + * @endcode + * + * Valid counter-clockwise sequence for CLK/DT: High/Low->Low/Low->Low/High->High/High + * + * @code + * 0 1 2 3 + * ----+ +--- High + * CLK | | + * +---+ Low + * + * --+ +----- High + * DT | | + * +---+ Low + * @endcode + * + * Home: https://github.com/codingABI/KY040 + * + * @author codingABI https://github.com/codingABI/ + * @copyright 2-Clause BSD License + * @file KY040.h + * @version 1.0.1 + */ +#pragma once + +/** Library version */ +#define KY040_VERSION "1.0.1" + +#include + +/** When using sleep modes wait X milliseconds for next sleep after a CLK/DT sequence start do prevent missing signals */ +#define PREVENTSLEEPMS 150 +// Pin idle state +#define INITSTEP 0b11 +// Max steps for a signal sequence +#define MAXSEQUENCESTEPS 4 + +/** Class for a KY-040 rotary encoder */ +class KY040 { + public: + /** Rotation states */ + enum directions + { + IDLE, /**< Rotary encoder is idle */ + ACTIVE, /**< Rotary encoder is rotating, but the CLK/DT sequence has not finished */ + CLOCKWISE, /**< CLK/DT sequence for one step clockwise rotation has finished */ + COUNTERCLOCKWISE /**< CLK/DT sequence for one step counter-clockwise rotation has finished */ + }; + + /**@brief + * Constructor of a the KY-040 rotary encoder + * + * @param[in] clk_pin Digital input pin connected to CLK aka. A + * @param[in] dt_pin Digital input pin connected to DT aka. B + */ + KY040(byte clk_pin, byte dt_pin) + { + m_clk_pin = clk_pin; // aka. A + m_dt_pin = dt_pin; // aka. B + v_state = 255; + v_lastResult = IDLE; + v_lastSequenceStartMillis = millis(); + v_sequenceStep = 0; + v_direction = IDLE; + v_oldState = INITSTEP; + } + + /**@brief + * Returns current rotation state from stored pin state. + * + * If you do not use interrupts, you have to start setState() and checkRotation() or a function using + * these (for example getRotation()) very frequently in your loop to prevent missing signals + * + * @retval KY040::CLOCKWISE CLK/DT sequence for one step clockwise rotation has finished + * @retval KY040::COUNTERCLOCKWISE CLK/DT sequence for one step counter-clockwise rotation has finished + * @retval KY040::IDLE Rotary encoder is idle + * @retval KY040::ACTIVE Rotary encoder is rotating, but the CLK/DT sequence has not finished + */ + byte checkRotation() + { + byte result = IDLE; + + if (v_state != v_oldState) { // State changed? + if (v_sequenceStep == 0) { // Check for begin of rotation + if (v_state == c_signalSequenceCW[0]) { // Begin of CW + v_direction=CLOCKWISE; + v_sequenceStep = 1; + v_lastSequenceStartMillis = millis(); + } + if (v_state == c_signalSequenceCCW[0]) { // Begin of CCW + v_direction=COUNTERCLOCKWISE; + v_sequenceStep = 1; + v_lastSequenceStartMillis = millis(); + } + } else { + switch (v_direction) { + case CLOCKWISE: + if (v_state == c_signalSequenceCW[v_sequenceStep]) { + v_sequenceStep++; + if (v_sequenceStep >= MAXSEQUENCESTEPS) { // Sequence has finished + result=v_direction; + v_lastResult=result; + v_direction=IDLE; + v_sequenceStep=0; + } else result=ACTIVE; + } else { + // Invalid sequence + if (v_state == INITSTEP) { // Reset sequence in init state + v_direction=IDLE; + v_sequenceStep=0; + } + } + break; + case COUNTERCLOCKWISE: + if (v_state == c_signalSequenceCCW[v_sequenceStep]) { + v_sequenceStep++; + if (v_sequenceStep >= MAXSEQUENCESTEPS) { // Sequence has finished + result=v_direction; + v_lastResult=result; + v_direction=IDLE; + v_sequenceStep=0; + } else result=ACTIVE; + } else { + // Invalid sequence + if (v_state == INITSTEP) { // Reset sequence in init state + v_direction=IDLE; + v_sequenceStep=0; + } + } + break; + } + } + v_oldState = v_state; + } + // Prevent unsigned long overrun + if (millis() - v_lastSequenceStartMillis > PREVENTSLEEPMS) { + v_lastSequenceStartMillis = millis() - PREVENTSLEEPMS - 1; + } + return result; + } + + /**@brief + * Get and reset last finished rotation step (Do not use inside ISR) + * + * @retval KY040::CLOCKWISE CLK/DT sequence for one step clockwise rotation has finished + * @retval KY040::COUNTERCLOCKWISE CLK/DT sequence for one step counter-clockwise rotation has finished + * @retval KY040::IDLE Rotary encoder is idle + */ + byte getAndResetLastRotation() + { + cli(); + byte result = v_lastResult; + v_lastResult = IDLE; + sei(); + return result; + } + + /**@brief + * Read and stores current pin state for CLK and DT and returns the current rotation state. + * + * Reads pin state for CLK and DT with DigitalRead() and checks current rotation state by calling checkRotation() + * + * @retval KY040::CLOCKWISE CLK/DT sequence for one step clockwise rotation has finished + * @retval KY040::COUNTERCLOCKWISE CLK/DT sequence for one step counter-clockwise rotation has finished + * @retval KY040::IDLE Rotary encoder is idle + * @retval KY040::ACTIVE Rotary encoder is rotating, but the CLK/DT sequence has not finished + */ + byte getRotation() + { + setState((digitalRead(m_clk_pin)<<1)+digitalRead(m_dt_pin)); + return checkRotation(); + } + + /**@brief + * Get stored pin states for CLK and DT (Left bit is for CLK, right bit is for DT). Should be called from ISR, when needed + * + * @returns Stored pin states for CLK and DT in two bits (Left bit is for CLK, right bit is for DT) + */ + byte getState() + { + return v_state; + } + + /**@brief + * Checks, if it save to go to sleep + * + * Returns true, if device was running long enough to get a full sequence (Do not use inside ISR) + * + * @retval true Yes, it is save to go to sleep + * @retval false No, it is not save and you could miss signals, if you go to sleep anyway + */ + bool readyForSleep() + { + cli(); + unsigned long lastStepMillis = v_lastSequenceStartMillis; + sei(); + return (millis()-lastStepMillis > PREVENTSLEEPMS); + } + + /**@brief + * Stores pin states for CLK and DT (Left bit is for CLK, right bit is for DT). Should be called from ISR, when needed. + * + * @param[in] state Pin state for CLK and DT in two bits (Left bit is for CLK, right bit is for DT) + */ + void setState(byte state) + { + v_state = state; + } + private: + byte m_clk_pin; // aka. A + byte m_dt_pin; // aka. B + volatile byte v_state; + volatile byte v_lastResult; + volatile unsigned long v_lastSequenceStartMillis; + volatile byte v_sequenceStep; + volatile byte v_direction; + volatile byte v_oldState; + // CLK/DT sequence for a clockwise rotation (One byte instead of a byte array would be enough for the four 2-bit values, but are harder to read) + const byte c_signalSequenceCW[MAXSEQUENCESTEPS] = {0b01,0b00,0b10,INITSTEP}; + // CLK/DT sequence for a counter-clockwise rotation (One byte instead of a byte array would be enough for the four 2-bit values, but are harder to read) + const byte c_signalSequenceCCW[MAXSEQUENCESTEPS] = {0b10,0b00,0b01,INITSTEP}; + +}; \ No newline at end of file diff --git a/sketch_sep25a.ino b/sketch_sep25a/sketch_sep25a.ino similarity index 99% rename from sketch_sep25a.ino rename to sketch_sep25a/sketch_sep25a.ino index 81f0f3d..6a9f6f3 100644 --- a/sketch_sep25a.ino +++ b/sketch_sep25a/sketch_sep25a.ino @@ -1,6 +1,6 @@ #include #include -#include +#include "KY040.h" //----------------------------------------------- Adafruit_SSD1306 display(128, 64, &Wire, D4);