Do while loop of u8g page drawing mode stops everything else in void loop

Hi, I am new to programing and I am trying to use a rotary encoder with 128x64 oled display. I am trying to create a menu that can be scrolled through using the rotary encoder. While testing the rotary encoder, I found out it jumps quite a lot, so I used Bounce2 library and few extra lines of code to get the encoder working properly.

#include <Bounce2.h>
#include "U8glib.h"
#define ENCODER_CLK 3
#define ENCODER_DT 2
#define ENCODER_SW 4

//buttons
Bounce2::Button encoder_clk = Bounce2::Button();
Bounce2::Button encoder_dt = Bounce2::Button();
Bounce2::Button encoder_sw = Bounce2::Button();
#define LED_PIN 13

bool led_state = LOW;

void setup() {
  
  //serial monitor
  Serial.begin(115200);
  
  //encoder and button debouncing
  encoder_clk.attach( ENCODER_CLK, INPUT);
  encoder_dt.attach( ENCODER_DT, INPUT);
  encoder_sw.attach( ENCODER_SW, INPUT_PULLUP);
  
  encoder_clk.interval(5);
  encoder_dt.interval(5);
  encoder_sw.interval(5);

  encoder_clk.setPressedState(LOW);
  encoder_dt.setPressedState(LOW);
  encoder_sw.setPressedState(LOW);
  
  //test led
  pinMode(LED_PIN,OUTPUT);
  digitalWrite(LED_PIN,led_state);
  

}


void loop() {
  
  encoder_clk.update();
  encoder_dt.update();
  encoder_sw.update();
  
  if (encoder_clk.changed()) {
    // There was a change on the CLK pin
    bool dtValue = encoder_dt.read();
    bool ckValue = encoder_clk.read();
    if (ckValue == LOW && dtValue == HIGH) {
      Serial.println("Rotated clockwise ⏩");
    }
    if (ckValue == LOW && dtValue == LOW) {
      Serial.println("Rotated counterclockwise ⏪");
    }
  }

  if(encoder_sw.pressed()){
    led_state = !led_state;
     digitalWrite(LED_PIN, led_state);
     Serial.println("Button Pressed");
  }
}

Using above code I was able to get proper clock wise and counter-clock wise serial prints of rotary encoder movements without any bounce.
But then I decided to test the oled

The display works properly but the rotary encoder movement are not being registered.
Can anyone tell me what I am doing wrong and what is the proper way to do it.
Above is my wokwi simulation project. I have teste this same setup and code in hardware aswell and it doesn't work.

This is the simulation of rotary encoder only

Your subject explains the problem. You'll have to switch to a different way to read the encoder, which as you see the way you do it needs constant attention.

I googled

  interrupt driven rotary arduino

to be sure you'll get lotsa hits when you do that. Sry I can't.], but maybe someone has a favorite rotary encoder library.

By using interrupts, the encoder is able to count whilst the main line code is doing the display update.

Although it occurs to me that this too may not work if U8glib turns off interrupts. So far I can neither confirm nor deny this is the case, looking more now.

a7

Does the thread title have ANYTHING to do with the question asked???

Hi @chizbiz ,

I took your Wokwi simulation and modified it so that it works:

Sketch:

/*
  Forum: https://forum.arduino.cc/t/do-while-loop-of-u8g-page-drawing-mode-stops-everything-else-in-void-loop/1240533
  Wokwi: https://wokwi.com/projects/393526781879393281

  2024/03/27
  ec2021

*/


#include "U8glib.h"
#define ENCODER_CLK 3
#define ENCODER_DT 2
#define ENCODER_SW 4

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); // Fast I2C / TWI

//images
// 'icon_dashboard', 16x16px
const unsigned char epd_bitmap_icon_dashboard [] PROGMEM = {
  0x07, 0xe0, 0x18, 0x18, 0x21, 0x24, 0x50, 0x02, 0x48, 0x0a, 0x84, 0x01, 0x83, 0x81, 0xa2, 0x45,
  0x82, 0x41, 0x81, 0x81, 0xa0, 0x05, 0x40, 0x02, 0x4b, 0xd2, 0x23, 0xc4, 0x18, 0x18, 0x07, 0xe0
};
// 'icon_battery', 16x16px
const unsigned char epd_bitmap_icon_battery [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf8, 0x40, 0x04, 0x5b, 0x66, 0x5b, 0x66,
  0x5b, 0x66, 0x40, 0x04, 0x3f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// 'icon_parksensor', 16x16px
const unsigned char epd_bitmap_icon_parksensor [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x22, 0x00, 0x25, 0x00, 0xf9, 0x00, 0x00, 0x81,
  0x0c, 0x85, 0x12, 0x95, 0xd2, 0x95, 0x0c, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// 'scrollbar_background', 8x64px
const unsigned char epd_bitmap_scrollbar_background [] PROGMEM = {
  0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02,
  0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02,
  0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02,
  0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00
};
// 'item_sel_outline', 128x21px
const unsigned char epd_bitmap_item_sel_outline [] PROGMEM = {
  0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
  0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
  0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0,
  0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0
};

const int NUM_ITEMS = 3;
char menu_item[NUM_ITEMS][20] {
  {"Battery"},
  {"Set Timer"},
  {"4WD Status"}
};

// Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 144)
const int epd_bitmap_allArray_LEN = 3;
const unsigned char* epd_bitmap_allArray[3] = {
  epd_bitmap_icon_battery,
  epd_bitmap_icon_dashboard,
  epd_bitmap_icon_parksensor
};

// Global variable that holds the values
//  0 = not rotated
//  1 = clockwise
// -1 = counterclockwise
// declared volatile to avoid compiler optimization
volatile int rotation = 0;

#define LED_PIN 13
bool led_state = LOW;

void setup() {

  //serial monitor
  Serial.begin(115200);
  // Set the encoder pins as required
  pinMode(ENCODER_CLK, INPUT);
  pinMode(ENCODER_DT, INPUT);
  pinMode(ENCODER_SW, INPUT_PULLUP);
  // Attach the function readEncoder to an interrupt on ENCODER_CLK pin on a falling edge
  attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), readEncoder, FALLING);

  //test led
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, led_state);

  //oled screen
  u8g.setFont(u8g_font_tpssb);
  u8g.setColorIndex(1);

}

void loop() {
  u8g.firstPage();
  do {
    u8g.drawBitmapP( 4, 2, 16 / 8, 16, epd_bitmap_allArray[0]);
    u8g.setFont(u8g_font_7x14);
    u8g.drawStr(26, 15, menu_item[0]);

    u8g.drawBitmapP( 4, 24, 16 / 8, 16, epd_bitmap_allArray[1]);
    u8g.setFont(u8g_font_7x14B);
    u8g.drawStr(26, 37, menu_item[1]);

    u8g.drawBitmapP( 4, 46, 16 / 8, 16, epd_bitmap_allArray[2]);
    u8g.setFont(u8g_font_7x14);
    u8g.drawStr(26, 59, menu_item[2]);

    u8g.drawBitmapP( 0, 22, 128 / 8, 21, epd_bitmap_item_sel_outline);
    u8g.drawBitmapP( 120, 0, 8 / 8, 64, epd_bitmap_scrollbar_background);
    // Call the encoder handler inside the ug8 while loop()
    handleEncoder();
  } while ( u8g.nextPage() );

}


// Interrupt routine to change the global variable rotation in case of a falling edge detected
// on ENCODER_CLK pin
void readEncoder() {
  int dtValue = digitalRead(ENCODER_DT);
  if (dtValue == HIGH) {
    rotation = 1; // Clockwise
  }
  if (dtValue == LOW) {
    rotation = -1; // Counterclockwise
  }
}

// Function to copy the value of rotation to another variable while
// interrupts are "switched off" and then returns this value to the Encoder handler routine
// After this rotation is reset to 0 so that it is set again by the next interrupt
int getRotation(){
  noInterrupts();
  int rot = rotation;
  rotation = 0;
  interrupts();
  return rot;
}


// Handles the value returned from getRotation() and also checks the 
// Encoder switch state
// If ENCODER_SW was LOW it ignores this state for 500 ms to avoid 
// bouncing and multiple detections
void handleEncoder() {
  static unsigned long lastPress = 0;
  switch (getRotation()) {
    case -1 :  Serial.println("Rotated counterclockwise ⏪");
      break;
    case 1 :  Serial.println("Rotated clockwise ⏩");
      break;
  }
  if (digitalRead(ENCODER_SW) == LOW && millis()-lastPress > 500) {
    lastPress = millis();
    led_state = !led_state;
    digitalWrite(LED_PIN, led_state);
    Serial.println("Button Pressed");
  }
}

You can test it here:

Changes:

For further information see the comments in the sketch ...

Good luckj
ec2021

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.