Author Topic: please someone unmuddle me. trying to get a rotary encoder working  (Read 4532 times)

0 Members and 1 Guest are viewing this topic.

Offline drspasticTopic starter

  • Contributor
  • Posts: 36
  • Country: bg
hi, im not very good at this arduino business. i managed to get the display attached and working, setup variables.
now im trying to graft in some code from: https://esp32io.com/tutorials/esp32-rotary-encoder
i got so far but i dont know where to put the rest and what loop should go where. the rotary controller ultimately should change the variable vfoa

Code: [Select]
#include <Adafruit_GFX.h>     
#include <Adafruit_ST7735.h> 
#include <ezButton.h>  // the library to use for SW pin on encoder

#define CLK_PIN 34 // ESP32 pin GPIO25 connected to the rotary encoder's CLK pin
#define DT_PIN  35 // ESP32 pin GPIO26 connected to the rotary encoder's DT pin
#define SW_PIN  32 // ESP32 pin GPIO27 connected to the rotary encoder's SW pin

#define DIRECTION_CW  0         // clockwise direction rotary
#define DIRECTION_CCW 1         // counter-clockwise direction rotary

#define TFT_CS 5
#define TFT_RST 2
#define TFT_DC 0
#define vfoa 0740.00000
#define vfob 2400.00000
#define TFT_BL 22               // LED back-light

int counter = 0;                //rotary
int direction = DIRECTION_CW;   //rotary
int CLK_state;                  //rotary
int prev_CLK_state;             //rotary

ezButton button(SW_PIN);  // create ezButton object that attach to pin 32;

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);  // hardware SPI


void setup(void) {
 
  tft.initR(INITR_BLACKTAB);      // Init ST7735S chip, black tab

 // configure encoder pins as inputs
  pinMode(CLK_PIN, INPUT);
  pinMode(DT_PIN, INPUT);
  button.setDebounceTime(50);  // set debounce time to 50 milliseconds

  // read the initial state of the rotary encoder's CLK pin
  prev_CLK_state = digitalRead(CLK_PIN);


 
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
  tft.setRotation(1);
  tft.fillScreen(ST77XX_BLACK);

  tft.setTextColor(ST77XX_GREEN);
  tft.setTextSize(2);
  tft.setCursor(0, 20);
  tft.println(vfoa);
  tft.println("mhz");
  tft.setTextColor(ST77XX_YELLOW);
  tft.setCursor(0, 60);
  tft.println(vfob);
 tft.println("mhz");
}

void loop() { }

im having to work this out bit at a time learning as i go. any suggestions welcome, except ask ai because its always wrong which is why im trying to do it myself.  ;D
 

Offline drspasticTopic starter

  • Contributor
  • Posts: 36
  • Country: bg
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #1 on: January 16, 2025, 06:25:52 pm »
ps. im concerned about interupts and reaction time because i will need the finished unit to switch values rather fast and have the rotary knob live at all times
 

Online MarkF

  • Super Contributor
  • ***
  • Posts: 2816
  • Country: us
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #2 on: January 16, 2025, 09:19:48 pm »
In the past, my approach to processing rotary encoder events falls in to two general sections.
  • Section 1:
    I setup a fixed interrupt at 200 Hz or greater.  (You can optimize the rate after everything is working.)  When this interrupt occurs, I check if the encoder has changed.  If changed, I increment or decrement a global variable that counts the changes.  The interrupt also checks for switches and setting global flags.  (Do switch debounce here also.)   This minimizes the execution time within the interrupt only checking rotation counts and switches.

    I DO NOT do interrupt-on-change to detect encoder changes.  The interrupts can quickly be overloaded by switch bouncing in the encoder.  Noise on the encoder lines will keep the processor in the interrupt routine and prevent any other processing.  (Others will argue this point but I consider interrupt-on-change a bad design in this case.)

  • Section 2:
    In your main execution loop, I check if the global counter is non-zero.  If non-zero, add the global count to the value being controlled.  Then zero the global count and wait for more changes from the interrupt.  Also, do similar processing for switch activation.  This is also where I draw any displays and any other processing that needs to be done.  This main loop does not have to be fast because the encoder counter is being handled at the fast interrupt rate.  You just use the cumulative count at the beginning of each time through the loop.

A note here:
You can also add rotation acceleration in the main loop by checking if the cumulative count is greater than a triggering value and then increasing the count.

Example main loop code to do encoder acceleration:

   int cCount=0;  // global cumulative count from interrupt
   int speed=0;   // sample value being controlled

   loop()
   {
      // check for encoder change
      if (cCount != 0) {
         // perform acceleration
         if (cCount > 2) cCount += 5;
         if (cCount < -2) cCount -= 5;

         // update speed with encoder change
         speed += cCount;

         // restart count from interrupt
         cCount = 0;
      }

      // TODO: more loop processing

   }
« Last Edit: January 16, 2025, 09:26:20 pm by MarkF »
 
The following users thanked this post: drspastic

Offline rhodges

  • Frequent Contributor
  • **
  • Posts: 358
  • Country: us
  • Available for embedded projects.
    • My public libraries, code samples, and projects for STM8.
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #3 on: January 16, 2025, 11:22:50 pm »
In your main execution loop, I check if the global counter is non-zero.  If non-zero, add the global count to the value being controlled.  Then zero the global count and wait for more changes from the interrupt.
Be careful of a race condition. A guard flag set and cleared around this code can alert the interrupt handler to skip the check.
Currently developing embedded RISC-V. Recently STM32 and STM8. All are excellent choices. Past includes 6809, Z80, 8086, PIC, MIPS, PNX1302, and some 8748 and 6805. Check out my public code on github. https://github.com/unfrozen
 

Online MarkF

  • Super Contributor
  • ***
  • Posts: 2816
  • Country: us
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #4 on: January 17, 2025, 12:30:04 am »
In your main execution loop, I check if the global counter is non-zero.  If non-zero, add the global count to the value being controlled.  Then zero the global count and wait for more changes from the interrupt.
Be careful of a race condition. A guard flag set and cleared around this code can alert the interrupt handler to skip the check.

Two points:
  1)  The interrupt does not check any thing and doesn't need to.  It just increments or decrements the global count.

  2)  In my actual main loop, reading the global variable is a little more complex. 
      (If you minimize the execution time between checking the count and clearing the count, it don't think even this is required.)

      temp = cCount;
      cCount = 0;
      //  use temp instead of cCount to avoid any changes during processing
      if (temp != 0) {
         . . .
      }

This will minimize the time between reading and clearing the count from the interrupt.

Also, any race condition really doesn't matter. 
The chance of getting an interrupt during the small time between checking and clearing the count is minimal.
It would be rare and it really does not matter if you miss an encoder count or not. 
The user will not notice any rare missing counts.

In practice, the bigger issue is switch bounce on the encoder pins.
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 16097
  • Country: fr
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #5 on: January 17, 2025, 12:34:53 am »
Apart from what has been said, what's the ESP32 you use here? Some models have a PCNT peripheral that makes it easier/more efficient to handle rotary encoders.
You can have a look at this: https://github.com/madhephaestus/ESP32Encoder
 

Online MarkF

  • Super Contributor
  • ***
  • Posts: 2816
  • Country: us
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #6 on: January 17, 2025, 12:38:10 am »
Here's my actual interrupt routine code.  The global variable is en0.  (MCU is a pic18f2620)

Code: [Select]
      // Read rotary encoder
      pin = PORTC;
      currA0 = (pin>>1) & 0x01;
      currB0 = (pin>>2) & 0x01;
      if (lastA0 == 0 && currA0 == 1) {               // rising edge?
         if (currB0 != currA0) en0--; else en0++;     // clockwise?
      }
      lastA0 = currA0;
 

Offline Peabody

  • Super Contributor
  • ***
  • Posts: 2320
  • Country: us
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #7 on: January 17, 2025, 01:21:04 am »
If the encoder servicing load is an issue because of bouncing, there's an alternative interrupt-based method that addresses that.  The pin change interrupt is enabled on only one pin at a time.  When the first interrupt occurs, the interrupt on that line is disabled in the ISR, and the interrupt on the other line is enabled.  As a result, no interrupts are generated on the first line while it's bouncing.  The other line will have transitioned some time ago, and is unlikely to still be bouncing.

A problem occurs when the direction is changed because you're following the wrong line.  But that is solved by expanding the typical 16-byte lookup table of the state machine model to 32 bytes.

The details of this method are included in my Github repo for Atmega328P processors, which includes a PDF explaining the process.

https://github.com/gbhug5a/Rotary-Encoder-Servicing-Routines/tree/master/Arduino

You would have to modify the routine to work with the ESP32.  The examples in the repo report in the serial monitor the number of interrupts that occur between two detents.  The theoretical number should be four for typical encoders - one for each transition - or three if you change direction.  Otherwise, a noisy encoder can generate dozens of interrupts from a single transition.

You might also want to consider some form of hardware debouncing.


 

Online MarkF

  • Super Contributor
  • ***
  • Posts: 2816
  • Country: us
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #8 on: January 17, 2025, 03:04:35 am »
Notice, I don't have a lookup table and I don't use a state machine.

I still hold that it is not good to use interrupt-on-pin-change to detect encoder activity.
It is much better to use a fixed timer interrupt to poll the encoder and more predictable.

Those six lines of code are my entire interrupt routine.
 

Offline Peabody

  • Super Contributor
  • ***
  • Posts: 2320
  • Country: us
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #9 on: January 17, 2025, 03:44:24 am »
With polling you are polling all the time, even if the encoder isn't being turned.  Using interrupts, you only spend time servicing the encoder when it's being turned.  Polling may be "much better" for some, but opinions may differ on this.
 

Online Atlan

  • Frequent Contributor
  • **
  • Posts: 546
  • Country: sk
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #10 on: January 17, 2025, 04:54:17 am »
The bouncing of the encoder contacts is solved by the hardware RCR circuit.  And that way uP doesn't have to spend time on unnecessary tasks in the interruption.  (whether there is a bounce or not) Especially when the given circuit is listed in the DS of the encoder. 
FNIRSI 1013D Always provide a picture or video with the problem where the parameters of the oscilloscope are visible, and a picture of the diagnostic screen with the values.
Firmware is here (or not) https://github.com/Atlan4/Fnirsi1013D/tree/main/latest%20firmware%20version
 

Offline drspasticTopic starter

  • Contributor
  • Posts: 36
  • Country: bg
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #11 on: January 17, 2025, 01:41:15 pm »
im using lolin lite, the usual suspect.

many thanks for all your responses although most of them are discussing things at a higher level than i am conversant with at the moment. im very much a beginner using spi, and displays.

the rotary acceleration is a good idea. originally i was going to use an analogue joystick but mounting it on a project box seemed awkward. the oscillator the project will control goes from 160mhz to 4400mhz and in quite tiny steps. i might need to use the push button to change the digit the encoder adjusts.

it all seems a bit overwhealming
« Last Edit: January 17, 2025, 01:51:54 pm by drspastic »
 

Online Atlan

  • Frequent Contributor
  • **
  • Posts: 546
  • Country: sk
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #12 on: January 17, 2025, 04:02:31 pm »
Try
FNIRSI 1013D Always provide a picture or video with the problem where the parameters of the oscilloscope are visible, and a picture of the diagnostic screen with the values.
Firmware is here (or not) https://github.com/Atlan4/Fnirsi1013D/tree/main/latest%20firmware%20version
 

Offline shabaz

  • Frequent Contributor
  • **
  • Posts: 673
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #13 on: January 17, 2025, 05:53:19 pm »
That's quite a large range if it is going to be granular, so it needs some good acceleration to work well. Something with several levels of acceleration (or an acceleration curve), and probably a very free-spinning encoder (although some of them tend to be more expensive optical ones rather than with conductive wiper contacts).

Although it irritated me to resort to it (I mentally prefer the idea of no buttons and rely on encoder acceleration for choosing the value, since that feels cleaner) the last time I did this with a not-very-free-spinning-encoder, I ended up using a button to select the amount of increment (you can see a purple bar in the photo, which in that case indicates increments of 1 MHz, but pressing the button allows the user to select (say) 1 Hz, 100 Hz, 10 kHz and 1 MHz increment options.

If it's a DIY project, then a cheap 4x3 numeric keypad is another nice and practical option if you have the space for it. Also, control over UART can be attractive (a serial console command-line interface, or an interface to software on the PC) depending on the use-case.
 

Offline pqass

  • Frequent Contributor
  • **
  • Posts: 981
  • Country: ca
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #14 on: January 17, 2025, 06:05:01 pm »
See my implementation to support multiple rotary encoders (array of structs) via one 1100Hz timer interrupt and no pin interrupts.
Basically, it will record a pin state (into a temp var every 900us) and will only increment/decrement an integer value when the last four states are all zero or all ones (ie. raw pin read has settled).  The main line then can just consume the integer or the switch settled state variable.


 

Offline drspasticTopic starter

  • Contributor
  • Posts: 36
  • Country: bg
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #15 on: January 17, 2025, 08:57:08 pm »
im happy to use the encoders button to select which number i edit. it must still be able to fine tune during use to correct drift.
 

Offline drspasticTopic starter

  • Contributor
  • Posts: 36
  • Country: bg
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #16 on: January 24, 2025, 05:10:16 pm »
so i got an encoder working and changing the lcd with this code:
https://pastebin.com/aXw7FhVV
but then i decided to add a second encoder so i had a tuning knob for both vfos. i doubled the sections of code for the first encoder and changed the variable names for the 2nd encoder by adding a 2 at the end. i ended up after (added a } at the end because compile wasnt happy) with this code:
https://pastebin.com/uqWg5HvA
but now neither encoder works!

please may i beg someone to look over the code and tell me what stupid mistake i made.
 

Offline mskeete

  • Contributor
  • Posts: 40
  • Country: gb
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #17 on: January 25, 2025, 12:23:05 pm »
At first glance I can see a few variable names you haven't updated on lines 65, 108 and 121
 

Offline drspasticTopic starter

  • Contributor
  • Posts: 36
  • Country: bg
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #18 on: January 25, 2025, 11:46:00 pm »
ok so i had some help from a kind soul. he reorganised it like this:
https://pastebin.com/y1wtJsaJ
and both encoders now work and display.

my next step was to improve granularity so i added 00000 to my starting point numbers (maximum it handles without fragging out) then tried to divide by 10000 in the display code like this:
Code: [Select]
tft.println(vfoa/10000);which i thought should display variable vfoa=74000000 as 740.00000 but it doesnt. infact what i really want is 740.000.00
but im stumped on how to get that. i need to show all trailing and leading zeros really so the decimal separators line up.
also, how can i get it to handle just one more digit so it can have granularity to 1Hz? is this a floating point arithmatic error that starts spewing random numbers?
 

Online DavidAlfa

  • Super Contributor
  • ***
  • Posts: 6466
  • Country: es
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #19 on: January 26, 2025, 03:48:12 am »
https://github.com/deividAlfa/stm32_soldering_iron_controller/blob/master/Drivers/generalIO/rotary_encoder.c

What's this? I don't really understand it.
You're doing "740/10000", which is 0.
Code: [Select]
#define vfoa 0740.00000

tft.println(vfoa/10000);

vfoa is not a variable, so just declare it as string?
Code: [Select]
#define vfoa "0740.000.00"

tft.println(vfoa);

« Last Edit: January 26, 2025, 03:53:51 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline drspasticTopic starter

  • Contributor
  • Posts: 36
  • Country: bg
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #20 on: January 27, 2025, 12:47:43 pm »
got it sorted! used the long long instead of int:

Code: [Select]
[code]#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <ezButton.h>  // the library to use for SW pin on encoder
 
#define CLK_PIN 34  // ESP32 pin GPIO25 connected to the rotary encoder's CLK pin
#define DT_PIN 35   // ESP32 pin GPIO26 connected to the rotary encoder's DT pin
#define SW_PIN 32   // ESP32 pin GPIO27 connected to the rotary encoder's SW pin
 
#define CLK_PIN2 4  // ESP32 2nd connected  rotary encoder's CLK pin
#define DT_PIN2 16   // ESP32 2nd connected  rotary encoder's DT pin
#define SW_PIN2 17   // ESP32 2nd connected  rotary encoder's SW pin
 
#define DIRECTION_CW 0   // clockwise direction rotary
#define DIRECTION_CCW 1  // counter-clockwise direction rotary
 
#define TFT_CS 5
#define TFT_RST 2
#define TFT_DC 0
#define TFT_BL 22  // LED back-light
 
//int counter = 0;                //rotary
long long vfoa = 740000000;
int direction = DIRECTION_CW;  //rotary
int CLK_state;                 //rotary
int prev_CLK_state;            //rotary
ezButton button(SW_PIN);       // create ezButton object that attach to pin 32;
 
long long vfob = 2400000000;
int direction2 = DIRECTION_CW;  //rotary2
int CLK_state2;                 //rotary2
int prev_CLK_state2;            //rotary2
ezButton button2(SW_PIN2);      // create ezButton object that attach to pin ;
 
bool updateDisplay = false;
 
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);  // hardware SPI
 
 
void setup(void) {
 
  tft.initR(INITR_BLACKTAB);  // Init ST7735S chip, black tab
 
  // configure encoder pins as inputs
  pinMode(CLK_PIN, INPUT);
  pinMode(DT_PIN, INPUT);
  button.setDebounceTime(50);  // set debounce time to 50 milliseconds
 
  // read the initial state of the rotary encoder's CLK pin
  prev_CLK_state = digitalRead(CLK_PIN);
 
  // configure encoder pins as inputs
  pinMode(CLK_PIN2, INPUT);
  pinMode(DT_PIN2, INPUT);
  button2.setDebounceTime(50);  // set debounce time to 50 milliseconds
 
  // read the initial state of the rotary encoder's CLK pin
  prev_CLK_state2 = digitalRead(CLK_PIN2);
 
  pinMode(TFT_BL, OUTPUT);
  digitalWrite(TFT_BL, HIGH);
  tft.setRotation(1);
  tft.fillScreen(ST77XX_BLACK);
 
  tft.setTextColor(ST77XX_GREEN);
  tft.setTextSize(2);
  tft.setCursor(0, 20);
  tft.println(vfoa/1000000.0,6);
  tft.setCursor(0, 40);
  tft.println("mhz");
  tft.setTextColor(ST77XX_YELLOW);
  tft.setCursor(0, 70);
  tft.println(vfob/1000000.0,6);
  tft.setCursor(0, 90);
  tft.println("mhz");
}
 
void loop() {
  button.loop();   // MUST call the loop() function first
  button2.loop();  // MUST call the loop() function first
 
  // read the current state of the rotary encoder's CLK pin
  CLK_state = digitalRead(CLK_PIN);
 
  // If the state of CLK is changed, then pulse occurred
  // React to only the rising edge (from LOW to HIGH) to avoid double count
  if (CLK_state != prev_CLK_state && CLK_state == HIGH) {
    // if the DT state is HIGH
    // the encoder is rotating in counter-clockwise direction => decrease the counter
    if (digitalRead(DT_PIN) == HIGH) {
      vfoa--;
      direction = DIRECTION_CCW;
    } else {
      // the encoder is rotating in clockwise direction => increase the counter
      vfoa++;
      direction = DIRECTION_CW;
    }
    updateDisplay = true;
  }
  // save last CLK state
  prev_CLK_state = CLK_state;
 
  // read the current state of the rotary encoder's CLK pin
  CLK_state2 = digitalRead(CLK_PIN2);
 
  // If the state of CLK is changed, then pulse occurred
  // React to only the rising edge (from LOW to HIGH) to avoid double count
  if (CLK_state2 != prev_CLK_state2 && CLK_state2 == HIGH) {
    // if the DT state is HIGH
    // the encoder is rotating in counter-clockwise direction => decrease the counter
    if (digitalRead(DT_PIN2) == HIGH) {
      vfob--;
      direction2 = DIRECTION_CCW;
    } else {
      // the encoder is rotating in clockwise direction => increase the counter
      vfob++;
      direction2 = DIRECTION_CW;
    }
    updateDisplay = true;
  }
  // save last CLK state
  prev_CLK_state2 = CLK_state2;
 
  if (updateDisplay) {
    updateDisplay = false;
 
    //to convert count to vfo and display
    tft.fillScreen(ST77XX_BLACK);
 
    tft.setTextColor(ST77XX_YELLOW);
    tft.setCursor(0, 70);
    tft.println(vfob/1000000.0,6);
    tft.setCursor(0, 90);
    tft.println("mhz");
    tft.fillScreen(ST77XX_BLACK);
    tft.setTextColor(ST77XX_GREEN);
    tft.setTextSize(2);
    tft.setCursor(0, 20);
    tft.println(vfoa/1000000.0,6);
    tft.setCursor(0, 40);
    tft.println("mhz");
    tft.setTextColor(ST77XX_YELLOW);
    tft.setCursor(0, 70);
    tft.println(vfob/1000000.0,6);
    tft.setCursor(0, 90);
    tft.println("mhz");
  }
 
  if (button.isPressed()) {
    Serial.println("button1 is pressed");
  }
 
  if (button2.isPressed()) {
    Serial.println("button2 is pressed");
  }
}
[/code]
 

Offline drspasticTopic starter

  • Contributor
  • Posts: 36
  • Country: bg
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #21 on: January 28, 2025, 07:28:12 pm »
next step was seperating the variable for the display. after a lot of problems with variable type i got this:

Code: [Select]
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <ezButton.h>  // the library to use for SW pin on encoder
 
#define CLK_PIN 34  // ESP32 pin GPIO25 connected to the rotary encoder's CLK pin
#define DT_PIN 35   // ESP32 pin GPIO26 connected to the rotary encoder's DT pin
#define SW_PIN 32   // ESP32 pin GPIO27 connected to the rotary encoder's SW pin
 
#define CLK_PIN2 4  // ESP32 2nd connected  rotary encoder's CLK pin
#define DT_PIN2 16   // ESP32 2nd connected  rotary encoder's DT pin
#define SW_PIN2 17   // ESP32 2nd connected  rotary encoder's SW pin
 
#define DIRECTION_CW 0   // clockwise direction rotary
#define DIRECTION_CCW 1  // counter-clockwise direction rotary
 
#define TFT_CS 5
#define TFT_RST 2
#define TFT_DC 0
#define TFT_BL 22  // LED back-light

 

 
//int counter = 0;                //rotary
long long vfoa = 740000000;
int direction = DIRECTION_CW;  //rotary
int CLK_state;                 //rotary
int prev_CLK_state;            //rotary
ezButton button(SW_PIN);       // create ezButton object that attach to pin 32;
 
long long vfob = 2400000000;
int direction2 = DIRECTION_CW;  //rotary2
int CLK_state2;                 //rotary2
int prev_CLK_state2;            //rotary2
ezButton button2(SW_PIN2);      // create ezButton object that attach to pin ;
 
bool updateDisplay = false;


                      //display variables
                      float vfoadisplay = (vfoa/1000000.0);
                      float vfobdisplay = (vfob/1000000.0);

 
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);  // hardware SPI
 
 
void setup(void) {
 
  tft.initR(INITR_BLACKTAB);  // Init ST7735S chip, black tab
 
  // configure encoder pins as inputs
  pinMode(CLK_PIN, INPUT);
  pinMode(DT_PIN, INPUT);
  button.setDebounceTime(50);  // set debounce time to 50 milliseconds
 
  // read the initial state of the rotary encoder's CLK pin
  prev_CLK_state = digitalRead(CLK_PIN);
 
  // configure encoder pins as inputs
  pinMode(CLK_PIN2, INPUT);
  pinMode(DT_PIN2, INPUT);
  button2.setDebounceTime(50);  // set debounce time to 50 milliseconds
 
  // read the initial state of the rotary encoder's CLK pin
  prev_CLK_state2 = digitalRead(CLK_PIN2);


 
           //display variables
            float vfoadisplay = (vfoa/1000000.0);
            float vfobdisplay = (vfob/1000000.0);

 
              pinMode(TFT_BL, OUTPUT);
              digitalWrite(TFT_BL, HIGH);
              tft.setRotation(1);
              tft.fillScreen(ST77XX_BLACK);
 
              tft.setTextColor(ST77XX_GREEN);
              tft.setTextSize(2);
              tft.setCursor(0, 20);
              tft.println((vfoadisplay),6);
              tft.setCursor(0, 40);
              tft.println("mhz");
              tft.setTextColor(ST77XX_YELLOW);
              tft.setCursor(0, 70);
              tft.println((vfobdisplay),6);
              tft.setCursor(0, 90);
              tft.println("mhz");
}
 
void loop() {
  button.loop();   // MUST call the loop() function first
  button2.loop();  // MUST call the loop() function first
 
  // read the current state of the rotary encoder's CLK pin
  CLK_state = digitalRead(CLK_PIN);
 
  // If the state of CLK is changed, then pulse occurred
  // React to only the rising edge (from LOW to HIGH) to avoid double count
  if (CLK_state != prev_CLK_state && CLK_state == HIGH) {
    // if the DT state is HIGH
    // the encoder is rotating in counter-clockwise direction => decrease the counter
    if (digitalRead(DT_PIN) == HIGH) {
      vfoa--;
      direction = DIRECTION_CCW;
    } else {
      // the encoder is rotating in clockwise direction => increase the counter
      vfoa++;
      direction = DIRECTION_CW;
    }
    updateDisplay = true;
  }
  // save last CLK state
  prev_CLK_state = CLK_state;
 
  // read the current state of the rotary encoder's CLK pin
  CLK_state2 = digitalRead(CLK_PIN2);
 
  // If the state of CLK is changed, then pulse occurred
  // React to only the rising edge (from LOW to HIGH) to avoid double count
  if (CLK_state2 != prev_CLK_state2 && CLK_state2 == HIGH) {
    // if the DT state is HIGH
    // the encoder is rotating in counter-clockwise direction => decrease the counter
    if (digitalRead(DT_PIN2) == HIGH) {
      vfob--;
      direction2 = DIRECTION_CCW;
    } else {
      // the encoder is rotating in clockwise direction => increase the counter
      vfob++;
      direction2 = DIRECTION_CW;
    }
    updateDisplay = true;
  }
  // save last CLK state
  prev_CLK_state2 = CLK_state2;


                    //update display variables
                    float vfoadisplay = (vfoa/1000000.0);
                    float vfobdisplay = (vfob/1000000.0);

 
                    if (updateDisplay) {
                    updateDisplay = false;
 
                    //to convert count to vfo and display
                    tft.fillScreen(ST77XX_BLACK);
 
                    tft.setTextColor(ST77XX_YELLOW);
                    tft.setCursor(0, 70);
                    tft.println((vfobdisplay),6);
                    tft.setCursor(0, 90);
                    tft.println("mhz");
                    tft.fillScreen(ST77XX_BLACK);
                    tft.setTextColor(ST77XX_GREEN);
                    tft.setTextSize(2);
                    tft.setCursor(0, 20);
                    tft.println((vfoadisplay),6);
                    tft.setCursor(0, 40);
                    tft.println("mhz");
                    tft.setTextColor(ST77XX_YELLOW);
                    tft.setCursor(0, 70);
                    tft.println((vfobdisplay),6);
                    tft.setCursor(0, 90);
                    tft.println("mhz");
  }
 
  if (button.isPressed()) {
    Serial.println("button1 is pressed");
  }
 
  if (button2.isPressed()) {
    Serial.println("button2 is pressed");
  }
}


which gives the correct format display but now the encoders stopped working! the display flickers as i turn them so they do something but either its not displayed or its not counting
 

Offline drspasticTopic starter

  • Contributor
  • Posts: 36
  • Country: bg
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #22 on: January 29, 2025, 12:06:25 am »
Code: [Select]
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <ezButton.h>

#define CLK_PIN 34
#define DT_PIN 35
#define SW_PIN 32

#define CLK_PIN2 4
#define DT_PIN2 16
#define SW_PIN2 17

#define DIRECTION_CW 0
#define DIRECTION_CCW 1

#define TFT_CS 5
#define TFT_RST 2
#define TFT_DC 0
#define TFT_BL 22

long long vfoa = 740000000;
long long vfob = 2400000000;
long long adjustmentA = 1;
long long adjustmentB = 1;
int direction;
int direction2;
int prev_CLK_state, prev_CLK_state2;
bool updateVFOA = false, updateVFOB = false;

Adafruit_ST7735 tft(TFT_CS, TFT_DC, TFT_RST);
ezButton button(SW_PIN);
ezButton button2(SW_PIN2);

void highlightDigitRightJustified(long long value, long long adjustment, int y, uint16_t normalColor, uint16_t highlightColor) {
    char buffer[15];  // Ensure buffer size is large enough for leading zeros
    sprintf(buffer, "%010lld", value); // Format frequency as 10 digits with leading zeros

    // Calculate the total text width (12 pixels per character for text size 2)
    int textWidth = strlen(buffer) * 12;

    // Starting X position for right justification (160 is screen width)
    int xStart = 160 - textWidth;

    // Find the index of the digit to highlight
    int digitIndex = 9 - log10(adjustment); // Determine which digit corresponds to the adjustment

    // Render the frequency string with highlighting
    for (int i = 0, pos = 0; i < strlen(buffer); i++) {
        // Highlight the correct digit
        if (pos == digitIndex) {
            tft.setTextColor(highlightColor, ST77XX_BLACK);
        } else {
            tft.setTextColor(normalColor, ST77XX_BLACK);
        }
        tft.setCursor(xStart + pos * 12, y);
        tft.print(buffer[i]);
        pos++;
    }
}

void updateDisplayVFOA() {
    tft.fillRect(0, 20, 160, 20, ST77XX_BLACK); // Clear the area
    highlightDigitRightJustified(vfoa, adjustmentA, 20, ST77XX_GREEN, ST77XX_RED);

    // Correctly justify "MHz" under the VFOA display
    tft.setCursor(124, 40); // Position "MHz" to be right-aligned
    tft.setTextColor(ST77XX_GREEN, ST77XX_BLACK);
    tft.print("MHz");
}

void updateDisplayVFOB() {
    tft.fillRect(0, 70, 160, 20, ST77XX_BLACK); // Clear the area
    highlightDigitRightJustified(vfob, adjustmentB, 70, ST77XX_YELLOW, ST77XX_RED);

    // Correctly justify "MHz" under the VFOB display
    tft.setCursor(124, 90); // Position "MHz" to be right-aligned
    tft.setTextColor(ST77XX_YELLOW, ST77XX_BLACK);
    tft.print("MHz");
}

void setup() {
    tft.initR(INITR_BLACKTAB);

    pinMode(CLK_PIN, INPUT);
    pinMode(DT_PIN, INPUT);
    pinMode(CLK_PIN2, INPUT);
    pinMode(DT_PIN2, INPUT);
    pinMode(TFT_BL, OUTPUT);
    digitalWrite(TFT_BL, HIGH);

    button.setDebounceTime(50);
    button2.setDebounceTime(50);

    prev_CLK_state = digitalRead(CLK_PIN);
    prev_CLK_state2 = digitalRead(CLK_PIN2);

    tft.setRotation(1);
    tft.fillScreen(ST77XX_BLACK);

    tft.setTextSize(2);
    updateDisplayVFOA();
    updateDisplayVFOB();
}

void loop() {
    button.loop();
    button2.loop();

    int CLK_state = digitalRead(CLK_PIN);
    if (CLK_state != prev_CLK_state && CLK_state == HIGH) {
        if (digitalRead(DT_PIN) == HIGH) {
            vfoa -= adjustmentA;
            direction = DIRECTION_CCW;
        } else {
            vfoa += adjustmentA;
            direction = DIRECTION_CW;
        }
        updateVFOA = true;
    }
    prev_CLK_state = CLK_state;

    int CLK_state2 = digitalRead(CLK_PIN2);
    if (CLK_state2 != prev_CLK_state2 && CLK_state2 == HIGH) {
        if (digitalRead(DT_PIN2) == HIGH) {
            vfob -= adjustmentB;
            direction2 = DIRECTION_CCW;
        } else {
            vfob += adjustmentB;
            direction2 = DIRECTION_CW;
        }
        updateVFOB = true;
    }
    prev_CLK_state2 = CLK_state2;

    if (updateVFOA) {
        updateVFOA = false;
        updateDisplayVFOA();
    }

    if (updateVFOB) {
        updateVFOB = false;
        updateDisplayVFOB();
    }

    if (button.isPressed()) {
        adjustmentA *= 10;
        if (adjustmentA > 1000000000) {
            adjustmentA = 1;
        }
        updateVFOA = true;
    }

    if (button2.isPressed()) {
        adjustmentB *= 10;
        if (adjustmentB > 1000000000) {
            adjustmentB = 1;
        }
        updateVFOB = true;
    }
}



any thoughts about this?
 

Online peter-h

  • Super Contributor
  • ***
  • Posts: 4501
  • Country: gb
  • Doing electronics since the 1960s...
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #23 on: January 29, 2025, 04:39:47 pm »
I haven't read the whole thread but normally shaft encoders are done with an arrangement with two counters, or one up-down counter with some logic in front of it.

I did this about 40 years ago with the Z80-CTC which was perfect for this job (with some 4000 logic). There may even be an appnote still online somewhere. This scheme did not need interrupts; the CPU merely had to read the CTC frequently enough so the 16 bit counters did not overflow (a wrap around was inevitable but the scheme dealt with it cleverly).

I imagine people do it with interrupts from the encoder edges nowadays. You have to correctly handle the case where the encoder is stationary and vibrating around one of the edges :)
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline drspasticTopic starter

  • Contributor
  • Posts: 36
  • Country: bg
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #24 on: January 29, 2025, 09:55:42 pm »
half borrowed, half i did myself then i let chatgpt optimise it and tidy it up. it works ok but the red colour denoting cursor loses allignment with the active digit under certain circumstances. maybe i will need to set limit stops..
i cut my teeth on z80 opcode. welcome to the grandad club!
 

Online peter-h

  • Super Contributor
  • ***
  • Posts: 4501
  • Country: gb
  • Doing electronics since the 1960s...
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #25 on: January 30, 2025, 07:19:41 am »
Quote
i let chatgpt optimise it and tidy it up

You mean somebody did it before and chatgpt found it :) Chatgpt cannot actually write code - it is merely a text analysis machine. But it does know sentences start with an uppercase letter ;)

Funny about grandad - you aren't wrong :)
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline drspasticTopic starter

  • Contributor
  • Posts: 36
  • Country: bg
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #26 on: January 30, 2025, 02:07:16 pm »
yeah it just removed a lot of duplicate lines and made it look tidy.
 

Offline OLderDan

  • Regular Contributor
  • *
  • Posts: 60
  • Country: au
Re: please someone unmuddle me. trying to get a rotary encoder working
« Reply #27 on: March 02, 2025, 12:51:29 am »
...i cut my teeth on z80 opcode. welcome to the grandad club!

61 here and still every time i see c3, i think jump!
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf