Millis and Flashing Bitmap Image on OLED Screen

Happy Friday everybody,

Pardon the long code but I have a small issue with the OLED display of an image inside the millis function.
What I would like the image (invoked, for example, in the line "display.drawBitmap(104, 30, AirOn, 16, 16, WHITE);") to do is to remain for the entire duration of the condition. Instead, it flashes once and disappears. Is there a way to make the bitmap image remain solidly in the same way that the lightbulb bitmap does?

I believe the problem begins from the line "if (PUMP_state == LOW)"

Code is below.

Thanks in advance

//LIBRARIES
#include <SPI.h>                //Call the SPI library
#include <Wire.h>               //Call the Wire library
#include <Adafruit_GFX.h>       //Call the Adafruit GFX library
#include <Adafruit_SSD1306.h>   //Call the Adafruit SSD1306 library
#include "DHT.h"                //Call the DHT library

//SCREEN AND SENSOR DEFINITIONS
Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire);  //Set dimensions of screen

//#define OLED_RESET 4

#define DHTTYPE DHT11           //Define the type of DHT Sensor
#define DHTPIN 2                //Define its pin

DHT dht(DHTPIN, DHTTYPE);

//PIN DEFINITIONS
int LDRPin = A0;
int PUMP = 3;

int PUMP_state = LOW;

//TIMER VALUES
const unsigned long Pump_ON = 500UL;
const unsigned long Pump_OFF = 2000UL;
unsigned long previousTime = 0;


const unsigned char myImage [] PROGMEM = {
  //paste in the HEX code generated from https://javl.github.io/image2cpp/ here
  //INTRO SCREEN



};

const unsigned char LightOn [] PROGMEM = {
  //paste in the HEX code generated from https://javl.github.io/image2cpp/ here
  0x07, 0xc0, 0x0f, 0x20, 0x1f, 0x90, 0x3f, 0xc8, 0x3f, 0xe8, 0x3f, 0xf8, 0x3f, 0xf8, 0x3f, 0xf8, 
  0x1f, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x00, 0x00, 0x07, 0xc0, 0x07, 0xc0

};


const unsigned char LightOff [] PROGMEM = {
  //paste in the HEX code generated from https://javl.github.io/image2cpp/ here
  0x07, 0xc0, 0x08, 0x20, 0x10, 0x10, 0x20, 0x08, 0x20, 0x08, 0x20, 0x08, 0x20, 0x08, 0x20, 0x08, 
  0x10, 0x10, 0x08, 0x20, 0x04, 0x40, 0x04, 0x40, 0x07, 0xc0, 0x00, 0x00, 0x07, 0xc0, 0x07, 0xc0

};

const unsigned char AirOn [] PROGMEM = {
  //paste in the HEX code generated from https://javl.github.io/image2cpp/ here
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x30, 0x00, 0x30, 0x1f, 0xf0, 0x36, 0x98, 0x6a, 0xac, 0x62, 0x9c, 0x2a, 0xa8, 0x1f, 0xf0

};

const unsigned char AirOff [] PROGMEM = {
  //paste in the HEX code generated from https://javl.github.io/image2cpp/ here
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x30, 0x00, 0x30, 0x1f, 0xf0, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x20, 0x08, 0x1f, 0xf0

};

void setup()   {    



  
    
  Serial.begin(230400); 

  pinMode (LDRPin, INPUT);

  dht.begin();

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x64)
  
  display.clearDisplay(); // Make sure the display is cleared
  // Draw the bitmap:
  // drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color)
  display.drawBitmap(0, 0, myImage, 64, 64, WHITE); //bitmap width and height are dimensions of image




  // Update the display
  display.display();
}


void loop() {

   unsigned long currentTime = millis();
   
   int LDR_Threshold = 40; //Set to your preference

    int LDR_val=analogRead(LDRPin);
    Serial.print("LDR value is: ");
    Serial.println(LDR_val);


 
  
    int temp  = dht.readTemperature();
    int humid = dht.readHumidity();

    display.clearDisplay();
    display.setTextColor(WHITE);

    //Serial.print("Temperature: ");
    //Serial.println(temp);
    display.setTextSize(1);
    display.setCursor(10,0);
    display.print("Temp.");
    display.setCursor(2,8);
    display.setTextSize(2);
    display.print(temp);
    display.print(char(167));
    display.print("C");

    display.setTextSize(1);    
    //Serial.print("Humidity: ");
    //Serial.println(humid);
    display.setCursor(56,0);
    display.print("Humidity");
    display.setCursor(60,8);
    display.setTextSize(2);
    display.print(humid);
    display.print("%");

//DRAW THE PUMP TIMER
    //drawRoundRect(x, y, width, height, colour)
    display.drawRoundRect(0, 38, 100, 8, 2,  WHITE);
    //fillRoundRect(x, y, width, height, radius, colour)
    display.fillRoundRect(0, 38 , 50, 8, 2, WHITE);
    display.setCursor (0, 28);
    display.setTextSize(1);
    display.print("Next stir");



//DRAW THE LIGHT METER    
    //drawRoundRect(x, y, width, height, colour)
    display.drawRoundRect(0, 56, 100, 8, 2,  WHITE);
    //fillRoundRect(x, y, width, height, radius, colour)
    display.fillRoundRect(0, 56,20 + LDR_val * 0.148, 8, 2, WHITE);
    display.setCursor (0, 48);
    display.setTextSize(1);
    display.print("Light Level");




if (LDR_val<LDR_Threshold) {
    display.drawBitmap(104, 48, LightOn, 16, 16, WHITE); //bitmap width and height are dimensions of image 
    display.display(); 

    } else {
    display.drawBitmap(104, 48, LightOff, 16, 16, WHITE); //bitmap width and height are dimensions of image
    display.display();  

    }  

  if (PUMP_state == LOW) {
    if((currentTime - previousTime) >= Pump_OFF) {
      PUMP_state = HIGH; //change state of pump to ON
    display.drawBitmap(104, 30, AirOn, 16, 16, WHITE); //bitmap width and height are dimensions of image
    display.display();
      previousTime = currentTime;
    }
  }
  else {
    if((currentTime - previousTime) >= Pump_ON) {
      PUMP_state = LOW; //change state of pump to ON
    display.drawBitmap(104, 30, AirOff, 16, 16, WHITE); //bitmap width and height are dimensions of image
    display.display();
      previousTime = currentTime;
  }
    

    
    display.display();
  }
}

Which one? The empty one?
1
The "air" one?
2
Also... your bar goes past 100%
3

Sure if write a logic that does so.

To narrow down the problem
add serial printing to your code

For analysing your problem serial printing will be very useful.
But only under the pre-condition that you use Arduino IDE Version 1.8.19.

The new IDE Version 2.X.Y has a fundamental problem with the serial monitor
In Arduino-IDE-Version 2.X.Y the serial monitor is crippled .

You can't make effective use of it.

This is a problem that has been discussed for more than a half year now, but the Arduino-IDE-development-team refuses to work on a solution.

This means your solution is to work with Arduino-IDE 1.8.19. There is a possability to have Arduino-IDE 1.8.19 on the same computer without interfering with IDE 2.X.Y.

This is done through making the Arduino-IDE Version 1.8.19 as a portable version. The portable version 1.8.19 does not interfere with a Arduino-IDE 2.X.Y-Installation.
Arduino-IDE Version 1.8.19 does have less bells and whistles than IDE 2.X.Y but compiles and uploads just the same. And serial printing works like a charm.

You can read in this tutorial how to install Arduino-IDE 1.8.19 as portable version
tutorial how to install Arduino-IDE Version 1.8.19 as portable

best regards Stefan

The upper of the two (the ‘next stir’ one)

All have "next stir"... one has "air" one is empty. They both appear and vanish.

still unclear. I really enjoy this game of short postings that use time for riddlings

Sorry for the delay.
The first two images encircled on xpfd’s post.

They both appear momentarily

and should do what instead?

Stay on the screen for the duration of each condition instead of appear momentarily at the start of each condition

I really enjoy these short postings. They left so many details over for asking back again and again.

What conditions?

You call this immediately after turning the pump on or off.

You should only clear the "air/no-air" image inside the "pump on (or off)" routine.

Thanks for the suggestion, xfpd.
The problem now becomes that if the ‘clearDisplay’ command is only in the conditions, when other things occur (like a change in temperature or humidy), it is written over the existing value displayed on the screen and doesn’t update until the millis timer value has elapsed

Instead of drawing the image inside each condition, set a flag (boolean variable) to indicate which image needs to be displayed, then in the main loop use that to draw the appropriate image to the screen each time it is refreshed.

Do not use multiple calls to display.display(), do that only once where you have it at the end of loop().

To eliminate flicker, only refreshed the display when something changes (either the image or the data being displayed). Do not clear the display, just overwrite whatever needs to be changed. For the data fields, this usually involves blanking out the previous data by either drawing a box in the background color, or overwriting the previous data by writing the old value of the data using the background color as the text color. For the images, drawing a box of the background color will erase the previous image.

1 Like

To do this, you could also use BLACK as the last parameter to draw the bitmaps...

Hello again,

Having the 'display.display();' command inside the conditional loop was causing other problems (the expanding progress bar meant that nothing else was updating on the screen until the condition came to an end) so, as per your suggestion, I have just one display.display command in the loop.
I then experimented with where to position the 'clearDisplay' command (this is why you will see several of these commands commented out along with a description of the issue they cause).

So, this is good as I can get it at the moment. The upper bar flickers continually but the light meter works again

//LIBRARIES
#include <SPI.h>                //Call the SPI library
#include <Wire.h>               //Call the Wire library
#include <Adafruit_GFX.h>       //Call the Adafruit GFX library
#include <Adafruit_SSD1306.h>   //Call the Adafruit SSD1306 library
#include "DHT.h"                //Call the DHT library

//SCREEN AND SENSOR DEFINITIONS
Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire);  //Set dimensions of screen

//#define OLED_RESET 4

#define DHTTYPE DHT11           //Define the type of DHT Sensor
#define DHTPIN 2                //Define its pin

DHT dht(DHTPIN, DHTTYPE);

//PIN DEFINITIONS
int LDRPin = A0;
int PUMP = 3;

int PUMP_state = LOW;

//TIMER VALUES
const unsigned long Pump_ON = 2000UL;
const unsigned long Pump_OFF = 8000UL;
unsigned long previousTime = 0;

const unsigned long LDR_CHECK = 100UL;
unsigned long LDRprevTime = 0;


const unsigned char myImage [] PROGMEM = {
  //paste in the HEX code generated from https://javl.github.io/image2cpp/ here
  //INTRO SCREEN
  0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0xf8, 0x3f, 0xff, 
  0xff, 0x80, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0x80, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0x80, 0xff, 0xe0, 
  0x07, 0xff, 0xf7, 0x80, 0xff, 0xe0, 0x07, 0xff, 0xff, 0x80, 0xff, 0xf0, 0x07, 0xff, 0xef, 0x80, 
  0xff, 0xf0, 0x07, 0xff, 0xe3, 0x80, 0xff, 0xf0, 0x0f, 0xff, 0xe3, 0x80, 0xff, 0xf0, 0x07, 0xff, 
  0xc3, 0x80, 0xff, 0xf0, 0x07, 0xff, 0xc3, 0x80, 0xff, 0xe0, 0x01, 0xff, 0x81, 0x80, 0xff, 0x80, 
  0x00, 0x7f, 0x81, 0x80, 0xff, 0x00, 0x00, 0x3f, 0x81, 0x80, 0xff, 0x00, 0x00, 0x3f, 0xc0, 0x80, 
  0xfe, 0x00, 0x00, 0x1f, 0xc0, 0x80, 0xfe, 0x00, 0x00, 0x0f, 0x81, 0x80, 0xfc, 0x00, 0x00, 0x0f, 
  0xe3, 0x80, 0xfc, 0x00, 0x00, 0x07, 0xe3, 0x80, 0xfc, 0x00, 0x00, 0x03, 0x87, 0x80, 0xfc, 0x00, 
  0x00, 0x03, 0x8f, 0x80, 0xf8, 0x20, 0x00, 0x00, 0x0f, 0x80, 0xf8, 0x70, 0x00, 0x40, 0x1f, 0x80, 
  0xf0, 0x70, 0x00, 0x60, 0x1f, 0x80, 0xf0, 0xf0, 0x00, 0x70, 0x3f, 0x80, 0xe0, 0xf8, 0x00, 0x78, 
  0x3f, 0x80, 0xc1, 0xf8, 0x00, 0x78, 0x7f, 0x80, 0xc1, 0xf8, 0x00, 0x7e, 0xff, 0x80, 0x80, 0x78, 
  0x00, 0x7f, 0xff, 0x80, 0x80, 0x00, 0x00, 0x7f, 0xff, 0x80, 0xc0, 0x00, 0x00, 0x7f, 0xff, 0x80, 
  0xe0, 0x00, 0x00, 0x7f, 0xff, 0x80, 0xe0, 0x00, 0x00, 0x7f, 0xff, 0x80, 0xe0, 0x00, 0x00, 0x7f, 
  0xff, 0x80, 0xe0, 0x00, 0x00, 0x3f, 0xff, 0x80, 0xe1, 0x80, 0x00, 0x3f, 0xff, 0x80, 0xe1, 0xc0, 
  0x00, 0x7f, 0xff, 0x80, 0xe1, 0xfc, 0x00, 0xff, 0xff, 0x80, 0xe1, 0xfc, 0x01, 0xff, 0xff, 0x80, 
  0xf1, 0xfc, 0x19, 0xff, 0xff, 0x80, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0x80, 0xff, 0xfc, 0x1f, 0xff, 
  0xff, 0x80, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0x80, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0x80, 0xff, 0xfc, 
  0x3f, 0xff, 0xff, 0x80, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0x80, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0x80, 
  0xff, 0xf8, 0x7f, 0xff, 0xff, 0x80, 0xff, 0xf8, 0x7f, 0xff, 0xff, 0x80, 0xff, 0xf8, 0x7f, 0xff, 
  0xff, 0x80, 0xff, 0xf8, 0xff, 0xff, 0xff, 0x80, 0xff, 0xf8, 0xff, 0xff, 0xff, 0x80, 0xff, 0xf0, 
  0xff, 0xff, 0xff, 0x80, 0xff, 0xf1, 0xff, 0xff, 0xff, 0x80, 0xff, 0xf1, 0xff, 0xff, 0xff, 0x80, 
  0xff, 0xf1, 0xff, 0xff, 0xff, 0x80, 0xff, 0xe3, 0xff, 0xff, 0xff, 0x80, 0xff, 0xc3, 0xff, 0xff, 
  0xff, 0x80, 0xff, 0x83, 0xff, 0xff, 0xff, 0x80, 0xff, 0x07, 0xff, 0xff, 0xff, 0x80, 0xff, 0x07, 
  0xff, 0xff, 0xff, 0x80, 0xfe, 0x0f, 0xff, 0xff, 0xff, 0x80, 0xff, 0x1f, 0xff, 0xff, 0xff, 0x80


};

const unsigned char LightOn [] PROGMEM = {
  //paste in the HEX code generated from https://javl.github.io/image2cpp/ here
  0x07, 0xc0, 0x0f, 0x20, 0x1f, 0x90, 0x3f, 0xc8, 0x3f, 0xe8, 0x3f, 0xf8, 0x3f, 0xf8, 0x3f, 0xf8, 
  0x1f, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x00, 0x00, 0x07, 0xc0, 0x07, 0xc0

};


const unsigned char LightOff [] PROGMEM = {
  //paste in the HEX code generated from https://javl.github.io/image2cpp/ here
  0x07, 0xc0, 0x08, 0x20, 0x10, 0x10, 0x20, 0x08, 0x20, 0x08, 0x20, 0x08, 0x20, 0x08, 0x20, 0x08, 
  0x10, 0x10, 0x08, 0x20, 0x04, 0x40, 0x04, 0x40, 0x07, 0xc0, 0x00, 0x00, 0x07, 0xc0, 0x07, 0xc0

};

const unsigned char AirOn [] PROGMEM = {
  //paste in the HEX code generated from https://javl.github.io/image2cpp/ here
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x30, 0x00, 0x30, 0x1f, 0xf0, 0x36, 0x98, 0x6a, 0xac, 0x62, 0x9c, 0x2a, 0xa8, 0x1f, 0xf0

};

const unsigned char AirOff [] PROGMEM = {
  //paste in the HEX code generated from https://javl.github.io/image2cpp/ here
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x30, 0x00, 0x30, 0x1f, 0xf0, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x20, 0x08, 0x1f, 0xf0

};

void setup()   {    



  
    
  Serial.begin(230400); 

  pinMode (LDRPin, INPUT);

  dht.begin();

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x64)
  
  display.clearDisplay(); // Make sure the display is cleared
  // Draw the bitmap:
  // drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color)
  display.drawBitmap(0, 0, myImage, 41, 64, WHITE); //bitmap width and height are dimensions of image
  display.setTextColor(WHITE);
  display.setCursor(50,0);
  display.setTextSize(1);
  display.print("Badalona");
  display.setCursor(50, 10);
  display.print("Makerspace");
  display.setCursor(50, 20);
  display.print(char(223));
  display.print("2024");




  // Update the display
  display.display();
  delay(2000);
  display.clearDisplay();
}


void loop() {

   display.clearDisplay(); //AWFUL BAR FLICKER BUT EVERYTHING ELSE WORKS

   unsigned long currentTime = millis();
   float onBar = ((currentTime - previousTime) * 100/Pump_OFF);
   float offBar = ((currentTime - previousTime) * 100/Pump_ON);
   
   int LDR_Threshold = 40; //Set to your preference

   int LDR_val=analogRead(LDRPin);
    //Serial.print("LDR value is: ");
    //Serial.println(LDR_val);

  
   int temp  = dht.readTemperature();
   int humid = dht.readHumidity();

   int val = currentTime - previousTime;

    

    //display.clearDisplay(); //AWFUL BAR FLICKER BUT EVERYTHING ELSE WORKS
    display.drawBitmap(104, 30, AirOn, 16, 16, WHITE);
    
    display.setTextColor(WHITE);

    //Serial.print("Temperature: ");
    //Serial.println(temp);
    display.setTextSize(1);
    display.setCursor(0,0);
    display.print("Temp.");
    display.print("(C)");

    display.setCursor(10,10);
    display.setTextSize(2);
    display.print(temp);
    
    

    display.setTextSize(1);    
    //Serial.print("Humidity: ");
    //Serial.println(humid);
    display.setCursor(54,0);
    display.print("Humid.(%)");
    display.setCursor(66,10);
    display.setTextSize(2);
    display.print(humid);

    //display.clearDisplay(); ONLY BOTTOM HALF OF SCREEN IS DRAWN. TOP BAR FLICKERS

//DRAW THE PUMP TIMER
    //drawRoundRect(x, y, width, height, colour)
    display.drawRoundRect(0, 38, 100, 8, 2,  WHITE);
    //fillRoundRect(x, y, width, height, radius, colour)
    //display.fillRoundRect(0, 38 , 50, 8, 2, WHITE);
    display.setCursor (0, 28);
    display.setTextSize(1);
    display.print("Next stir");

    //display.clearDisplay(); BAR FLICKERS + ONLY LOWER HALF OF SCREEN APPEARS

//DRAW THE LIGHT METER    
    //drawRoundRect(x, y, width, height, colour)
    display.drawRoundRect(0, 56, 100, 8, 2,  WHITE);
    //fillRoundRect(x, y, width, height, radius, colour)

    //display.fillRoundRect(0, 56,20 + LDR_val * 0.148, 8, 2, WHITE);
    display.setCursor (0, 48);
    display.setTextSize(1);
    display.print("Light Level");
    


    //display.clearDisplay(); BAR FLASHES + ONLY LOWER HALF OF SCREEN APPEARS

if (LDR_val<LDR_Threshold) {
    display.drawBitmap(104, 48, LightOn, 16, 16, WHITE); //bitmap width and height are dimensions of image 
    display.fillRoundRect(0, 56,20 + LDR_val * 0.148, 8, 2, WHITE);
    display.display(); 

    } else {
    display.drawBitmap(104, 48, LightOff, 16, 16, WHITE); //bitmap width and height are dimensions of image
    display.fillRoundRect(0, 56,20 + LDR_val * 0.148, 8, 2, WHITE);
    display.display();  

    }  


    //display.clearDisplay(); THE BAR IS SOLID BUT REST OF SCREEN FLASHES
  if (PUMP_state == LOW) {
    //Serial.println((currentTime - previousTime));
    Serial.print("ON: ");
    Serial.println((onBar));
    display.fillRoundRect(0 , 38 , onBar, 8, 2, WHITE);
    //display.display();
    if((currentTime - previousTime) >= Pump_OFF) {      
      PUMP_state = HIGH; //change state of pump to ON

      //drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color)
      display.drawBitmap(104, 30, AirOn, 16, 16, WHITE); //bitmap width and height are dimensions of image
      //display.fillRoundRect(0 , 38 , onBar, 8, 2, WHITE);
      //Serial.println((onBar));
      //display.display();
      previousTime = currentTime;
      
    }
  }
  else {
    //Serial.println((currentTime - previousTime));
    Serial.print("OFF: ");
    Serial.println((offBar));
    //fillRoundRect(x, y, width, height, radius, colour)
    display.fillRoundRect(0 , 38 , offBar, 8, 2, WHITE);
    //display.display();
    //display.fillRoundRect(0, 56,20 + LDR_val * 0.148, 8, 2, WHITE);
    if((currentTime - previousTime) >= Pump_ON) {
      PUMP_state = LOW; //change state of pump to OFF
      //display.clearDisplay();
      //drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color)
      display.drawBitmap(104, 30, AirOff, 16, 16, WHITE); //bitmap width and height are dimensions of image
       //display.fillRoundRect(0, 56,20 + LDR_val * 0.148, 8, 2, WHITE);
      //Serial.println((offBar));
      //display.display();
      previousTime = currentTime;
  }
  
      
    //display.fillRoundRect(0 , 38 , onBar, 8, 2, WHITE);
    //display.clearDisplay(); SCREEN DOESN'T UPDATE FOR ONE OF THE PUMP LOOPS
  }
      //display.clearDisplay(); FULL SCREEN FLICKER
      display.display();
}

Thanks in advance

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