1

I'm having a really strange problem with my screen. It's connected in i2c to my Arduino Nano and I'm using a rotary encoder to navigate through a menu.

At startup everything is fine, but when I change menus a couple of times, there are flickering artifacts that start showing up in the bottom right corner. Did anyone ever get that kind of localized noise?

I thought maybe it would be my refresh rate that was a bit too high, I only had 10ms. But I cranked it up to 50 and it didn't change a thing.

https://youtu.be/riwLRgtHG0g

EDIT: Thank you for your feedback! I tried a different approach, like you suggested, and got rid of all the calls to displayMenu() and the multiple while loops. The artifacts are smaller now, but they're still there...

They used to occur all the time and now they're just appearing during chooseValue() and action3() now. As if it was linked to the "value" variable. I don't get it...

Here's the updated code:

#include <Arduino.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_I2CDevice.h>
#include <math.h>

#include <RotaryEncoder.h>
#include <ButtonStates.h>

#define SCREEN_WIDTH    128
#define SCREEN_HEIGHT   64
#define OLED_RESET      -1                                                  // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS  0x3C

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

RotaryEncoder encoder(A2, A3);                                              // Rotary encoder connected to A2 and A3
ButtonSwitch button(2);                                                     // Rotary encoder button

int value = 0;                                                              // Value changed in one of the menus
int selection = 0;                                                          // Encoder position

struct menuItem {                                                           // Structure defining a menu item
  const char *name;                                                         // Item name
  void (*action)();                                                         // Pointer pointing to the function handling the action
};

struct menu {                                                               // Structure defining a menu
  char *name;                                                               // Menu name
  int items;                                                                // Number of items in the menu
  struct menuItem *collection;                                              // Array containing all the menu items
};

struct menuItem menu1collection[5];                                         // Array for the menu
struct menuItem item1, item2, item3, item4, item5;                          // Items of the menu
struct menu menu1 = { "Today's menu", 5, menu1collection };                 // Structure holding all the menu's info

struct menuItem menu2collection[4];
struct menuItem item6, item7, item8, item9;
struct menu menu2 = { "Tomorrow's menu", 4, menu2collection };

struct menu currentmenu = menu1;

enum screens { MENU, CHOOSEVAL };
enum screens screen = MENU;

void chooseValue(){
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 20);
  display.write( 17 );
  display.setCursor(45, 20);
  display.print(selection);
  display.print("g");
  display.setCursor(115, 20);
  display.write( 16 );
  display.display();
}

void displayMenu(){                                                         // Function displaying the menu passed in argument
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 5);
  display.println(currentmenu.name);                                        // Display menu title

  int div = floor((selection-0.5)/4);                                       // Handle menus with more than 4 elements

  for(int i=(4*div+1), j=0 ; i<=currentmenu.items && j<4 ; i++, j++){       // Cycle through 4 menu items
    if(i==selection){                                                       // Highlight selected item
      display.fillRect(0, 18+10*j, 128, 10, SSD1306_WHITE);
      display.setCursor(20, 20+10*j);
      display.setTextColor(SSD1306_BLACK);
      display.println(currentmenu.collection[i-1].name);
    }
    else{
      display.setTextColor(SSD1306_WHITE);
      display.setCursor(20, 20+10*j);
      display.println(currentmenu.collection[i-1].name);                    // Display item
    }
  }
  display.display();
}

ISR(PCINT1_vect) {                                                          // Interrupt routine for the rotary encoder
  encoder.tick();                                                           // Check the state of the encoder
}

// Functions handling the actions for each menu item ------------------------------------------------------------------
void action1(){ 
  screen = CHOOSEVAL;
}

void action2(){          
  currentmenu = menu2;
  encoder.setPosition(1);
}

void action3(){                  
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(45, 20);
  display.print(value);
  display.println("g");
  display.display();

  delay(1000);
}

void action4(){  
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 20);
  display.println("Action 4!");
  display.display();

  delay(500);
}

void action5(){ 
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 20);
  display.println("Action 5!");
  display.display();

  delay(500);
}

void action6(){
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 20);
  display.println("Action 6!");
  display.display();

  delay(500);
}

void action7(){
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 20);
  display.println("Action 7!");
  display.display();

  delay(500);
}

void action8(){
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 20);
  display.println("Action 8!");
  display.display();

  delay(500);
}

void action9(){
  currentmenu = menu1;
  encoder.setPosition(1);
}
// -------------------------------------------------------------------------------------------------------------------

void handleMenu(){
}

void setup() {
  Serial.begin(9600);

  // Creating a menu requires the following ----------------------------------------------------------------------------
  item1.name = "Choose a value";
  item1.action = action1;
  menu1collection[0] = item1;

  item2.name = "Go to menu 2";
  item2.action = action2;
  menu1collection[1] = item2;

  item3.name = "Display value";
  item3.action = action3;
  menu1collection[2] = item3;

  item4.name = "Item 4";
  item4.action = action4;
  menu1collection[3] = item4;

  item5.name = "Item 5";
  item5.action = action5;
  menu1collection[4] = item5;

  item6.name = "Item 6";
  item6.action = action6;
  menu2collection[0] = item6;

  item7.name = "Item 7";
  item7.action = action7;
  menu2collection[1] = item7;

  item8.name = "Item 8";
  item8.action = action8;
  menu2collection[2] = item8;

  item9.name = "< Back";
  item9.action = action9;
  menu2collection[3] = item9;
  // -------------------------------------------------------------------------------------------------------------------

  PCICR |= (1 << PCIE1);                                                  // This enables Pin Change Interrupt 1 that covers the Analog input pins or Port C.
  PCMSK1 |= (1 << PCINT10) | (1 << PCINT11);                              // This enables the interrupt for pin 2 and 3 of Port C.

  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {              // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);                                                              // Don't proceed, loop forever
  }

  // Init display
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 20);
  display.println("Loading...");
  display.display();

  delay(500);
}

void loop() {
  switch(screen){
    case MENU:
      selection = encoder.getPosition();                                      // Get the encoder's position

      if (selection < 1) {                                                    // Stop the encoder on the min position
        encoder.setPosition(1);
        selection = 1;

      } else if (selection > currentmenu.items) {                             // Stop the encoder on the max position (number of items minus 1)
        encoder.setPosition(currentmenu.items);
        selection = currentmenu.items;
      }

      displayMenu();

      if (button.triggerSingle()){                                    
        currentmenu.collection[selection-1].action();                         // If a click is detected do the item's action
      }
      break;
    case CHOOSEVAL:
      selection = value + encoder.getPosition() * 10;

      if (selection < 1) {                                                    // Stop the encoder on the min position
        encoder.setPosition(1);
        selection = 1;
      } 

      chooseValue();

      if (button.triggerSingle()){                                    
        value = selection;
        encoder.setPosition(1);
        screen = MENU;
      }
      break;
  }
}
10
  • You called the displayMenu method at all over the program and recursive in itself. Why? Also, you use an eternal while (1) { in the display method. I suggest to remove the 'while(1)' loop. The only call to displayMenu that make sense, is the one in the loop() method. remove all others. I would also copy the code that starts actions from the display to the loop. Don't use delay() in the display method that stops the whole system. Use millis() (the example Blinlking LED without Delay shows you how. Feb 14 at 9:50
  • Remove the while from the chooseValue method too. Then move it to the loop function. I'm not sure I have seen all problems, but that would be a good start. If it does not hel, edit the question wit the new observations. Feb 14 at 9:50
  • 1
    Thanks for your feedback @PeterPaulKiefer! I updated my code in the original post.
    – Mugen
    Feb 14 at 15:02
  • 1
    The fact that the corruption is in the bottom right means it's at the end of the screen buffer. That will be (one of) the last thing on the heap. The stack is above the heap and grows down into it. Too many nested function calls and local variables will cause the stack to grow downwards until it overlaps with the heap, and the end of your screen buffer, corrupting the screen buffer in the bottom right of the screen. Flatten your program. Reduce RAM usage.
    – Majenko
    Feb 14 at 15:06
  • 1
    For a start use F(...) around all string literals in print(...) calls and such.
    – Majenko
    Feb 14 at 16:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.