OLED Adafruit_SSD1306.h library not working inside void loop()

Hi everybody,

a beginner looking for answer: why can i not run any command from the display library (Adafruit_SSD1306.h) inside "void loop()" ?

I stuck at a simple project with OLED display, RFID reader and 2 buttons. The hardware works just fine. If i run example separate scripts for display and RFID reader all works. but once i put it together in one script then the hardware is not working anymore as soon as i use a "display.*" command inside the "void loop()". Strange thing: there are no errors when compiling the script!

what should happen is: i place a RFID Chip on the board, if i then press one of the 2 buttons the display will show if it was button 1 or 2. and then restart the loop to check for RFID Chips again.

#include <SPI.h>                  // RC522 communication (four-wire serial bus)
#include <Wire.h>                 // OLED communication  (two-wire interface)
#include <MFRC522.h>              // RC522 library - RFID Reader
//#include <Adafruit_GFX.h>         // OLED library for graphic output
#include <Adafruit_SSD1306.h>     // OLED library for text output

#define SCREEN_WIDTH 128          // OLED display width, in pixels
#define SCREEN_HEIGHT 32          // OLED display height, in pixels
#define OLED_RESET     -1         // OLED Reset pin # (or -1 if sharing Arduino reset pin)
#define RST_PIN         7         // RC522 RESET pin - not used
#define SS_PIN          11        // RC522 SDA pin

int Button_Left = 5;              // BUTTON pin - left side
int Button_Right = 3;             // BUTTON pin - right side
int val; // Temporaere Variable   // BUTTON variable to save status - HIGH or LOW

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // OLED on I2C

MFRC522 mfrc522(SS_PIN, RST_PIN); // MFRC522 create instance

void setup() {
   // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  
  display.clearDisplay();             // Clear the initial buffer content - Adafruit Logo
  display.setTextSize(1);             // Draw X-scale text
  display.setTextColor(WHITE);        // Set text color to white
  display.setCursor(10, 0);           // Set cursor position
  display.println(F("starting..."));  // Output text on Display
  display.display();                  // Show display buffer - "starting"
  delay(2000);                        // Pause for 2 seconds
  display.clearDisplay();             // Clear the buffer
  display.display();                  // Show display buffer - empty screen     

  pinMode (Button_Left, INPUT) ;      // Initialising sensor pin
  digitalWrite(Button_Left, HIGH);    // Activating internal Pull-Up resistor
  pinMode (Button_Right, INPUT) ;     // Initialising sensor pin
  digitalWrite(Button_Right, HIGH);   // Activating internal Pull-Up resistor
  
	Serial.begin(9600);		              // Initialize serial communications with the PC
	while (!Serial);		                // Do nothing until serial port is opened 
	SPI.begin();			                  // Init SPI bus
	mfrc522.PCD_Init();		              // Init MFRC522
	mfrc522.PCD_DumpVersionToSerial();	// Show details of PCD - MFRC522 Card Reader details
	Serial.println(F("Scan RFID.."));   // Output on serial monitor
}

void loop() {
	if ( ! mfrc522.PICC_IsNewCardPresent()) { // Looo until a card is present
		return;
	}
	if ( ! mfrc522.PICC_ReadCardSerial()) {   // Select and read one of the cards
		return;
	}

  Serial.print("Printing HEX UID : ");                        // Output on serial monitor
  for (byte i = 0; i < mfrc522.uid.size; i++) {               // get RFID UID and print to serial monitor
    Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? "0" : "");
    Serial.print(mfrc522.uid.uidByte[i], HEX);
  } 
  Serial.println(""); // Output line break on serial monitor
  
  unsigned long starttime = millis();   // variable for loop time calculation
  unsigned long endtime = starttime;    // variable for loop time calculation
  while ((endtime - starttime) <=4000)  // loop for up to 4000ms to wait for user input (Buttons)
  {
    val = digitalRead(Button_Left);     // read pin(Button) input value
    if (val == LOW) {                   // check if the input is LOW (Button pressed)
      Serial.println("Check In");       // Output text on serial Monitor
      display.clearDisplay();           // Clear the buffer      
      display.println(F("Check In"));   // Output text on Display
      display.display();                // Show display buffer - "Check In"
      // future code to send Timestamp, RFID UID and Button status to SQL DB
      break;                            // Exit the while loop
    } 
    val = digitalRead(Button_Right);    // read pin(Button) input value
    if (val == LOW) {                   // check if the input is LOW (Button pressed)
      Serial.println("Check Out");      // Output text on serial Monitor
      display.clearDisplay();           // Clear the buffer
      display.println(F("Check Out"));  // Output text on Display
      display.display();                // Show display buffer - "Check Out"
      // future code to send Timestamp, RFID UID and Button status to SQL DB
      break;                            // Exit the while loop
    } 
        
    endtime = millis();                 // update variable for loop time calculation
    
  }
  delay(3000); // wait 3 seconds and restart the loop
}

i tried to put all content of "void loop()" inside a new function, calling it from within "void setup()", but still not working, same misbehaviour. On the serial monitor i get the output of the pressed button but after that the script stops working/responding.

Happy and thankful for any suggestion or idea! (and sorry for any wrong spelling, i'm not a native english speaker)

does the "starting" from the setup() section display ?

BabyGeezer:
does the "starting" from the setup() section display ?

yes, it does.

i'm still a bit n00b as well, and always slow to try and figure out using millis(), i'm not sure if you're using it right, redeclaring endtime for every loop ?

try displaying something at the start of the loop - independent of the button press/RFID detect section.

some tips;

you can replace

  pinMode (Button_Left, INPUT) ;      // Initialising sensor pin
  digitalWrite(Button_Left, HIGH);    // Activating internal Pull-Up resistor

with just

pinMode (Button_Left, INPUT_PULLUP) ;

plus

int Button_Left = 5;

uses up 2 bytes of memory, whereas byte Button_Left = 5; only 1 byte.
for values that are within the 0-255 range, it is a good habit to use byte instead of int for variable declaration.

and, because it never changes, you can also add the const keyword, so;
const byte Button_Left = 5;

@BabyGeezer: thank you for your ideas and input

BabyGeezer:
i'm still a bit n00b as well, and always slow to try and figure out using millis(), i'm not sure if you're using it right, redeclaring endtime for every loop ?

The loop works fine. if i delete all display.* functions the script works fine, looping all over again and sending the correct output to serial monitor. redeclaring "endtime" is necessary to check the difference between "starttime" and "endtime". if the difference is bigger then 4000ms then the loop will exit.

BabyGeezer:
try displaying something at the start of the loop - independent of the button press/RFID detect section.

it will not show on the display. as soon as i call any display.* function inside "void loop()" the whole script stops.

helmutgiersch:
...
it will not show on the display.
...

i have tried the code on a Nano and 128x64 OLED - it works - but it scrolls the text (off the screen) !

so you should add ;

display.setCursor(0,0);

BabyGeezer:
so you should add ;

display.setCursor(0,0);

I did update the code with all sugestions but still the same result.
But i was able to trace down the error a bit closer.

i added some output for the serial monitor to see where exactly the hardware freeze starts. it is the display.display(); that freezes the hardware. This is the code i used to trace the error:

    val = digitalRead(Button_Left);     // read pin(Button) input value
    if (val == LOW) {                   // check if the input is LOW (Button pressed)
      Serial.println("next comes clearDisplay buffer");       // Output text on serial Monitor
      display.clearDisplay();           // Clear the buffer      
      Serial.println("next comes write text to buffer");       // Output text on serial Monitor
      display.println("Check In");   // Output text on Display
      Serial.println("next comes show Display buffer");       // Output text on serial Monitor
      display.display();                // Show display buffer - "Check In"
      // future code to send Timestamp, RFID UID and Button status to SQL DB
      break;                            // Exit the while loop
    }

and the output of the serial monitor is:

11:07:35.511 -> Firmware Version: 0x92 = v2.0
11:07:35.511 -> Scan RFID..
11:07:36.595 -> Printing HEX UID : 034A1E1B
11:07:39.359 -> next comes clearDisplay buffer
11:07:39.359 -> next comes write text to buffer
11:07:39.359 -> next comes show Display buffer

so i hope to be one step closer to a solution. Still don't know why display.display() does not work but freeze the hardware instead.

Still don't know why display.display() does not work but freeze the hardware instead.

You don't KNOW that. You only know that the next Serial.print() statement doesn't happen.

Put a Serial.print() statement after the display() call, to make certain that you understand where the program really hangs.

Disconnect the MFRC522 for now, and delete all related code. Maybe there is some pin conflict when both devices are connected/used.

PaulS:
...
Disconnect the MFRC522 for now, and delete all related code. Maybe there is some pin conflict when both devices are connected/used.

this would seem most likely, since my setup was only the MCU + OLED, i don't have any RFID sensors.

PaulS:
Put a Serial.print() statement after the display() call, to make certain that you understand where the program really hangs.

i did insert that code already... sorry, the code was not updated in the post:

    val = digitalRead(Button_Left);     // read pin(Button) input value
    if (val == LOW) {                   // check if the input is LOW (Button pressed)
      Serial.println("next comes clearDisplay buffer");       // Output text on serial Monitor
      display.clearDisplay();           // Clear the buffer      
      Serial.println("next comes write text to buffer");       // Output text on serial Monitor
      display.println("Check In");   // Output text on Display
      Serial.println("next comes show Display buffer");       // Output text on serial Monitor
      display.display();                // Show display buffer - "Check In"
      Serial.println("Display buffer was finished");       // Output text on serial Monitor
      // future code to send Timestamp, RFID UID and Button status to SQL DB
      break;                            // Exit the while loop
    }

so, the script really stops at the display.dosplay(); command.

Disconnect the MFRC522 for now, and delete all related code. Maybe there is some pin conflict when both devices are connected/used.

here the code WITHOUT all code concerning the RC522. it works like this. The OLED display shows its content the right way.

#include <SPI.h>                  // RC522 communication (four-wire serial bus)
#include <Wire.h>                 // OLED communication  (two-wire interface)
//#include <MFRC522.h>              // RC522 library - RFID Reader
//#include <Adafruit_GFX.h>         // OLED library for graphic output
#include <Adafruit_SSD1306.h>     // OLED library for text output

#define SCREEN_WIDTH 128          // OLED display width, in pixels
#define SCREEN_HEIGHT 32          // OLED display height, in pixels
#define OLED_RESET     -1         // OLED Reset pin # (or -1 if sharing Arduino reset pin)
#define RST_PIN         7         // RC522 RESET pin - not used
#define SS_PIN          11        // RC522 SDA pin

const byte  Button_Left = 5;              // BUTTON pin - left side
const byte  Button_Right = 3;             // BUTTON pin - right side
int val; // Temporaere Variable   // BUTTON variable to save status - HIGH or LOW

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // OLED on I2C

//MFRC522 mfrc522(SS_PIN, RST_PIN); // MFRC522 create instance

void setup() {
   // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  
  display.clearDisplay();             // Clear the initial buffer content - Adafruit Logo
  display.setTextSize(1);             // Draw X-scale text
  display.setTextColor(WHITE);        // Set text color to white
  display.setCursor(0,0);           // Set cursor position
  display.println(F("starting..."));  // Output text on Display
  display.display();                  // Show display buffer - "starting"
  delay(2000);                        // Pause for 2 seconds
  display.clearDisplay();             // Clear the buffer
  display.display();                  // Show display buffer - empty screen     

  
  pinMode (Button_Left, INPUT_PULLUP);     // Initialising sensor pin 
  pinMode (Button_Right, INPUT_PULLUP);  // Initialising sensor pin
  
	SPI.begin();   		                  // Init SPI bus
	//mfrc522.PCD_Init();		              // Init MFRC522
  Serial.begin(9600);                 // Initialize serial communications with the PC
  while (!Serial);                    // Do nothing until serial port is opened 
	//mfrc522.PCD_DumpVersionToSerial();	// Show details of PCD - MFRC522 Card Reader details
	Serial.println(F("Scan RFID.."));   // Output on serial monitor
}

void loop() {
  
	// if ( ! mfrc522.PICC_IsNewCardPresent()) { // Looo until a card is present
	// 	return;
	// }
	// if ( ! mfrc522.PICC_ReadCardSerial()) {   // Select and read one of the cards
	// 	return;
	// }
  display.setTextSize(1);             // Draw X-scale text
  display.setTextColor(WHITE);        // Set text color to white
  display.setCursor(0,0);           // Set cursor position
  Serial.print("Printing HEX UID : ");                        // Output on serial monitor
  // for (byte i = 0; i < mfrc522.uid.size; i++) {               // get RFID UID and print to serial monitor
  //   Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? "0" : "");
  //   Serial.print(mfrc522.uid.uidByte[i], HEX);
  // } 
  // Serial.println(""); // Output line break on serial monitor
  
  unsigned long starttime = millis();   // variable for loop time calculation
  unsigned long endtime = starttime;    // variable for loop time calculation
  while ((endtime - starttime) <=4000)  // loop for up to 4000ms to wait for user input (Buttons)
  {
    val = digitalRead(Button_Left);     // read pin(Button) input value
    if (val == LOW) {                   // check if the input is LOW (Button pressed)
      Serial.println("next comes clearDisplay buffer");       // Output text on serial Monitor
      display.clearDisplay();           // Clear the buffer      
      Serial.println("next comes write text to buffer");       // Output text on serial Monitor
      display.println("Check In");   // Output text on Display
      Serial.println("next comes show Display buffer");       // Output text on serial Monitor
      display.display();                // Show display buffer - "Check In"
      Serial.println("Display buffer was finished");       // Output text on serial Monitor
      // future code to send Timestamp, RFID UID and Button status to SQL DB
      break;                            // Exit the while loop
    } 
    val = digitalRead(Button_Right);    // read pin(Button) input value
    if (val == LOW) {                   // check if the input is LOW (Button pressed)
      Serial.println("Check Out");      // Output text on serial Monitor
      display.clearDisplay();           // Clear the buffer
      display.println(F("Check Out"));  // Output text on Display
      display.display();                // Show display buffer - "Check Out"
      // future code to send Timestamp, RFID UID and Button status to SQL DB
      break;                            // Exit the while loop
    } 
        
    endtime = millis();                 // update variable for loop time calculation
    
  }
  //delay(3000); // wait 3 seconds and restart the loop
}

i should try to find a different library for the RFID Reader. must be a conflict. Thanks everybody to help me until this point!

i should try to find a different library for the RFID Reader.

More likely, that library is fine, and you need to use different pins.

PaulS:
More likely, that library is fine, and you need to use different pins.

OK. The only pin i use in common is SDA (Pin 11 on MKR1000). The screenshot from Fritzing is in the first post.
I need SDA it for both, RC522 and OLED

So, can i declare an adittional/separate SDA Pin for the OLED display? how to do that?

So, can i declare an adittional/separate SDA Pin for the OLED display?

Why would you want to do that? The RC522 constructor makes it trivial to change pins, suggesting to me that it is bit-banging I2C, rather than using the hardware I2C. So, let it bit-bang I2C on some other pin.

helmutgiersch:
OK. The only pin i use in common is SDA (Pin 11 on MKR1000). The screenshot from Fritzing is in the first post.
I need SDA it for both, RC522 and OLED

So, can i declare an adittional/separate SDA Pin for the OLED display? how to do that?

you shouldn't have a common "SDA", if the OLED uses I2C, then SDA is pin A4, and SCL is A5.
it looks like your RC522 module is using the SPI, that will be different pins.

BabyGeezer:
you shouldn't have a common "SDA", if the OLED uses I2C, then SDA is pin A4, and SCL is A5.
it looks like your RC522 module is using the SPI, that will be different pins.

are you sure this will work with my MKR1000? Looks like you use another board with different pinout. This is mine:

i'm not familiar with that.

it looks like D11 & D12 are the I2C pins - "SCL" and "SDA"
you have to check what pins are used for SPI - those are usually MISO, MOSI, SCK (clock) and SS or CS (chip select) - which you should be able to determine yourself, so DON'T use D11 for it.

After some more digging:
You are right, i should use separate SDA pins for SPI and I2C. However, It is possible to use both, SPI and I2C, on the same pin, but it takes a lot more effort and code, to separate and handle the timing on a single SDA pin. Before a I2C call can be made, all open SPI connections must be closed and the other way around too.

situation:
for this project the MKR1000 is just the wrong board. according to the pinout it got only ONE set of pins that can be declared either as SPI OR I2C OR UART. so, no good for projects with SPI + I2C devices.

conclusion:
i will swich to another board, will give a try to D1mini. Its pinout looks good, separate pins for SDA / I2C ans enough Pins left for my 2 buttons. Also got WiFi onboard for my DB-connection. I will come back with the results.

Big Thanks @ all!

helmutgiersch:
...i should use separate SDA pins for SPI and I2C. ...

i don't know the further details for the MKR1000 board, but there really is no such thing as "SDA" for SPI bus.

this is correct;
i should use separate pins for SPI and I2C.