Rotary encoder couldn't work when using milis()

Although the code I put between two slash//// raw, works normally, but it doesn't work when using multitasking with milis()

int count = 0;
bool signupOK = false;
uint32_t Time_aht;
uint32_t Time_lim;
uint32_t Time_oled;
uint32_t Time_fb;
void setup() {
 Time_aht = millis();
 Time_lim = millis();
  Time_oled = millis();
   Time_fb = millis();
   pinMode (ppclk,INPUT);
   pinMode (dt,INPUT);   
   LastState = digitalRead(ppclk); 
}
void loop() {   
   if(Firebase.ready() && signupOK && (millis() > Time_fb)){
//codes
Time_fb = getStartTime(1000);
}
 if(millis() > Time_lim){

/////////////////////////////////////////////////

   State = digitalRead(ppclk); // Reads the "current" state of the clock pin
   if (State != LastState){     
     if (digitalRead(dt) != State) { 
       lim --;
     } else {
       lim ++;
     }
     delay(100);
     Serial.println(lim);
}
//////////////////////////////////////////////////////////
   } 
   if(millis() > Time_aht){
//codes
Time_aht = getStartTime(1000);
}
}

I'm trying to make a counter with a rotary encoder. It works when I use interrupt, but when I try to send rotary value to firebase, interrupt and firebase conflicts

//void IRAM_ATTR isrAB() {
ICACHE_RAM_ATTR void isrAB() {
// portENTER_CRITICAL_ISR(&gpioMux);

    int clk1 = digitalRead(bCLK);
   if (clk1 != prevClk1 && clk1 == LOW) {
  int dt1 = digitalRead(bDT);
 if (dt1==HIGH ) {
  lim++;
  if(lim>30)lim=30;
 }
else {
 lim--;
 if(lim<10)lim=10;
 }
}
delay(100);
Serial.println(lim);
prevClk1 = clk1;
haschanged(lim);
//portEXIT_CRITICAL_ISR(&gpioMux);
}
void haschanged(int newval){
    if(newval==oldval){
  lim=oldval;
  }else{
  lim=newval;
  oldval=newval;
  count=newval;
  
  }
fbsetlim();
//     Serial.println("lim");
//   Serial.println(lim);
  }
void fbsetlim(){
  if (Firebase.ready() && signupOK){
Firebase.RTDB.setInt(&fbdo, "test/lim", lim);
Serial.println("lim");
   Serial.println(lim);
   delay(500);
  }
}
void setup() {
  attachInterrupt(digitalPinToInterrupt(bDT) , isrAB, CHANGE);
  attachInterrupt(digitalPinToInterrupt(bCLK), isrAB, CHANGE);
}

Hello huseynik

The combination of the millis() and delay() function is a bad combination to receive the expected realtime behaiviour of the sketch.

I´hvnt check the complete sketch.

Have a nice day and enjoy programming in C++ and learning.
MIND THE GAP

1 Like

I use this code for my encoders:
arduino UNO:

#define ClockPin 2 // Must be pin 2 
#define DataPin 3 // Must be pin 3
#define readA bitRead(PIND,2)//faster than digitalRead()  (((value) >> (bit)) & 0x01)
#define readB bitRead(PIND,3)//faster than digitalRead()  (((value) >> (bit)) & 0x01)
volatile long count = 0;
long lastCtr;
unsigned long SpareCycles;

void Encoder(bool A) {
  (readB == A)  ? count++ : count--;
}

void setup() {
  Serial.begin(115200); //115200
  pinMode(ClockPin, INPUT);
  pinMode(DataPin, INPUT);
  /* 1 Step */
  attachInterrupt(digitalPinToInterrupt(ClockPin), [] {Encoder( true);}, RISING);
  /**/

  /*  2 step count
  attachInterrupt(digitalPinToInterrupt(ClockPin), [] {Encoder( readB);}, CHANGE);
  */
  
  /* Full 4 Step Count
  attachInterrupt(digitalPinToInterrupt(ClockPin), [] {Encoder( readB);}, CHANGE);
  attachInterrupt(digitalPinToInterrupt(DataPin ), [] {Encoder( !readA);}, CHANGE);
  */
}

void loop() {
  long Counter;
  SpareCycles++;
  noInterrupts ();
  Counter = count;
  interrupts ();
  if ((lastCtr != Counter) & (SpareCycles > 10)) {
    Serial.println(Counter);
    SpareCycles = 0;
  }
  lastCtr = Counter;
}

I'm not sure how to integrate firebase.
Z

1 Like

Thank you for your answer. I make comment the delay() when using milis. If you can, please help me how to fix my problems, first or other.
Thank you :slight_smile:

You interrupt routine should be short and light, get in get out. use volatile vars to exchange with other parts of your code.

My interrupt already works. Only get error when call firebase getInt or setInt. Do you have solution for this or another solution for second problem:
Using rotary code with milis without interrupt

It first works and then it doesn’t. whatever

When arrive at home, I send you entire code.It works first and also then . If I don't use data transfer from firebase, there's no problem. But I have to do.

I ask you a solution but you beat around the bush :slight_smile:

I don't need your code. Just know this, if it works in C++ that doesn't mean it would always work unless you know what you are doing and adhere to standard. Have fun

1 Like

Thank you for your effort. But we have no solution.
:pray:

Using encoder without interrupt is not possible.
You want a impossible solution with millis() for it.

This is an example of using a rotary encoder without interrupts. Note there are no delay() functions and this must run faster than the rotation of the rotary encoder
if any delay occurs between running the loop the encoder pulses may be missed.

  int dt = A0;
  int clk = A2;
  int counter;

void setup() {
  Serial.begin (9600);
  pinMode(clk, INPUT);
  pinMode(dt, INPUT);
}


void loop() {
  static int LastState;
  int Clock;
  if(LastState == (Clock = digitalRead(clk))) return;
  LastState = Clock;
  if(Clock == 0) return;
  //Clock went High
  (digitalRead(dt))? counter++: counter--;
  Serial.println(counter);
}

Z

1 Like

In addition, you may try to do this:

Add the following yield() function to your code:

int dt = A0;
int clk = A2;
volatile int counter;

void yield() {
  static int LastState;
  int Clock;
  if (LastState == (Clock = digitalRead(clk))) return;
  LastState = Clock;
  if (Clock == 0) return;
  //Clock went High
  (digitalRead(dt)) ? counter++ : counter--;
  Serial.println(counter);
}

void setup() {
  Serial.begin (9600);
  pinMode(clk, INPUT);
  pinMode(dt, INPUT);
}

void loop() {
  delay(1000); // The magical non blocking delay!!!!
}

If you find yourself using delay a lot and need the encoder to work. go ahead and use it a ton
delay() everywhere you want and see if this will capture your encoder correctly

how it works as part of the delay function

void delay( unsigned long ms )
{
  if (ms == 0)
  {
    return;
  }

  uint32_t start = micros();

  while (ms > 0)
  {
    yield();   //<<<<<<<<<<<<<<< The yield() function is executed here.
    while (ms > 0 && (micros() - start) >= 1000)
    {
      ms--;
      start += 1000;
    }
  }
}

So the delay will trigger yield and capture the encoder state changes.

Z

1 Like

I also took an hour to try to understand your code.
If you were to provide the entire sketch it would have helped.

here is an extract of your provided code that gives a direction as to what I would recommend doing. this is only a start and I'm sure there are errors as I didn't have enough information to check with a compiler.

bool signupOK = false;
volatile int lim = 10;

//void IRAM_ATTR isrAB() {
ICACHE_RAM_ATTR void isrAB {
  (digitalRead(bDT))  ? lim++ : lim--;
  if (lim > 30)lim = 30;
  if (lim < 10)lim = 10;
}

void setup() {
  Serial.begin(115200); //115200
  attachInterrupt(digitalPinToInterrupt(bCLK), isrAB, RISING);
}

void loop() {
  static int lastCtr;
  static unsigned long SpamTimer;
  int Counter;
  noInterrupts ();
  Counter = lim;
  interrupts ();
  if (lastCtr != Counter) {
    if (Firebase.ready() && signupOK) {
      Firebase.RTDB.setInt(&fbdo, "test/lim", Counter);
      if ((millis() - SpamTimer) >= (500)) {
        SpamTimer = millis();
        Serial.println("lim");
        Serial.println(lim);
      }
    }
    lastCtr = Counter;
  }
}

Z

1 Like

Yes you are right. I tought that on the bus today. It must be an interrupt but I thought how do I resolve the conflict with firebase. Here's a solution that will make firebase passive when interrupt is active. but how. Do you have an idea?

Thank you so much. Maybe this is the solution. Even if it doesn't work, your effort is invaluable. I'll try soon. I'll post the result here.

That is perfect. I love it. miracle as, you said

hi @zhomeslice
here is my code and yours integrated

 #include <Arduino.h>
#if defined(ESP32)
  #include <WiFi.h>
#elif defined(ESP8266)
  #include <ESP8266WiFi.h>
#endif
//#include<DHTesp.h>
#include <Wire.h>
#include <Firebase_ESP_Client.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_AHTX0.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);

//Provide the token generation process info.
#include "addons/TokenHelper.h"
//Provide the RTDB payload printing info and other helper functions.
#include "addons/RTDBHelper.h"

// Insert your network credentials
#define WIFI_SSID "xxx"
#define WIFI_PASSWORD "1234567a"

// Insert Firebase project API Key
#define API_KEY "AIzaSyDxRT8-0O8lIZnt9aWazSgo"

// Insert RTDB URLefine the RTDB URL */
#define DATABASE_URL "https://essu-default-rtdb.firebaseio.com/"
//Define Firebase Data object
FirebaseData fbdo;

FirebaseAuth auth;
FirebaseConfig config;

uint32_t Time_aht;
uint32_t Time_lim;
uint32_t Time_oled;
uint32_t Time_fb;

Adafruit_AHTX0 aht;
#define bCLK 2
#define bDT  0
#define bSW  5
int prevClk1 = HIGH;

//portMUX_TYPE gpioMux = portMUX_INITIALIZER_UNLOCKED;

bool signupOK = false;
String stringValue;
String stringVal;
float temperature;
float humidity;
int lim=10; // limit of temperature
int count = 10;
int oldval=10;
int newfb;


void drawUpdateOLED(float temp, float humi, int st) {
  delay(2000);
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(5, 5);
  display.println("Temp:");
  display.setCursor(65, 5);
  display.print(temp,1);

  display.setCursor(5, 20);
  display.println("Hum.:");
  display.setCursor(65, 20);
  display.print(humi,1);

  display.setCursor(5, 35);
  display.println("lim.:");
  display.setCursor(65, 35);
  display.print(st);
 display.drawRoundRect(10, 58, 102, 1, 1, WHITE);
  display.setCursor((st-10)*5,45) ;
  display.println(".");
 // display.fillRect(10, 56, (st-10)*5, 5, WHITE);
  display.display(); 
  

}
uint32_t getStartTime(uint32_t DataCaptureDelay) {
  return millis() + DataCaptureDelay;
}

///////////////////////// InitializeAHT
void initAHT(){
  if (! aht.begin()) {
    Serial.println("Could not find AHT? Check wiring");
    while (1) delay(10);
  }
  Serial.println("AHT10 or AHT20 found");
}
//////////////////////// Initialize WiFi
void initWiFi() {
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting to WF ..");
  delay(1000);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
   delay(1000);
  } 
  Serial.println(WiFi.localIP());
  Serial.println();
  /* Assign the api key (required) */
  config.api_key = API_KEY;

  /* Assign the RTDB URL (required) */
  config.database_url = DATABASE_URL;
  
  /* Sign up */
  if (Firebase.signUp(&config, &auth, "", "")){
    Serial.println("ok");
    signupOK = true;
  }
  else{
    Serial.printf("%s\n", config.signer.signupError.message.c_str());
  }
  /* Assign the callback function for the long running token generation task */
  config.token_status_callback = tokenStatusCallback; //see addons/TokenHelper.h
  
  Firebase.begin(&config, &auth);
  Firebase.reconnectWiFi(true);
}
///////////////////////////Rotary encoder
ICACHE_RAM_ATTR void isrAB() {
 (digitalRead(bDT))  ? lim++ : lim--;
  if (lim > 30)lim = 30;
  if (lim < 10)lim = 10;
}
//Serial.println(lim);
//haschanged(lim);
//}

/////////////////////////////check if last value has changed
//void haschanged(int newval){
//    if(newval==oldval){
//  lim=oldval;
//  }else{
//  lim=newval;
//  oldval=newval;
//  count=newval;
//  
//  }
//fbsetlim();
//     Serial.println("lim");
//   Serial.println(lim);
//  }
/////////////////////////////if last value has changed send firebase 
//void fbsetlim(){
//  if (Firebase.ready() && signupOK){
//Firebase.RTDB.setInt(&fbdo, "test/lim", lim);
//Serial.println("lim");
//   Serial.println(lim);
//   delay(500);
//  }
//}

void setup() {
  Serial.begin(115200);
    // Initialize oled screen
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  
  initWiFi();  // Initialize wifi
  initAHT();  // Initialize AHT sensor

   pinMode(bCLK, INPUT);
  pinMode(bDT, INPUT);
  pinMode(bSW, INPUT_PULLUP);
  Time_aht = millis();
 Time_lim = millis();
  Time_oled = millis();
   Time_fb = millis();
  attachInterrupt(digitalPinToInterrupt(bCLK), isrAB, RISING);
}

void loop() {
  static int lastCtr;
  static unsigned long SpamTimer;
  int Counter;
  noInterrupts ();
  Counter = lim;
  interrupts ();
  if (lastCtr != Counter) {
    if (Firebase.ready() && signupOK) {
      Firebase.RTDB.setInt(&fbdo, "test/lim", Counter);
if (millis() > Time_fb){
        Serial.println("lim");
        Serial.println(lim);
      }
    }
       lastCtr = Counter;
       Time_fb = getStartTime(1000);
  }
//Firebase.RTDB.setInt(&fbdo, "test/term", temperature);
//Firebase.RTDB.setInt(&fbdo, "test/hum", humidity);
//stringVal=String(lim);
//Firebase.RTDB.setInt(&fbdo, "test/lim", lim);
//if(Firebase.RTDB.getString(&fbdo, "/test/lim")){
// if (fbdo.dataType() == "string") {
//        stringValue = fbdo.stringData();
//       newfb=stringValue.toInt();
//      }   
//  }
//  haschanged(newfb);
// Time_fb = getStartTime(1000);
//}
//TEMP DATA
  if(millis() > Time_aht){
   sensors_event_t humi, temp;
  aht.getEvent(&humi, &temp);// populate temp and humidity objects with fresh data
 temperature=temp.temperature;
 humidity=humi.relative_humidity;
    Time_aht = getStartTime(3000);
  }
//if(millis() > Time_lim)
//  {
//    Time_lim = getStartTime(100);
//  }
  if(millis() > Time_oled)
  {
    drawUpdateOLED(temperature, humidity, Counter);
    Time_oled = getStartTime(1000);
  }
}

After turning the rotary encoder a few times, the value of the button goes to firebase. and after a while it becomes the initial value 10 again

I would slow things down
blink without delay timers around everything that transmits
Along time ago I had a lengthy discussion based on Blink without delay and we came up with this as the safe solution:

static unsigned long SpamTimer;
if ((millis() - SpamTimer) >= (1000)) {
    // Delayed Code Here
    SpamTimer= millis();
}

Note that there are issues with not doing the addition method, especially on millis() rollover events Subtraction method always works without issues.

// Flawed way to do Blink without delay.
static unsigned long SpamTimer;

if (millis() > SpamTimer) {
    // Delayed Code Here
  SpamTimer= millis() + 1000;
}

Suggestions to try on your loop() function:

void loop() {
  static int lastCtr;
  static unsigned long Time_fb,Time_aht,Time_oled; // These are now static and local variables
  int Counter;
  noInterrupts ();
  Counter = lim;
  interrupts ();
  if (lastCtr != Counter) {
    if ((millis() - Time_fb) >= 1000) { // Moved Spam timer to here Wrapping FireBase
      if (Firebase.ready() && signupOK) {
        Firebase.RTDB.setInt(&fbdo, "test/lim", Counter);
        Serial.println("lim");
        Serial.println(lim);
      }
      Time_fb = millis();
      lastCtr = Counter; // moved this to within the blink without delay to allow any changes to be captured and sent 
    }
  }
  if ((millis() - Time_aht) >= 3000) 
  {
    sensors_event_t humi, temp;
    aht.getEvent(&humi, &temp);// populate temp and humidity objects with fresh data
    temperature = temp.temperature;
    humidity = humi.relative_humidity;
    Time_aht = millis();
  }
  if ((millis() > Time_oled) >= 1000)
  {
    drawUpdateOLED(temperature, humidity, Counter);
    Time_oled = millis();
  }
}

Z

1 Like

You are King :crown:.

Thank you for your effort.
It s worked



///////////////////////////Rotary encoder
ICACHE_RAM_ATTR void isrAB() {
 (digitalRead(bDT))  ? lim-- : lim++;
  if (lim > 30)lim = 30;
  if (lim < 10)lim = 10;

}

void setup() {
  Serial.begin(115200);
    // Initialize oled screen
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  
  initWiFi();  // Initialize wifi
  initAHT();  // Initialize AHT sensor

   pinMode(bCLK, INPUT);
  pinMode(bDT, INPUT);
  pinMode(bSW, INPUT_PULLUP);
  Time_aht = millis();
 Time_lim = millis();
  Time_oled = millis();
   Time_fb = millis();
   Time_fbr = millis();
  attachInterrupt(digitalPinToInterrupt(bCLK), isrAB, RISING);
}
void loop() {
  static int lastCtr;
  static unsigned long Time_fb,Time_aht,Time_oled; // These are now static and local variables
  int Counter;
  noInterrupts ();
  Counter = lim;
  interrupts ();
  if (lastCtr != Counter) {
    if ((millis() - Time_fb) >= 500) { // Moved Spam timer to here Wrapping FireBase
      if (Firebase.ready() && signupOK) {
        stringVal=String(Counter);
        Firebase.RTDB.setString(&fbdo, "test/lim", stringVal);
      
//        Serial.print("lim");
//        Serial.println(Counter);
      }
      Time_fb = millis();
      lastCtr = Counter; // moved this to within the blink without delay to allow any changes to be captured and sent 
    }
  }
   if ((millis() - Time_fbr) >= 1000) 
  {
      if (Firebase.ready() && signupOK) {
       
        Firebase.RTDB.setInt(&fbdo, "test/term", temperature);
        Firebase.RTDB.setInt(&fbdo, "test/hum", humidity);
   
if(Firebase.RTDB.getString(&fbdo, "/test/lim")){
 if (fbdo.dataType() == "string") {
        stringValue = fbdo.stringData();
     
      }  
      newfb=stringValue.toInt(); 
  }
          Serial.print("fblim ");
        Serial.println(newfb);
        Serial.println(stringValue);
//  (newfb==lim)  ? lim : 
lim=newfb;
    }
    Time_fbr = millis();
    }
  if ((millis() - Time_aht) >= 3000) 
  {
    sensors_event_t humi, temp;
    aht.getEvent(&humi, &temp);// populate temp and humidity objects with fresh data
    temperature = temp.temperature;
    humidity = humi.relative_humidity;
    Time_aht = millis();
  }
  if ((millis() - Time_oled) >= 500)
  {
   
    drawUpdateOLED(temperature, humidity, lim);
    Time_oled = millis();
  }
}