Rotary encoder increment/decrement

hi all,

i would like to increment/decrement different variables based on one variable set by rotary encoder. i have 6 different functions each handling different task like set time, set date, set alarm… so instead of running encoder code 6 times in each function or placing each variable in the encoder code to increment/decrement, it would be great if i could use existing “encoderPos += 1;” and “encoderPos += 1;” from “rafbuff” encoder code bellow.

/* interrupt routine for Rotary Encoders
   tested with Noble RE0124PVB 17.7FINB-24 https://www.nobleusa.com/pdf/xre.pdf - available at pollin.de
   and a few others, seems pretty universal

   The average rotary encoder has three pins, seen from front: A C B
   Clockwise rotation A(on)->B(on)->A(off)->B(off)
   CounterCW rotation B(on)->A(on)->B(off)->A(off)

   and may be a push switch with another two pins, pulled low at pin 8 in this case
   raf@synapps.de 20120107

*/

// usually the rotary encoders three pins have the ground pin in the middle
enum PinAssignments {
  encoderPinA = 2,   // right
  encoderPinB = 3,   // left
  clearButton = 8    // another two pins
};

volatile unsigned int encoderPos = 0;  // a counter for the dial
unsigned int lastReportedPos = 1;   // change management
static boolean rotating = false;    // debounce management

// interrupt service routine vars
boolean A_set = false;
boolean B_set = false;


void setup() {
  pinMode(encoderPinA, INPUT);
  pinMode(encoderPinB, INPUT);
  pinMode(clearButton, INPUT);
  // turn on pullup resistors
  digitalWrite(encoderPinA, HIGH);
  digitalWrite(encoderPinB, HIGH);
  digitalWrite(clearButton, HIGH);

  // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, doEncoderA, CHANGE);
  // encoder pin on interrupt 1 (pin 3)
  attachInterrupt(1, doEncoderB, CHANGE);

  Serial.begin(9600);  // output
}

// main loop, work is done by interrupt service routines, this one only prints stuff
void loop() {
  rotating = true;  // reset the debouncer

  if (lastReportedPos != encoderPos) {
    Serial.print("Index:");
    Serial.println(encoderPos, DEC);
    lastReportedPos = encoderPos;
  }
  if (digitalRead(clearButton) == LOW )  {
    encoderPos = 0;
  }
}

// Interrupt on A changing state
void doEncoderA() {
  // debounce
  if ( rotating ) delay (1);  // wait a little until the bouncing is done

  // Test transition, did things really change?
  if ( digitalRead(encoderPinA) != A_set ) { // debounce once more
    A_set = !A_set;

    // adjust counter + if A leads B
    if ( A_set && !B_set )
      encoderPos += 1;

    rotating = false;  // no more debouncing until loop() hits again
  }
}

// Interrupt on B changing state, same as A above
void doEncoderB() {
  if ( rotating ) delay (1);
  if ( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if ( B_set && !A_set )
      encoderPos -= 1;

    rotating = false;
  }
}

and then call that encoder variable into my function below and increment/decrement “counter” variable.

void encoder_brightness(){
  
    if (lastReportedPos_brightness_count != counter) {
      if (counter>15) {(counter = 15);}
       if (counter<0) {(counter = 0);}

       tensofcounts = counter / 10;
       singlecounts = counter % 10; 

       lastReportedPos_brightness_count = counter;

       MaximCC.setIntensity(DisplayDate, counter);
       
       MaximCC.setChar(DisplayBrightness,0,tensofcounts,false);
       MaximCC.setChar(DisplayBrightness,1,singlecounts,false);

    } 
}

Many thanks in advance,
Alek

How will the sketch know which variable to update with the output from the encoder ?

Either you need some method of choosing which variable to update or you sequence through them. Either way needs some form of user input such as a button and an indication of which variable is being updated

based on button press count it would run one of the functions chosen, then inside that function turning the encoder would update variable that needs to inc/dec in that function.

example above shows one of those functions, for setting up the display brightness, we got there buy holding the button for more than 3 seconds.

I will never have a situation where i have two different variables in the same function, so the encoder would always output “encoderPos += 1;” and “encoderPos += 1;” and i would just like to change local variable based on that encoderPos.

thanks,
Alek

To do what you want it would be best to call the encoder function from the target function rather than calling the encoder function then passing the output to the target function

ok, so entire “doEncoderA and B” code every time i want to call encoder in that function…
thanks,
Alek

One scenario.  Do it in a series of steps.

  1. First, get the encoder to cycle through the number of options you’ll have.   Starting at zero, increment to the highest number-1 (5 in your case) then start over at zero.   Do the same for the other encoder direction, when at zero rollover to the top number-1.

    1a. A suggestion - don’t use the encoder value encoderPos. Make a separate variable and just generate increment and decrement signals for the encoder based on direction of rotation. YMMV

  2. Make an array* with as many elements as the number of options (array elements are numbered 0 thru number-1).   Just for testing put some random values in the array elements.

  3. Using the encoder-controlled value from step 1 as the index into the array, ‘select’ array elements, one at a time, for display.

  4. Create a boolean* variable and toggle it by pressing the
    pushbutton.  One state of the boolean puts the code in ‘select’ mode - to select a value for display - while the opposite state will put the code into ‘variable change’ mode.

  5. In variable change mode the encoder is logically disconnected from the array element selection code and instead increments/decrements the selected array element.   Press the button to toggle out of change mode and back to select mode.

Many of the techniques to put something like this together are found in the first five demos in IDE → file/examples/digital.

IDE reference page

Many thanks dougp!

That was the next step i was going to do, setup a “menu” pf sort and cycle through by using encoder, pretty much as you described it.
At this moment, i am using a button to do the same thing, (ezButton library) so to get into “menu” i keep the button pressed for 3 seconds and i start counting button presses. So buttonCount 1 goes into the menu for “set alarm”. i get EEPROM values on the 8 digit display of the last alarm ON/OFF time. next button press (button count2) selects the first 2 digits (hour alarm starts at) and i can set the hour by turning the encoder, once done next button press selects the next 2 digits (minutes alarm starts at) and so on.

This all works great but now i am planning to add time and date setup, so i need sort of a menu like you mentioned, with just 3 items

  1. set time
  2. set date
  3. set alarm

i would cycle through these (each is represented with a LED light) with encoder and based on my choice, a function will run. once in that function, i would get a display shows digits as in “set alarm” case.

i think i can manage all this on my own but the issue i have is too many variables in the encoder loop.

i have switched back to ISR sampling and encoder now looks like this:

ISR(TIMER1_COMPA_vect) {
  // debounce
  if ( rotating ) delay (0);  // wait a little until the bouncing is done
  if ( digitalRead(encoderPinA) != A_set ) { // debounce once more
    A_set = !A_set;
    if ( A_set && !B_set ){
      if (count2 == true)
        counter_hour_start += 1; 
      else if (count3 == true)
        counter_minute_start += 1;
      else if (count4 == true)
        counter_hour_end += 1;
      else if (count5 == true)
        counter_minute_end += 1;
      else if (count0 == true)
        counter += 1;

        rotating = false;
    }
  }
    if ( rotating ) delay (0);
    if ( digitalRead(encoderPinB) != B_set ) { // debounce once more
     B_set = !B_set;
      if ( B_set && !A_set ) {
      if (count2 == true)
        counter_hour_start -= 1;
      else if (count3 == true)
        counter_minute_start -= 1;
      else if (count4 == true)
        counter_hour_end -= 1;
      else if (count5 == true)
        counter_minute_end -= 1;
      else if (count0 == true)
        counter -= 1;

        rotating = false;
    }  
  }
}

i already have 4 variables just to set up the alarm, need to add 2 more for “set time” and 3 more to “Set date”, so will end up with at least 5 more, plus maybe for the menu items.

Bottom line and sort of the core of all this is, can i just keep piling up variables in the ISR for encoder? would that slow it down or would it be a bad idea for any reason?

that’s why i asked if i can just use ONE encoder variable in ISR, like “encoderPos” and then later, based on that encoderPos change the count in each function separately, for set time, set alarm etc

many thanks,
Alek

you had something like this in mind?

int setupMenuCounter; // counter goes from 1 to 3 or whatever the number of menu items is. Counter cycles so after 3 we get 1 or if we go to less than 1 we get 3


  if setupMenuCounter == 1 {
    // light up a LED showing "set sleep/wake time"
    if(button.isPressed()) {
      setAlarm();
    }
  }
  
  if setupMenuCounter == 2 {
    // light up a LED showing "set time"
    if(button.isPressed()) {
      setTime();
    }
  }
  
  if setupMenuCounter == 3 {
    // light up a LED showing "set date"
    if(button.isPressed()) {
      setDate();
    }
  }

so encoder cycles through the menu and each menu item is a number, then based on number selected, button press then leads to a function corresponding to the number selected

Ok, i understand this, as my previous post shows, that was my next step with the menu selection

this is a core of my problem, how do i just generate increment/decrement and then use it in a given function outside of encoder ISR?

i did not mess with arrays before, so need to research before i can understand what you are saying.

this is great idea, this way i will not have to cycle through all values with button counts without a possibility to go back to previous value, ie if i set “hours start” and go to “minutes start” digits, i will have to cycle all the way to the end, exit the setup and go back to set up “hours starts” number again, with your idea i would be able to select any set of digits without linear order.

again, i know what you mean but will have to deal with array before i fully understand.

many thanks,
Alek

I have used this code.  It uses an encoder library (link in the code).  There may be better, more efficient ways but this works for me.

//
#define INTPIN1 PIN2   // Rotary encoder interrupt on this Arduino Uno pin.
#define INTPIN2 PIN3   // Rotary encoder interrupt on this Arduino Uno pin.
#define ENCODER_PB PIN5   // 

#include <Rotary.h>  // encoder handler by Buxton
//                   https://github.com/buxtronix/arduino/tree/master/libraries/Rotary

//
unsigned char encoderTurned;
int8_t IRQActivity = 0;          //  IRQ activity flag
volatile int8_t IRQcounter = 0;  //  Encoder direction value, set by 'rotate' ISR
bool encoderDirectionCW;    // clockwise/counterclockwise indicator
bool isEncoderPBPressed;
int monitoredCount;
/*
   Rotary encoder pin assignments
*/
Rotary rotary = Rotary(INTPIN1, INTPIN2);  // interrupts are used on both pins 2 & 3
//
// === S E T U P ===

void setup() {
  Serial.begin(115200);
  attachInterrupt(0, rotate, CHANGE);
  pinMode(INTPIN1, INPUT_PULLUP);
  attachInterrupt(1, rotate, CHANGE);
  pinMode(INTPIN2, INPUT_PULLUP);
  pinMode(ENCODER_PB, INPUT_PULLUP);
}

void loop() {
  dialMoved();

  if (encoderTurned) {
    if (encoderDirectionCW) {
      monitoredCount++;
    }
    else monitoredCount--;
  }
  Serial.println(monitoredCount);
}
//
//-----------------------------------------------------
void dialMoved() {
  /*
       After clearing any encoderTurned flag, check the count value
       returned from the ISR to see if the rotary encoder has moved.
       If it has, then a detent crossing has been recognized and
       encoderTurned will be set true for one scan only.

       encoderDirectionCW is conditioned according to the direction the
       encoder was turned - CW = true, CCW = false.
  */
  encoderTurned = false;  // Reset previous turned value.
  /*
     detect encoder activity and indicate direction.
  */
  if (IRQActivity != IRQcounter) {
    encoderTurned = true;
    if (IRQcounter > IRQActivity) {
      encoderDirectionCW = true;
    }
    else {
      encoderDirectionCW = false;
    }
    IRQActivity = IRQcounter; // reset one-shot value to be ready for next IRQ
  }
} // end of dialMoved
//
//---------------------------------------------------------
void rotate() { // ISR
  /*
     Interrupt Service Routine for rotary encoder:
     An interrupt is generated any time either of
     the rotary inputs change state.
  */
  byte result = rotary.process();
  if (result == DIR_CW) {
    IRQcounter++;
  } else if (result == DIR_CCW) {
    IRQcounter--;
  }
}

Thanks dougp!

I have tried to implement the new setup menu but i have a problem.

  1. If i keep the encoder button pressed for 3 seconds i get into the setup menu “encoder_alarm”, this is ok
  2. i can toggle “select” boolean to be able to chose digits to set (when select = true) and to adjust the values of digits set (select = false)
  3. one thing i do not know how to do is how to exit the “encoder_alarm” loop once i have finished setting up the alarm.

as i have 4 groups of digits to set: hour start, minute start, hour end and minute end, each 2 digit groups I have set up the 5th “menu item” which when selected, and button clicked, should exit the loop, but not sure how to do that.

full code below, keep in mind this code is test WIP and a bit messy:

#include <EEPROM.h>
#include <ezButton.h>
#include <LedControl.h>

#include <TimeLib.h>
#include <RTClib.h>
#include <OneWire.h>
#include <DS1307RTC.h>
#include <SPI.h>


#define MAXIMCCLD 10         // output - CS/LOAD
#define MAXIMCCCLK 11        // output - CLOCK
#define MAXIMCCDATA 9       // output - DATA    
#define DS1307_I2C_ADDRESS 0x68 // define the RTC I2C address

#define ENCODER_SW 8

ezButton button(8);  // create ezButton object that attach to pin 7;
LedControl MaximCC=LedControl(MAXIMCCDATA, MAXIMCCCLK, MAXIMCCLD, 1); // Define pins for Maxim 72xx and how many 72xx we use

int LED = 4;
int LED2 = 119;


volatile signed int counter_hour_start = EEPROM.read(2);
volatile signed int counter_minute_start = EEPROM.read(3);
volatile signed int counter_hour_end = EEPROM.read(4);
volatile signed int counter_minute_end = EEPROM.read(5);
volatile signed int counter;


// usually the rotary encoders three pins have the ground pin in the middle
enum PinAssignments {
  encoderPinA = 2,   // right DT
  encoderPinB = 3,   // left CLK
};

  // a counter for the dial
signed int lastReportedPos_hour_start = 1;   // change management
signed int lastReportedPos_minute_start = 1;   // change management
signed int lastReportedPos_hour_end = 1;   // change management
signed int lastReportedPos_minute_end = 1;   // change management
signed int lastReportedPos_brightness_count = 1;   // change management

// interrupt service routine vars
boolean A_set = false;
boolean B_set = false;

int previousSecond = 0;
int tensofhours, singlehours, tensofminutes, singleminutes,tensofhours1, singlehours1, tensofminutes1, singleminutes1,singlecounts,tensofcounts;

const int SHORT_PRESS_TIME = 1000; // 1000 milliseconds
const int LONG_PRESS_TIME  = 1000; // 1000 milliseconds

unsigned long pressedTime  = 0;
unsigned long releasedTime = 0;
bool isPressing = false;
bool isLongDetected = false;

unsigned long count0 = true;
unsigned long count1;
unsigned long count2;
unsigned long count3;
unsigned long count4;
unsigned long count5;
unsigned long count6;
unsigned long largerThanZero;

int period = 20;
unsigned long time_now = 0;

signed int lastReportedPos_setupMenuCounter = 1;
signed int setupMenuCounter = 1; // counter goes from 1 to 3 or whatever the number of menu items is. Counter cycles so after 3 we get 1 or if we go to less than 1 we get 3
signed int lastReportedPos_sleepWakeCounter = 1;
signed int sleepWakeCounter = 1;

int oldEncoderButtonState = LOW;
int encoderButtonState = LOW;

bool select = false;

volatile unsigned int encoderPos = 0;  // a counter for the dial
unsigned int lastReportedPos = 1;   // change management
static boolean rotating = false;    // debounce management

//==============================================================================
// SETUP
//==============================================================================

void setup() {

  pinMode(LED, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(53, OUTPUT);
  pinMode(ENCODER_SW, INPUT);

  pinMode(encoderPinA, INPUT);
  pinMode(encoderPinB, INPUT);
  
  // Initialize RTC and set as SyncProvider.
  // Later RTC will be synced with DCF time
  setSyncProvider(RTC.get); // the function to get the time from the RTC
  // check if RTC has set the system time
  if (timeStatus() != timeSet)
  { // Unable to sync with the RTC - activate RTCError LED  
  }
  
  MaximCC.shutdown(0,false);
  MaximCC.setIntensity(0,15);
  MaximCC.clearDisplay(0);
  button.setDebounceTime(10); // set debounce time to 50 milliseconds
  button.setCountMode(COUNT_FALLING);

     // turn on pullup resistors
  digitalWrite(encoderPinA, HIGH);
  digitalWrite(encoderPinB, HIGH);

  // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, doEncoderA, CHANGE);
  // encoder pin on interrupt 1 (pin 3)
  attachInterrupt(1, doEncoderB, CHANGE);

  tensofhours = counter_hour_start / 10;
  singlehours = counter_hour_start % 10;
  tensofminutes = counter_minute_start / 10;
  singleminutes = counter_minute_start % 10;
  tensofhours1 = counter_hour_end / 10;
  singlehours1 = counter_hour_end % 10;
  tensofminutes1 = counter_minute_end / 10;
  singleminutes1 = counter_minute_end % 10; 
}

//==============================================================================
// LOOP
//==============================================================================

void loop() {

  tasksEverySecond();
 
  // check if there was a short or a long press of a bottom // 
  button.loop(); // MUST call the loop() function first

  if(button.isPressed()){
    pressedTime = millis();
    isPressing = true;
    isLongDetected = false;
  }

  if(button.isReleased()) {
    isPressing = false;
    releasedTime = millis();

    long pressDuration = releasedTime - pressedTime;
    
    if( pressDuration < SHORT_PRESS_TIME ) // on short button press, keep resetting the counter to zero so it does not count before long press
      if (count0 == true){
      button.resetCount();
      }
  }

  if(isPressing == true && isLongDetected == false) {
    long pressDuration = millis() - pressedTime;

    if( pressDuration > LONG_PRESS_TIME ) {
      isLongDetected = true;
      
    }
  }
   
  if (count0 == true){ 
    if(millis() > time_now + period){
        time_now = millis();
    manual_brightness_control();
    } 
  } else {
    if(millis() > time_now + period){
        time_now = millis();
    encoder_alarm ();
  }
  }
  
  if (isLongDetected == true){
    button_press();
  }
  digitalWrite(53, LOW);  // set pin 0 low when leaving ISR()
}


//==============================================================================
// tasksEverySecond
//==============================================================================
void tasksEverySecond()
{
  if (second() != previousSecond)
  {
    previousSecond = second();
    displayRtcTime(); 
    ledTest();
  }
}

//==============================================================================
// ENCODER INTERRUP ROUTINE
//==============================================================================
// Interrupt on A changing state
void doEncoderA() {
  // debounce
  if ( rotating ) delay (1);  // wait a little until the bouncing is done
  // Test transition, did things really change?
  if ( digitalRead(encoderPinA) != A_set ) { // debounce once more
    A_set = !A_set;

    // adjust counter + if A leads B
    if ( A_set && !B_set )
    if (count0 == true)
        counter +=1;
      else if (select == true)
        sleepWakeCounter +=1;
      else if ((sleepWakeCounter == 0) && (select == false))
        counter_hour_start +=1; 
      else if ((sleepWakeCounter == 1) && (select == false))
        counter_minute_start +=1;
      else if ((sleepWakeCounter == 2) && (select == false))
        counter_hour_end +=1;
      else if ((sleepWakeCounter == 3) && (select == false))
        counter_minute_end +=1;
      
        rotating = false;  // no more debouncing until loop() hits again

    }
}

// Interrupt on B changing state, same as A above
void doEncoderB() {
  // debounce
  if ( rotating ) delay (1);  // wait a little until the bouncing is done
  if ( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if ( B_set && !A_set )
      if (count0 == true)
        counter -=1;
      else if (select == true)
        sleepWakeCounter -=1;
      else if ((sleepWakeCounter == 0) && (select == false))
        counter_hour_start -=1;
      else if ((sleepWakeCounter == 1) && (select == false))
        counter_minute_start -=1;
      else if ((sleepWakeCounter == 2) && (select == false))
        counter_hour_end -=1;
      else if ((sleepWakeCounter == 3) && (select == false))
        counter_minute_end -=1;
      
        rotating = false;  // no more debouncing until loop() hits again
    
  }
}

//==============================================================================
// button_press
//==============================================================================
void button_press (){

  unsigned long count = button.getCount();
  
  if (count == 0) {
    count0 = true;
    } else {
      count0 = false;
    }  
}


//==============================================================================
// ENCODER ALARM
//==============================================================================

void encoder_alarm(){

  button.loop(); // MUST call the loop() function first
  unsigned long count = button.getCount();

  // toggle boolean state to choose between cycling the setup positions and changing the values
  encoderButtonState = digitalRead(ENCODER_SW);
  if (( encoderButtonState != oldEncoderButtonState ) && (encoderButtonState == HIGH) ) {
     select = !select;
     Serial.println(select);
    }
    oldEncoderButtonState = encoderButtonState;

  //--------------------- HOURS alarm STARTS at ------------------------//
  
    if (lastReportedPos_hour_start != counter_hour_start) {
      if (counter_hour_start>23) {(counter_hour_start = 0);}
      if (counter_hour_start<0) {(counter_hour_start = 23);}

      tensofhours = counter_hour_start / 10;
      singlehours = counter_hour_start % 10; 

      lastReportedPos_hour_start = counter_hour_start;
  }
      EEPROM.update(2,counter_hour_start);


  //--------------------- MINUTES alarm STARTS at ------------------------//

    if (lastReportedPos_minute_start != counter_minute_start) {
      if (counter_minute_start>59) {(counter_minute_start = 0);}
       if (counter_minute_start<0) {(counter_minute_start = 59);}

       tensofminutes = counter_minute_start / 10;
       singleminutes = counter_minute_start % 10; 

      lastReportedPos_minute_start = counter_minute_start;

       EEPROM.update(3,counter_minute_start);
  } 
  
  //--------------------- HOURS alarm ENDS at ------------------------// 

    if (lastReportedPos_hour_end != counter_hour_end) {
      if (counter_hour_end>23) {(counter_hour_end = 0);}
      if (counter_hour_end<0) {(counter_hour_end = 23);}

      tensofhours1 = counter_hour_end / 10;
      singlehours1 = counter_hour_end % 10; 

      lastReportedPos_hour_end = counter_hour_end;
  }
      EEPROM.update(4,counter_hour_end);

  
  //--------------------- MINUTES alarm ENDS at ------------------------//
  
    if (lastReportedPos_minute_end != counter_minute_end) {
      if (counter_minute_end>59) {(counter_minute_end = 0);}
       if (counter_minute_end<0) {(counter_minute_end = 59);}

       tensofminutes1 = counter_minute_end / 10;
       singleminutes1 = counter_minute_end % 10; 

      lastReportedPos_minute_end = counter_minute_end;

       EEPROM.update(5,counter_minute_end);
  } 

  
  //--------------------- 7 Segment display  ------------------------//

    MaximCC.setChar(0,7,tensofhours,false);
    MaximCC.setChar(0,6,singlehours,false);
    MaximCC.setChar(0,5,tensofminutes,false);
    MaximCC.setChar(0,4,singleminutes,false);
    MaximCC.setChar(0,3,tensofhours1,false);
    MaximCC.setChar(0,2,singlehours1,false);
    MaximCC.setChar(0,1,tensofminutes1,false);
    MaximCC.setChar(0,0,singleminutes1,false);

  if (select == true) {

    if (lastReportedPos_sleepWakeCounter != sleepWakeCounter) {
      if (sleepWakeCounter>4) {(sleepWakeCounter = 0);}
       if (sleepWakeCounter<0) {(sleepWakeCounter = 4);}
    
       lastReportedPos_sleepWakeCounter = sleepWakeCounter;
    
    }
  }

  if ((sleepWakeCounter == 0) || (sleepWakeCounter == 1) || (sleepWakeCounter == 2) || (sleepWakeCounter == 3)) { 
    if(button.isPressed()) {
      select = false;
      }
  }

  
    if (sleepWakeCounter == 4) {
      if(button.isPressed()) {
      count0 = true;
      }
  }
   

  
  if (sleepWakeCounter == 0) {
    MaximCC.setChar(0,7,tensofhours,false);
    MaximCC.setChar(0,6,singlehours,true); 
  } else {
    MaximCC.setChar(0,6,singlehours,false); 
  }

  if (sleepWakeCounter == 1) {
    MaximCC.setChar(0,5,tensofminutes,false);
    MaximCC.setChar(0,4,singleminutes,true); 
  } else {
    MaximCC.setChar(0,4,singleminutes,false); 
  }

  if (sleepWakeCounter == 2) {
    MaximCC.setChar(0,3,tensofhours1,false);
    MaximCC.setChar(0,2,singlehours1,true); 
  } else {
    MaximCC.setChar(0,2,singlehours1,false); 
  }

  if (sleepWakeCounter == 3) {
    MaximCC.setChar(0,1,tensofminutes1,false);
    MaximCC.setChar(0,0,singleminutes1,true); 
  } else {
    MaximCC.setChar(0,0,singleminutes1,false); 
  }
}

//==============================================================================
// manual_brightness_control
//==============================================================================

void manual_brightness_control()
{
  
  if (lastReportedPos_brightness_count != counter) {
      if (counter>15) {(counter = 15);}
       if (counter<0) {(counter = 0);}

       singlecounts = counter / 10;
       singlecounts = counter % 10; 

      lastReportedPos_brightness_count = counter;
      MaximCC.setIntensity(0,counter);
  }  
}
//==============================================================================
// displayRtcTime
//==============================================================================

void displayRtcTime()
{
  if (count0 == true){
  MaximCC.setChar(0, 7, (hour() / 10), false);
  MaximCC.setChar(0, 6, (hour() % 10), false);
  
  MaximCC.setChar(0, 5, '-', false);
  MaximCC.setChar(0, 4, (minute() / 10), false);
  MaximCC.setChar(0, 3, (minute() % 10), false);

  MaximCC.setChar(0, 2, '-', false);
  MaximCC.setChar(0, 1, (second() / 10), false);
  MaximCC.setChar(0, 0, (second() % 10), false);
  
  }
}

//==============================================================================
// ledTest
//==============================================================================
void ledTest(){
  int midnight;
  int powersaving;
  int startTime = counter_hour_start * 100 + counter_minute_start;
  int endTime = counter_hour_end * 100 + counter_minute_end;
  int timeNow = hour() * 100 + minute();
  

  if (endTime == 0000 && startTime != 0000){
    endTime = 2359;
  }
  
    
   if (timeNow >= startTime && timeNow < endTime)  {
    powersaving = true;
   } else {
    powersaving = false;
   }
  
  if (powersaving == true){
    digitalWrite(LED2, HIGH);
  } else {
    digitalWrite(LED2, LOW);
  }
}

many thanks for any help,
Alek

I cannot see where the state of rotating is ever changed. It is only ever set false.

volatile signed int counter_hour_start = EEPROM.read(2);
volatile signed int counter_minute_start = EEPROM.read(3);
volatile signed int counter_hour_end = EEPROM.read(4);
volatile signed int counter_minute_end = EEPROM.read(5);

Above you are reading from consecutive EEPROM addresses. The EEPROM stores one byte in each address. ints are two bytes each. You need to separate each EEPROM address by at least the size of the variable you’re storing. It’s one of the ‘gotchas’ that YOU must be more hands-on to manage EEPROM usage.

Also, you’re using ints to store values that’ll never exceed 12710. byte, or, uint8_t would work as well and use half the space. The time values are signed. You’ll never have negative time so unsigned is more accurate. I’m pretty sure those values don’t have to be volatile either.

I want to make sure we’re both talking about the same idea.

When select is true turning the encoder goes through the hours/minutes choices. There might be a display presentation such as:
start time          end time
hrs   mins          hrs  mins

where the cursor is on start hours. If the encoder is now rotated clockwise the display changes to:

start time          end time
hrs   mins          hrs  mins

and so forth. When the encoder, or other, button is pressed select goes to false and the display is updated to look like

start mins - nn

where nn is some number 0 to 23. The value, whatever it was, that told us to point to the mins value is retained while we’re adjusting the number value.

Now, moving the encoder increments/decrements the start time minutes value - which is updated on the display in real time. When the number is correct pressing the encoder button again returns us to select = true and the first display is presented again and the value saved previously is retrieved and restores us to pointing to the mins value. A new value can now be selected for change.

Exactly like that! The sketch is now working and i am able to exit the “encoder_alarm” loop and the setup menu, but have another sort of a problem which i am trying to solve.

EEPROM is indeed messy and thats something i will get back to when i solve the problem i have now, related to 7 segment display behavior when i am cycling through the settings.

Many thanks man, you gave me some nice ideas indeed, apart from helping with other things!
Alek

Great information regarding the encoders and the issues of it. And can I please know the best company or the encoders you are using that are good and accurate

I got two of these: https://www.amazon.com/gp/product/B01JE9KJJO/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1

They are now unavailable. Oh, well.

I will note that I wouldn’t consider these for long term or intensive use as the slot cut in the shaft allows the shaft halves to compress and let the knob slide off. Bourns is reputable.

1 Like

ones i am using are cheap PRC knockoffs and they are awful, definitely something that has to be of higher quality.

One possibility:

I expect not as pricey as Bourns’ offerings.

Any encoder like the Adafruit type has contacts that bounce - it’s just mechanical switches inside after all. The library used in post #10 has debouncing built in. Try it with your encoder, it may well improve things.

i just did some tests, i have 2 different types of encoder, both cheap chinese ones but the one i installed on my PCB seems to be bad beyond belief, it bounces like crazy.
The other one is “module” type that you can get on ebay, Amazon etc, much better, seems more reliable but still not perfect.

since my prototype PCB has the encoder connected to arduino pins 2 and 3, i have added external encoder (module type LINK) and connected it to pins 52 and 53 of my MEGA2560 pro mini.

Installed encoder, on pins 2 and 3 (LINK)

Now i am wondering, is the “module” encoder so much better that my sketch works flawlessly OR, did i make a mistake by using INT pins 2 and 3 for the encoder?

As i said before, i am also using an interrupt (on change) on pin 19 to capture DCF77 atomic clock signal, and when i am using encoder on pins 2 and 3 my signal jumps a lot more than if using external encoder on pins 52 and 53. Did i somehow affect my pin 19 interrupt with encoder on interrupt pins…

My encoder schematic attached


schematic shows polarized caps while on PCB i have ceramic 100n.

many thanks,
Alek

Thanks for the information. I am actually working on the project with rotary encoders. I will try these models in the other sites.