Rotary Encoder - only slow and twice -

i am in the last stage of my DieselHeaterEnhancer. It will controll a DieselHeater with various options. I created a Menu for these options. To change the Values (mostly the time) i use a rotary encoder.
So far everything works fine!
The only thing is that the rotary encoder is glitchy. You have to turn it very slowly for it to register the turn. Also it counts every turn as 2 turns. So it adds or subtracts twice the value it should.
Do you have any tips to how i can improve the coder?
Thx!

/**
  Name: DiselHeaterController
  Purpose: Controlling a Diesel Heater

  @author Peter Kirsch
  @version 1.0 20/10/2023
*/

//RotaryEncoder
#define inputCLK 2
#define inputDT 5
int aState;
int aLastState;

//TimerFunktion
int counter = 30;

//CountDownFunktion
float countDown = 60;

//RotaryEncoderSwitch
#define inputSW 4
int sw;  //Switch
int swValue;
int lastSwValue = 0;

//Button
#define button 7
int buttonVal;
int lastButtonVal = 0;
int menuVal = 1;
int buttonPeriod = 100;

//SerialPrint intervall
int serialPrintPeriod = 1000;

//Screen
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

//Heater
int relayPin = 6;         //relayPin Pin
int HeaterButton = 4000;  // Length of the HeaterControllerButton necessary to be HIGH

//millis
unsigned long timeOld;

//HeaterThermostate
bool heaterThermo = 0;

//DS18B20
// Include the libraries we need
#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 3

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
float tempC;

//Ob Heizun An oder Aus ist
bool heaterVal = 0;

void setup() {

  Serial.begin(9600);
  pinMode(inputCLK, INPUT);
  pinMode(inputDT, INPUT);
  pinMode(inputSW, INPUT_PULLUP);  //SwitchPin
  pinMode(relayPin, OUTPUT);       //relayPin
  digitalWrite(relayPin, HIGH);    //relayPin
  pinMode(button, INPUT);          //ButtonPin



  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  //DisplayStart
  display.clearDisplay();
  display.display();  //Update/WriteToDisplay

  aLastState = digitalRead(inputCLK);
  sensors.begin();  //DS18B20Start
}

void loop() {

  //Reading DS18B20
  ReadingTemp();

  //Serial.Print
  if (millis() - timeOld >= serialPrintPeriod) {
    Serial.print("buttonVal ");
    Serial.println(buttonVal);
    Serial.print("menuVal ");
    Serial.println(menuVal);
    Serial.print("counter ");
    Serial.println(counter);
    Serial.print("CountDown ");
    Serial.println(countDown);
    timeOld = millis();
  }

  //Buttonfunktion
  checkButton();

  //Heater ON & OFF
  while (menuVal == 1) {
    //Buttonfunktion
    checkButton();
    //Reading DS18B20
    sensors.requestTemperatures();             // Send the command to get temperatures
    float tempC = sensors.getTempCByIndex(0);  // We use the function ByIndex, and as an example get the temperature from the first sensor only.

    //DisplayText
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(0, 0);
    display.println("Heizung An & Aus");
    display.println("----------");
    display.println("Schaltet die Heizung an und nach ");
    display.print(counter);
    display.println(" Minuten ");
    display.println("---------");
    display.print("wieder aus.");
    display.setCursor(88, 57);
    display.print(tempC, 1);
    display.println(" C");
    display.display();

    //Rotary
    aState = digitalRead(inputCLK);
    if (aState != aLastState) {
      if (digitalRead(inputDT) != aState) {
        counter = counter - 5;
      } else {
        counter = counter + 5;
      }
    }
    aLastState = aState;

    //Timer Funktion
    sw = digitalRead(inputSW);
    if (sw == 0) {
      Serial.println(counter);
      //HeaterButton
      digitalWrite(relayPin, LOW);
      delay(HeaterButton);
      digitalWrite(relayPin, HIGH);
      //delay till heater on/off
      delay(counter * 60ul * 1000ul);
      //HeaterButton
      digitalWrite(relayPin, LOW);
      delay(HeaterButton);
      digitalWrite(relayPin, HIGH);
    }
  }

  //Heater OFF
  while (menuVal == 2) {
    //Buttonfunktion
    checkButton();
    //Reading DS18B20
    sensors.requestTemperatures();             // Send the command to get temperatures
    float tempC = sensors.getTempCByIndex(0);  // We use the function ByIndex, and as an example get the temperature from the first sensor only.

    //DisplayText
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(0, 0);
    display.println("Heizung Aus");
    display.println("----------");
    display.println("Schaltet die Heizung nach ");
    display.print(counter);
    display.println(" Minuten ");
    display.println("---------");
    display.print("aus.");
    display.setCursor(88, 57);
    display.print(tempC, 1);
    display.println(" C");
    display.display();

    //Rotary
    aState = digitalRead(inputCLK);
    if (aState != aLastState) {
      if (digitalRead(inputDT) != aState) {
        counter = counter - 5;
      } else {
        counter = counter + 5;
      }
    }
    aLastState = aState;

    //Timer Funktion
    sw = digitalRead(inputSW);
    if (sw == 0) {
      Serial.println(counter);
      //delay till heater on/off
      delay(counter * 60ul * 1000ul);
      //HeaterButton
      digitalWrite(relayPin, LOW);
      delay(HeaterButton);
      digitalWrite(relayPin, HIGH);
    }
  }

  //Heater Delay ON&OFF
  while (menuVal == 3) {
    //Buttonfunktion
    checkButton();
    //Reading DS18B20
    sensors.requestTemperatures();             // Send the command to get temperatures
    float tempC = sensors.getTempCByIndex(0);  // We use the function ByIndex, and as an example get the temperature from the first sensor only.

    //DisplayText
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(0, 0);
    display.println("Heizung An");
    display.println("----------");
    display.println("Schaltet die Heizung nach ");
    if (countDown <= 60) {
      display.print("-> ");
      display.print(countDown);
      display.print(" <-");
      display.println(" Minuten ");
    } else {
      display.print("-> ");
      display.print(countDown / 60, 1);
      display.print(" <-");
      display.println(" Stunden ");
    }
    display.println("an und nach");
    display.print("-> ");
    display.print(counter);
    display.print(" <-");
    display.print(" Minuten ");
    display.print("wieder aus.");
    //Temperatur in der rechten unteren Ecke
    display.setCursor(88, 57);
    display.print(tempC, 1);
    display.println(" C");
    display.display();

    //Rotary
    aState = digitalRead(inputCLK);
    if (aState != aLastState) {
      if (digitalRead(inputDT) != aState) {
        countDown = countDown - 15;
      } else {
        countDown = countDown + 15;
      }
    }
    aLastState = aState;

    //SW && Timer Funktion
    sw = digitalRead(inputSW);
    if (sw == 0) {
      Serial.println(countDown);
      //delay till heater on
      delay(countDown * 60ul * 1000ul);
      digitalWrite(relayPin, LOW);
      delay(HeaterButton);
      digitalWrite(relayPin, HIGH);
      //delay till heater on/off
      delay(counter * 60ul * 1000ul);
      //HeaterButton
      digitalWrite(relayPin, LOW);
      delay(HeaterButton);
      digitalWrite(relayPin, HIGH);

      //Buttonfunktion
      checkButton();
    }
  }

  //Heater Delay ON&OFF
  while (menuVal == 4) {
    //Buttonfunktion
    checkButton();

    //Reading DS18B20
    sensors.requestTemperatures();             // Send the command to get temperatures
    float tempC = sensors.getTempCByIndex(0);  // We use the function ByIndex, and as an example get the temperature from the first sensor only.

    //DisplayText
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(0, 0);
    display.println("Thermostate");
    display.println("----------");
    display.println("Heizung Aus/An = 0/1");
    display.setCursor(62, 40);
    display.print(heaterVal);
    display.setCursor(55, 45);
    display.print("---");
    display.setCursor(88, 57);
    display.print(tempC, 1);
    display.println(" C");
    display.display();

    //Rotary heaterVal
    aState = digitalRead(inputCLK);
    if (aState != aLastState) {
      if (digitalRead(inputDT) != aState) {
        heaterVal = 0;
      } else {
        heaterVal = 1;
      }
    }
    aLastState = aState;

    //SW Funktion
    sw = digitalRead(inputSW);
    if (sw == 0) {
      while (menuVal == 4) {
        if (tempC >= 15 && heaterVal == 1) {
          //HeaterButton
          digitalWrite(relayPin, LOW);
          delay(HeaterButton);
          digitalWrite(relayPin, HIGH);
          heaterVal = 0;
        }
        if (tempC <= 5 && heaterVal == 0) {
          //HeaterButton
          digitalWrite(relayPin, LOW);
          delay(HeaterButton);
          digitalWrite(relayPin, HIGH);
          heaterVal = 1;
        }
        //Buttonfunktion
        checkButton();
        //Reading DS18B20
        sensors.requestTemperatures();             // Send the command to get temperatures
        float tempC = sensors.getTempCByIndex(0);  // We use the function ByIndex, and as an example get the temperature from the first sensor only.
      }
    }
  }
}

And what is that rotary encoder? Why do you have one without detents?

This freezes the code AFAIK for 750ms.

Maybe read that sensor not more than once a second, and asynchronously.
Leo..

i have the ky-040. i dont know why you imply that i have an encoder without detents?

how do you know that it freezes the code? sadly the "glitching" was there before i added this function.

sensors.requestTemperatures(); waits for a new temp conversion.
That takes about 750ms when the DS18B20 is in (default) 12-bit mode.
As said, if you use the DS18B20 asynchronously every second, then you don't have that waiting problem. DS18B20 example code can tell you how to do that.

Did you first try the rotary encoder separately, without any other code.
Leo..

A rotary encoder will have at least two output pins. You only show one.

That is what will cause "glitches".

The documentation for that device shows the TWO output pins and the center, common pin that the other two pins connect to.

Hello pommel

Take a search engine of your choice and ask the WWW for 'NewEncoder.h' to collect some data to be sorted out to get the needed information.

Have a nice day and enjoy coding in C++.

Can u specify this?
I defined both (CLK & DT) Pins before setup. In Setup i defined them as InputPins. In Loop they are both present in the Rotary "function".
What did i Miss?
Thx

So what do i do?
Thx

Hi,
Can you write some code that JUST reads the encoder?
This will check that the communication with the encoder is good.
Forget your main code for a moment, and concentrate on reading your encoder reliably.

I assume if you turn the encoder clockwise or counter clockwise, from your code, the menu scrolls in the one direction.

What model Arduino are you using?
Do you have a DMM?

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?
Hand drawn and photographed is perfectly acceptable.
Please include ALL hardware, power supplies, component names and pin labels.

Do you have the 5V pi of the encoder connected to 5V?

A suggestion, put a 0.1uF capacitor between each of the outputs of the encoder and gnd. this will help debounce the encoder switches.

Thanks... Tom... :grinning: :+1: :coffee: :australia:

My bad. I was looking at inputSW and thinking that was how you were trying to read the encoder.
A couple of potential problems:

  • Your encoder read will only get half of the pulses because you're triggering on a state change of just one input. This is not a major problem but it does reduce the resolution.
  • A bigger problem is that the way you're doing it is not noise-resistant. Quadrature encoders follow a "Gray code" and the pulses change in a predetermined order. If you track that expected order, you are less likely to run into problems. I suggest you look for an arduino encoder library that manages all of that.
  • Does your encoder output HIGH and LOW signals or just switch to ground? Defining the inputs as INPUT instead of INPUT_PULLUP assumes that you are getting solid logic signals at the input pins.
  • Ideally you would isolate the code that reads the encoder into its own function. You have it in at least two different spots with a slightly different count each time.

This is the "Rotary Code" in it self. Its doing the same here. It is not working correct.

#define inputCLK 2
#define inputDT 7
int aState;
int aLastState = 1;
int counter = 0;

void setup() {
  Serial.begin(9600);
  pinMode(inputCLK, INPUT);
  pinMode(inputDT, INPUT);
}

void loop() {
  //Rotary
  aState = digitalRead(inputCLK);
  if (aState != aLastState) {
    if (digitalRead(inputDT) != aState) {
      counter = counter - 5;
    } else {
      counter = counter + 5;
    }
    Serial.println(counter);
  }
  aLastState = aState;
}

i get stuff like this as a result
image

Thx guys

I am looking at the thread for a schematic, but don't see one. Can you at least make a block diagram showing the system and all the connections?

I found a example Code. It worked with my button and i added the "thing" that worked.

    //Rotary
    aState = digitalRead(inputCLK);
    if (aState != aLastState && aState == 1) {
      if (digitalRead(inputDT) != aState) {
        counter = counter - 10;
      } else {
        counter = counter + 10;
      }
    }
    aLastState = aState;

i added "aState == 1". that did the trick. though i still have to rotate the knob very slowly for the value to change.

i think this is the problam. How do i check the DS18B20 asynchonously every second?

There are examples that show asynchronous DS18B20 reading in the DallasTemperature library examples.
The WaitForConversion examples.

I also suggest that you look at the Encoder library.

Sorry but Quadrature Encoders are not Gray Code.

Tom... :grinning: :+1: :coffee: :australia: