Inefficient code writing to OLED displays

I'm working on code to control multiple OLED displays. My ultimate goal here is to have 8 separate displays that count days. Each display is wired to its own push button. When their respective push buttons get pressed, it resets the day count. If the day count exceeds a threshold set for each individual display, the text turns to red and blinks.

I got my code to work for one display. I attempted to expand to two displays and the displays now update painfully slow and blink erratically. I'm very new to arduino so I suspect I've coded this in a super inefficient way and was hoping y'all can point me in the right direction.

Here is my code to control just a single display - which worked quite well.

#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_GC9A01A.h"

Adafruit_GC9A01A tft( 10, 7);
//Adafruit_GC9A01A tft_2( 6, 7);
//Adafruit_GC9A01A tft_3( pin, 7 );
//Adafruit_GC9A01A tft_4( pin, 7 );
//Adafruit_GC9A01A tft_5( pin, 7 );
//Adafruit_GC9A01A tft_6( pin, 7 );
//Adafruit_GC9A01A tft_7( pin, 7 );
//Adafruit_GC9A01A tft_8( pin, 7 );

int day_1 = 1;

int threshold_1 = 5;

unsigned long previousMillis = 0;
const long interval = 5000;
const long interval_blink_on = 1000;
const long interval_blink_off = 300;
unsigned long previousMillisBlink = 0;

bool on = true;

const int buttonPin_1 = 4;

int buttonState_1 = 0;


void setup() {
  Serial.begin(9600);
  Serial.println("GC9A01A Test!");

  tft.begin();
  tft.setRotation(3);
  tft.setTextColor(GC9A01A_WHITE);
  tft.fillScreen(GC9A01A_BLACK);
  tft.setCursor(70, 60);
  tft.setTextSize(20);
  tft.println(day_1);
}

void loop() {

  unsigned long currentMillis = millis();       //this will be used to keep track of days
  unsigned long currentMillisBlink = millis();  //this will be used to keep track of blink intervals

  buttonState_1 = digitalRead(buttonPin_1);

  if (buttonState_1 == HIGH) {  //if button is pressed...
    day_1 = 0;                  //reset day to 0
    tft.setTextColor(GC9A01A_WHITE); //set tect color to white
    tft.fillScreen(GC9A01A_BLACK);  //wipe screen
    tft.setCursor(70, 60);         
    tft.setTextSize(20);
    tft.println(day_1);  //print day count
  }

  if (currentMillis - previousMillis >= interval) {  //check if interval has passed
    previousMillis = currentMillis;                  //reset timer/interval
    day_1++;                                         //add 1 to day counter
    if (day_1 > 9) {                                 //if day is 2 digit number, reduce text size
      tft.fillScreen(GC9A01A_BLACK);
      tft.setCursor(35, 70);
      tft.setTextSize(15);
      tft.println(day_1);
    } else {
      tft.setCursor(70, 60);
      tft.setTextSize(20);
      tft.println(day_1);                            //print new day count
    }
  }

    if (day_1 >= threshold_1) {                                   //if the day count is over specified threshold...
      tft.setTextColor(GC9A01A_RED, GC9A01A_BLACK);               //set text color to red
      if ((on == true) && (currentMillisBlink - previousMillisBlink >= interval_blink_on)) {  //If the text is showing and the blink interval is up
        tft.setTextColor(GC9A01A_BLACK);                                                      //set text to black to erase the text later
        previousMillisBlink = currentMillisBlink;                                            //reset the blink interval
        on = !on;                                                                           //show that text is off
        if (day_1 > 9) {
          tft.setCursor(35, 70);
          tft.setTextSize(15);
          tft.println(day_1);
        } else {
          tft.setCursor(70, 60);
          tft.setTextSize(20);
          tft.println(day_1);                                                              //erase the text
        }
      }
      if ((on == false) && (currentMillisBlink - previousMillisBlink >= interval_blink_off)) { //if the screen is blank and the blink interval is up
        tft.setTextColor(GC9A01A_RED);                                                         //set text color to red
        previousMillisBlink = currentMillisBlink;                                              //reset blink interval
        on = !on;                                                                           //show that text is now on
        if (day_1 > 9) {
          tft.setCursor(35, 70);
          tft.setTextSize(15);
          tft.println(day_1);
        } else {
          tft.setCursor(70, 60);
          tft.setTextSize(20);
          tft.println(day_1);                                                             //display the text the correct size according to # of digits
        }
      }
    }
     else {
      tft.setTextColor(GC9A01A_WHITE, GC9A01A_BLACK);                                   //set text to white if the threshold is not met
    }
}

And here is my code when I tried to bring in a second display.

#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_GC9A01A.h"

Adafruit_GC9A01A tft( 10, 7);
Adafruit_GC9A01A tft_2( 6, 7);
//Adafruit_GC9A01A tft_3( pin, 7 );
//Adafruit_GC9A01A tft_4( pin, 7 );
//Adafruit_GC9A01A tft_5( pin, 7 );
//Adafruit_GC9A01A tft_6( pin, 7 );
//Adafruit_GC9A01A tft_7( pin, 7 );
//Adafruit_GC9A01A tft_8( pin, 7 );

int day_1 = 1;
int day_2 = 1;
//int day_3 = 9;
//int day_4 = 9;
//int day_5 = 9;
//int day_6 = 9;
//int day_7 = 9;
//int day_8 = 9;

int threshold_1 = 5;
int threshold_2 = 2;
//int threshold_3 = 3;
//int threshold_4 = 4;
//int threshold_5 = 5;
//int threshold_6 = 6;
//int threshold_7 = 7;
//int threshold_8 = 8;

unsigned long previousMillis = 0;
const long interval = 5000;
const long interval_blink_on = 1000;
const long interval_blink_off = 400;
unsigned long previousMillisBlink = 0;

bool on = true;

const int buttonPin_1 = 4;
const int buttonPin_2 = 3;

int buttonState_1 = 0;
int buttonState_2 = 0;


void setup() {
  Serial.begin(9600);
  Serial.println("GC9A01A Test!");

  Serial.println(millis());

  tft.begin();
  tft.setRotation(3);
  tft.setTextColor(GC9A01A_WHITE);
  tft.fillScreen(GC9A01A_BLACK);
  tft.setCursor(70, 60);
  tft.setTextSize(20);
  tft.println(day_1);

  tft_2.begin();
  tft_2.setRotation(3);
  tft_2.setTextColor(GC9A01A_WHITE);
  tft_2.fillScreen(GC9A01A_BLACK);
  tft_2.setCursor(70, 60);
  tft_2.setTextSize(20);
  tft_2.println(day_2);
}

void loop() {

  unsigned long currentMillis = millis();       //this will be used to keep track of days
  unsigned long currentMillisBlink = millis();  //this will be used to keep track of blink intervals

  buttonState_1 = digitalRead(buttonPin_1);
  buttonState_2 = digitalRead(buttonPin_2);
  //buttonState_3 = digitalRead(buttonPin_3);
  //buttonState_4 = digitalRead(buttonPin_4);
  //buttonState_5 = digitalRead(buttonPin_5);
  //buttonState_6 = digitalRead(buttonPin_6);
  //buttonState_7 = digitalRead(buttonPin_7);
  //buttonState_8 = digitalRead(buttonPin_8);

  if (buttonState_1 == HIGH) {  //if button is pressed...
    day_1 = 0;                  //reset day to 0
    tft.setTextColor(GC9A01A_WHITE);
    tft.fillScreen(GC9A01A_BLACK);
    tft.setCursor(70, 60);
    tft.setTextSize(20);
    tft.println(day_1);  //print day
  }

  if (currentMillis - previousMillis >= interval) {  //if 1 day has passed...
    previousMillis = currentMillis;
    day_1++;  //add 1 to day counter
    if (day_1 > 9) {
      tft.fillScreen(GC9A01A_BLACK);
      tft.setCursor(35, 70);
      tft.setTextSize(15);
      tft.println(day_1);
    } else {
      tft.setCursor(70, 60);
      tft.setTextSize(20);
      tft.println(day_1);  //print new day count
    }

    day_2++;  //add 1 to day counter
    if (day_2 > 9) {
      tft_2.fillScreen(GC9A01A_BLACK);
      tft_2.setCursor(35, 70);
      tft_2.setTextSize(15);
      tft_2.println(day_2);
    } else {
      tft_2.setCursor(70, 60);
      tft_2.setTextSize(20);
      tft_2.println(day_2);
    }

    if (day_1 >= threshold_1) {  //if the day count is over specified threshold...
      tft.setTextColor(GC9A01A_RED, GC9A01A_BLACK);
      if ((on == true) && (currentMillisBlink - previousMillisBlink >= interval_blink_on)) {
        tft.setTextColor(GC9A01A_BLACK);
        previousMillisBlink = currentMillisBlink;
        on = !on;
        if (day_1 > 9) {
          tft.setCursor(35, 70);
          tft.setTextSize(15);
          tft.println(day_1);
        } else {
          tft.setCursor(70, 60);
          tft.setTextSize(20);
          tft.println(day_1);
        }
      }
      if ((on == false) && (currentMillisBlink - previousMillisBlink >= interval_blink_off)) {
        tft.setTextColor(GC9A01A_RED);
        previousMillisBlink = currentMillisBlink;
        on = !on;  //change text to red and blink
        if (day_1 > 9) {
          tft.setCursor(35, 70);
          tft.setTextSize(15);
          tft.println(day_1);
        } else {
          tft.setCursor(70, 60);
          tft.setTextSize(20);
          tft.println(day_1);
        }
      }
    }
     else {
      tft.setTextColor(GC9A01A_WHITE, GC9A01A_BLACK);
    }

    if (buttonState_2 == HIGH) {  //if button is pressed...
      day_2 = 0;                  //reset day to 0
      tft_2.setTextColor(GC9A01A_WHITE);
      tft_2.fillScreen(GC9A01A_BLACK);
      tft_2.setCursor(70, 60);
      tft_2.setTextSize(20);
      tft_2.println(day_2);  //print day
    }

    if (day_2 >= threshold_2) {  //if the day count is over specified threshold...
      tft_2.setTextColor(GC9A01A_RED, GC9A01A_BLACK);
      if ((on == true) && (currentMillisBlink - previousMillisBlink >= interval_blink_on)) {
        tft_2.setTextColor(GC9A01A_BLACK);
        previousMillisBlink = currentMillisBlink;
        on = !on;
        if (day_2 > 9) {
          tft_2.setCursor(35, 70);
          tft_2.setTextSize(15);
          tft_2.println(day_2);
        } else {
          tft_2.setCursor(70, 60);
          tft_2.setTextSize(20);
          tft_2.println(day_2);
        }
      }
      if ((on == false) && (currentMillisBlink - previousMillisBlink >= interval_blink_off)) {
        tft_2.setTextColor(GC9A01A_RED);
        previousMillisBlink = currentMillisBlink;
        on = !on;  //change text to red and blink
        if (day_2 > 9) {
          tft_2.setCursor(35, 70);
          tft_2.setTextSize(15);
          tft_2.println(day_2);
        } else {
          tft_2.setCursor(70, 60);
          tft_2.setTextSize(20);
          tft_2.println(day_1);
        }
      }
    else {
    tft_2.setTextColor(GC9A01A_WHITE, GC9A01A_BLACK);
    }
    }
  }
}

Will you get the same result with both displays connected and with the sketch for only one display? Or if you run the second sketch aimed for two displays, with either 1st or 2nd display connected?

When I run the code for both displays, each display works and shows different values, both buttons work independently etc.

It doesn't seem like the controller can keep up with the display updates though. This is especially noticeable with the blinks. The screens blink erratically, sometimes not at all for a few cycles.

One of the pushbuttons also takes a few seconds to register, I'm assuming because the code is stuck somewhere

Would you care to make a schematic over the hardware? Even one drawn by hand works.

Kudos btw for being new here and attach the code as code. :ok_hand:

Since I don't see it mentioned, what Arduino are you using? And, what are the compiler output results - memory usage - at the end of a successful compile for that Arduino?

Check connections.

DroneBotWorkShop used multiple OLED with the same address without issue.

Im using an Uno R3 but am probably going to go to an Esp32.

41% program storage space and dynamic 27% memory

Connections are solid, thanks for the link I'll give this a watch

That is because the test for (buttonState_2 == HIGH) (line 154) is inside the if statement on line 93 checking if "interval" amount of time has passed. That results in the button 2 state only being checked every 5000mS.

I don't have time to look closely at the code, but it also appears the code sections for blinking the red colored text is also inside this same if statement.

2 Likes

What software do people normally use to draw circuits? I can do something up

I have oled 1 wired directly to the uno board and the oled 2 is wired in parallel except for its chip select which gets its own pin on the uno.

I could kiss you homie. I have been very haphazard with my brackets and I see their importance now

1 Like

You got Fritzing and Cirkit, then the Cad programs, but that's a bit over the top to just make a schematic for this purpose.

Anyway, you were on track with the problem and @david_2018 solved it, so everything's fine.

Your code for 2 displays is already 2x longer than it needs to be. With 8 displays, it's going to be unmanageable. You need to learn to use arrays!

1 Like

look this over

  • buttons configured with internal pull-up and wired between pin and ground

  • see The C Programming Language, og 114 for an explanation of struct

  • did not implement blinking

#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_GC9A01A.h"

struct Disp {
    const byte       PinBut;
    Adafruit_GC9A01A tft;
    int              thresh;

    unsigned long    secs;
    byte             butState;
}
disp [] = {
    { 4, Adafruit_GC9A01A ( 10, 7), 1 },
    { 3, Adafruit_GC9A01A (  6, 7), 2 },
};
const int Ndisp = sizeof (disp)/sizeof (Disp);

// -----------------------------------------------------------------------------
const unsigned long OneSec = 1000;
      unsigned long msec0;

char s [90];

void loop ()
{
    // monitor buttons
    for (int n = 0; n < Ndisp; n++)  {
        byte but = digitalRead (disp [n].PinBut);
        if (disp [n].butState != but)  {
            disp [n].butState  = but;
            delay (20);                     // debounce

            if (LOW == but)
                disp [n].secs = 0;
        }
    }

    // -------------------------------------
    // update sec
    unsigned long msec = millis ();

    if (msec - msec0 < OneSec)
        return;

    msec0 += OneSec;

    // -------------------------------------
    // update displays
    for (int n = 0; n < Ndisp; n++)  {
        disp [n].secs++;
        disp [n].secs+= 5000;                   // for testing

        int sec    =  disp [n].secs % 60;
        int min    = (disp [n].secs /   60) % 60;
        int hour   = (disp [n].secs / 3600) % 24;
        int day    =  disp [n].secs / (24 * 3600L);

        sprintf (s, " %2d %3d %2d:%02d:%02d", n, day, hour, min, sec);
        Serial.println (s);

        if (disp [n].thresh < day)
            disp [n].tft.setTextColor (GC9A01A_RED, GC9A01A_BLACK);
        else
            disp [n].tft.setTextColor (GC9A01A_WHITE, GC9A01A_BLACK);
        disp [n].tft.fillScreen (GC9A01A_BLACK);
        disp [n].tft.setCursor  (10, 60);
        disp [n].tft.println    (s);
    }
}

// -----------------------------------------------------------------------------
void setup () {
    Serial.begin (9600);
    Serial.println ("GC9A01A Test!");

    for (int n = 0; n < Ndisp; n++)  {
        pinMode (disp [n].PinBut, INPUT_PULLUP);
        disp [n].butState = digitalRead (disp [n].PinBut);

        disp [n].tft.begin        ();
        disp [n].tft.setRotation  (3);
        disp [n].tft.fillScreen   (GC9A01A_BLACK);
        disp [n].tft.setTextSize  (20);
    }
}

I thought arrays might be my answer but I haven't messed around with them yet. I'll look into it, thanks for you help!

here's an approach for handling blink
it clears the display on 1/2 second intervales when day > thresh

#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_GC9A01A.h"

struct Disp {
    const byte       PinBut;
    Adafruit_GC9A01A tft;
    int              thresh;

    bool             blink;
    unsigned long    secs;
    byte             butState;
}
disp [] = {
    { 4, Adafruit_GC9A01A ( 10, 7), 1 },
    { 3, Adafruit_GC9A01A (  6, 7), 2 },
};
const int Ndisp = sizeof (disp)/sizeof (Disp);

// -----------------------------------------------------------------------------
const unsigned long OneSec = 1000;
      unsigned long msec0;

char s [90];

void loop ()
{
    // monitor buttons
    for (int n = 0; n < Ndisp; n++)  {
        byte but = digitalRead (disp [n].PinBut);
        if (disp [n].butState != but)  {
            disp [n].butState  = but;
            delay (20);                     // debounce

            if (LOW == but)
                disp [n].secs = 0;
        }
    }

    // -------------------------------------
    unsigned long msec = millis ();

    // blink - clear every half sec
    if (msec - msec0 >= OneSec/2)  {
        for (int n = 0; n < Ndisp; n++)
            if (disp [n].blink)
                disp [n].tft.fillScreen (GC9A01A_BLACK);
    }

    // update sec
    if (msec - msec0 < OneSec)
        return;

    msec0 += OneSec;

    // -------------------------------------
    // update displays
    for (int n = 0; n < Ndisp; n++)  {
        disp [n].secs++;
        disp [n].secs+= 5000;                   // for testing

        int sec    =  disp [n].secs % 60;
        int min    = (disp [n].secs /   60) % 60;
        int hour   = (disp [n].secs / 3600) % 24;
        int day    =  disp [n].secs / (24 * 3600L);

        sprintf (s, " %2d %3d %2d:%02d:%02d", n, day, hour, min, sec);
        Serial.println (s);

        if (disp [n].thresh < day)  {
            disp [n].tft.setTextColor (GC9A01A_RED, GC9A01A_BLACK);
            disp [n].blink = true;
        }
        else  {
            disp [n].tft.setTextColor (GC9A01A_WHITE, GC9A01A_BLACK);
            disp [n].blink = false;
        }
        disp [n].tft.fillScreen (GC9A01A_BLACK);
        disp [n].tft.setCursor  (10, 60);
        disp [n].tft.println    (s);
    }
}

// -----------------------------------------------------------------------------
void setup () {
    Serial.begin (9600);
    Serial.println ("GC9A01A Test!");

    for (int n = 0; n < Ndisp; n++)  {
        pinMode (disp [n].PinBut, INPUT_PULLUP);
        disp [n].butState = digitalRead (disp [n].PinBut);

        disp [n].tft.begin        ();
        disp [n].tft.setRotation  (3);
        disp [n].tft.fillScreen   (GC9A01A_BLACK);
        disp [n].tft.setTextSize  (20);
    }
}

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