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)
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.
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:
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.
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.
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!
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:
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.