I have an OLED display connected to a TCA9548A multiplexer and I'm trying to achieve the following:
Display a white line on the OLED in a blinking fashion by drawing a black and white line repeatedly with an interval of 200 milliseconds.
The codes works fine if I use a delay but I want to use millis as I have multiple displays connected to the multiplexer. I have the basic understanding of millis but just need some guidance incorporating this in my project. Here's the code which is a basic function:
void DisplayEngine(){
uint8_t TimeInterval = 200;
DisplayEngineCurrent = millis();
if (DisplayEngineCurrent - DisplayEngineStart >= TimeInterval){
tcaselect(0); //Select port 0 on the TCA9548A multiplexer
display.clearDisplay(); //Clear the display
display.drawLine(94, 32, 33, 32, 0); //Display a black line in the middle of the screen
display.display(); //Activate the display
DisplayEngineStart = DisplayEngineCurrent; //Delay 200 milliseconds
display.clearDisplay(); //Clear the display
display.drawLine(94, 32, 33, 32, 1); //Display a white line in the middle of the screen
display.display(); //Activate the display
DisplayEngineStart = DisplayEngineCurrent; //Delay 200 milliseconds
}
}
The DisplayEngineStart is an unsigned long variable, has a value of "0" and is defined at the top of the sketch.
you don't want to clear the screen every time otherwise everything else goes away and you don't really need to repaint the white line in black
something like this may be:
void blinkLineWhenNeeded(bool reset = false) {
const uint32_t halfPeriod = 200;
static bool lineIsVisible = false;
static uint32_t startTime = 0;
uint32_t currentTime = millis();
if (reset) {
startTime = currentTime - halfPeriod; // so that it gets triggered right away
display.clearDisplay(); //Clear the display
lineIsVisible = false;
}
if (currentTime - startTime >= halfPeriod) {
tcaselect(0); // Select port 0 on the TCA9548A multiplexer
if (lineIsVisible) display.drawLine(94, 32, 33, 32, 0); // Display a black line in the middle of the screen
else display.drawLine(94, 32, 33, 32, 1); // Display a white line in the middle of the screen
display.display(); // Transfer to the display
lineIsVisible = !lineIsVisible; // Inverse line state
startTime = currentTime;
}
}
you can call the function directly with blinkLineWhenNeeded(); but if you want to reset the state of the blinking you can call blinkLineWhenNeeded(true);. The latter will take into account the current time for the start of the blinking.
(are you sharing multiple physical displays but using only one display instance ?)
Omw, thank you so much! I implemented your suggestion in a test sketch where I put the code into loop() just for testing. This is what I came up with:
void loop() {
uint8_t TimeInterval = 200;
DisplayEngineCurrent = millis(); //Save current time in variable
if ((DisplayEngineCurrent - DisplayEngineStart >= TimeInterval) && (LineWhite == false)){ //Time between current time and start of code. If larger than 200ms, proceed
tcaselect(0); //Select port 0 on the TCA9548A multiplexer
display.clearDisplay(); //Clear the display
display.drawLine(94, 32, 33, 32, 1); //Display a white line in the middle of the screen
display.display(); //Clear the display
DisplayEngineStart = DisplayEngineCurrent; //Delay 200 milliseconds
LineWhite = true; //Change state of variable
}
if ((DisplayEngineCurrent - DisplayEngineStart >= TimeInterval) && (LineWhite == true)){ //Time between current time and start of code. If larger than 200ms, proceed
display.clearDisplay(); //Clear the display
display.drawLine(94, 32, 33, 32, 0); //Display a black line in the middle of the screen
display.display(); //Clear the display
DisplayEngineStart = DisplayEngineCurrent; //Delay 200 milliseconds
LineWhite = false; //Change state of variable
}
}
I can now successfully select how fast the line should blink by altering the TimeInterval variable.
A bonus question. I'm planning to display a lot more on this display than just this blinking line and looking at the code, this is going to require a lot of if statements. What would be the best practice for this? I'm planning to integrate at least 4 additional displays with similar functionality.
Have a great day!
Edit: The above is not entirely correct. I can somewhat control the speed of the blinking but any value over 800 in TimeInterval makes the line blink faster and not slower. It does what I need it to do but it's not working entirely like expected. Like if you wanted to blink at 1 second intervals.
in the code you posted above you can see that there is a lot of repeated code (like testing if it's time to update for example) and you missed the tcaselect() call.
You should factor things a bit together to avoid repeating code
void loop() {
const uint8_t TimeInterval = 200;
DisplayEngineCurrent = millis(); // Save current time in variable
if (DisplayEngineCurrent - DisplayEngineStart >= TimeInterval) { // Is it time to update ?
tcaselect(0); // Select port 0 on the TCA9548A multiplexer
display.clearDisplay(); // Clear the display
if (LineWhite) {
display.drawLine(94, 32, 33, 32, 0); // Display a black line in the middle of the screen
} else {
display.drawLine(94, 32, 33, 32, 1); // Display a white line in the middle of the screen
}
display.display();
DisplayEngineStart = DisplayEngineCurrent; // update the time of actoin
LineWhite = ! LineWhite; // Invert state of variable
}
}
Do you plan having multiple instances of the display or just share one and rely on tcaselect() to select the right destination ? (assuming you have a demultiplexer)
Thank very much for the code provided! Thanks works too
Here's the entire code with your changes:
#include <Wire.h> //Requried to run I2C SH1106
#include <SPI.h> //Requried to run I2C SH1106
#include <Adafruit_GFX.h> //https://github.com/adafruit/Adafruit-GFX-Library
#include <Adafruit_SH110X.h> //https://github.com/adafruit/Adafruit_SH110x
#define SCREEN_WIDTH 128 //OLED display width, in pixels
#define SCREEN_HEIGHT 64 //OLED display height, in pixels
#define OLED_RESET -1 //Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C //See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
#define TCAADDR 0x70 //Hardware address for the TCA9548A Multiplexer
unsigned long DisplayEngineStart = 0; //Type & value of variable
unsigned long DisplayEngineCurrent; //Type of variable
boolean LineWhite = false;
Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); //Type of OLED display used
void tcaselect(uint8_t channel) {
if (channel > 7) return;
Wire.beginTransmission(TCAADDR);
Wire.write(1 << channel);
Wire.endTransmission();
}
void setup() {
Wire.begin();
Serial.begin(9600);
tcaselect(0); //Select port 0 on the TCA9548A multiplexer
if(!display.begin(0x3C, true)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.clearDisplay();
display.display();
}
void loop() {
const uint8_t TimeInterval = 200;
DisplayEngineCurrent = millis(); // Save current time in variable
if (DisplayEngineCurrent - DisplayEngineStart >= TimeInterval) { // Is it time to update ?
tcaselect(0); // Select port 0 on the TCA9548A multiplexer
display.clearDisplay(); // Clear the display
if (LineWhite) {
display.drawLine(94, 32, 33, 32, 0); // Display a black line in the middle of the screen
} else {
display.drawLine(94, 32, 33, 32, 1); // Display a white line in the middle of the screen
}
display.display();
DisplayEngineStart = DisplayEngineCurrent; // update the time of actoin
LineWhite = ! LineWhite; // Invert state of variable
}
}
I'm using the TCA9548A Multiplexer and I can use the tcaselect() to select which display I want to update. Right now I only have one connected but I'm planning on 5 in total for this project. Plan is to show some very basic animations and then some text at the end like "Engine On", "Engine Off" etc.
you are using a TCA9548A Multiplexer to access the right display you might want to experiment keeping onl y one display and erasing/repainting a new screen after selecting a target display with your TCA9548A Multiplexer or having multiple display instances but this would have an impact on memory ➜ what's your arduino?
Yes, I just tested with one display for starters. I have three connected to my Arduino Leonardo and all three are working as intended at the moment. I need the USB functionality of the Leonardo, so I can send keyboard commands to my PC using the keyboard.h library.