Oled, scrolling text crashes screen on reset sometimes

I need to display some scrolling text on a 128 x 64 pixel oled, connected to an arduino Due via I2C,

The arduino may (or may not) then be reset via a button connected to the reset pin, so it can run through the if else statements again in setup and call different bits of code depending on the number set on some BCD switches.

but i've found that sometimes when resetting the arduino, the oled display will 'crash'
the arduino is still running, leds working etc, but when pressing reset it takes a lot longer than normal for the led's to change colour (indicating a correct or incorrect number code has been set on the BCD switches)

Only unplugging and replugging the usb cable gets the oled display going again.

i've isolated the code to just the stuff that does the scrolling on the oled display, and found out that it will do the same thing if using the adafruit or U8g2 libraries to run the oled, it's slightly different code for the U8G2 version... but in both resetting can lock the display up when scrolling.

it's random when the display will crash, sometimes i can press the reset button 40+ times in a row and it keeps running fine, other times i press it once and crash it.

i can even get it to do this behavior in an arduino simulator, where only restarting the simulation gets the oled going again.

Any ideas what's causing the display to do this, and how i can go about fixing it?

#include <Wire.h>//I2C stuff
#include <Adafruit_SSD1306.h>//oled oled stuff
#include <Adafruit_GFX.h>//Draw stuff on oled


Adafruit_SSD1306 oled(128, 64, &Wire, -1);//Declare oled type, size, connection and that it has no reset pin.

char message[] = "Oled roulette: Pressing the reset button may or may not crash the display";//Text to scroll
int x, minX;//Store position and length of text

void setup() {

  oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);//Start oled on I2C

  x = oled.width();//Sets to 128 pixels width
  minX = -12 * strlen(message);//12 = 6 pixels/character * text size 2

}

void loop() {

  oled.clearDisplay();//Clear buffer and screen
  oled.setTextColor(WHITE);//Use white pixels
  oled.setTextSize(3);//Set 3 times normal text size
  oled.setTextWrap(false);//Turn text wrapping off
  oled.setCursor(x, 16);//Position cursor
  oled.print(message);//Scrolling message
  oled.display();//Send the above to the display
  x = x - 3;//scroll speed, make more positive to slow down the scroll
  if (x < minX) x = oled.width();//If text length less than 128 pixels, use screen size as length to base scrolling maths on

}

it's slightly different code for the U8G2 version

What is the difference?

Can you explain your reasoning for allowing negative X pixel coordinates to position the text? I don't see any justification in the GFX library documentation for doing so.

minX = -12 * strlen(message);//12 = 6 pixels/character * text size 2

Put in some Serial.print statements to see what values the variables of interest have, when the error occurs.

Note: take a look at the text scroll features built into the SSD1306 library.

I took a look at the character drawing routine. The initial clipping does not properly check for negative X or Y coordinates. In the case of X, it checks only if the rightmost edge of the character is negative.

void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c,
                            uint16_t color, uint16_t bg, uint8_t size_x,
                            uint8_t size_y) {

  if (!gfxFont) { // 'Classic' built-in font

    if ((x >= _width) ||              // Clip right
        (y >= _height) ||             // Clip bottom
        ((x + 6 * size_x - 1) < 0) || // Clip left
        ((y + 8 * size_y - 1) < 0))   // Clip top
      return;

That may not be instantly fatal, because the drawPixel routine does check for negative X. However, after that check, it performs the screen rotation, with no further checks on bounds.

Since the bitmap is in memory, it is easy to imagine an out of bounds write, which causes a crash.

void Adafruit_SSD1306::drawPixel(int16_t x, int16_t y, uint16_t color) {
  if ((x >= 0) && (x < width()) && (y >= 0) && (y < height())) {
    // Pixel is in-bounds. Rotate coordinates if needed.
    switch (getRotation()) {
    case 1:
      ssd1306_swap(x, y);
      x = WIDTH - x - 1;
      break;
    case 2:
      x = WIDTH - x - 1;
      y = HEIGHT - y - 1;
      break;
    case 3:
      ssd1306_swap(x, y);
      y = HEIGHT - y - 1;
      break;
    }
    switch (color) {
    case SSD1306_WHITE:
      buffer[x + (y / 8) * WIDTH] |= (1 << (y & 7));
      break;
    case SSD1306_BLACK:
      buffer[x + (y / 8) * WIDTH] &= ~(1 << (y & 7));
      break;
    case SSD1306_INVERSE:
      buffer[x + (y / 8) * WIDTH] ^= (1 << (y & 7));
      break;
    }
  }

What is the difference?

The U8G2 version is :

#include <U8g2lib.h>
#include <Wire.h>

U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

u8g2_uint_t offset;     // current offset for the scrolling text
u8g2_uint_t width;      // pixel width of the scrolling text (must be lesser than 128 unless U8G2_16BIT is defined <Done>
const char *text = "Oled roulette: Pressing the reset button may or may not crash the display  "; // scroll this text from right to left


void setup(void) {

  u8g2.begin();  
  
  u8g2.setFont(u8g2_font_profont22_mf); // set the target font to calculate the pixel width
  width = u8g2.getUTF8Width(text);    // calculate the pixel width of the text
  
  u8g2.setFontMode(0);    // enable transparent mode, which is faster
}


void loop(void) {
  u8g2_uint_t x;
  
  u8g2.firstPage();
  do {
  
    // draw the scrolling text at current offset
    x = offset;
    u8g2.setFont(u8g2_font_profont22_mf);   // set the target font
    do {                // repeated drawing of the scrolling text...
      u8g2.drawUTF8(x, 30, text);     // draw the scolling text
      x += width;           // add the pixel width of the scrolling text
    } while( x < u8g2.getDisplayWidth() );    // draw again until the complete display is filled
    
    
  } while ( u8g2.nextPage() );
  
  offset-=1;              // scroll by one pixel
  if ( (u8g2_uint_t)offset < (u8g2_uint_t)-width )  
    offset = 0;             // start over again
    
  delay(10);              // do some small delay
}

I have uncommented the ''U8G2_16BIT'' line in U8G2.h to get longer than 128 pixels of text.
but it will still crash the oled if i press the reset 'at the wrong time'

Can you explain your reasoning for allowing negative X pixel coordinates to position the text? I don't see any justification in the GFX library documentation for doing so.

I'm a bit useless at coding, i just found that bit of code, and changed the text part to suit my needs,
i 'think' the negative thing makes it start the scrolling from the right hand side of the screen?

the U8G2 version starts scrolling from the left side, i.e. part of the text suddenly appears on the display, then starts moving left to reveal more (unless i pad the text with spaces),

the 'adafruit SSD1306' version starts with a blank display, then the text scrolls in from the right hand edge... a much nicer effect.

Note: take a look at the text scroll features built into the SSD1306 library.

i'm probably wrong, but i was under the impression that the scroll features in the library were hardware scrolling, which can only move text that fits on the screen,

but again i could well be wrong, i really struggle with this coding stuff, mostly getting away with copying bits and pieces of examples to get what i need to work.

Try it out.

Until you have more details about possible causes of the crash, like the value of X when it happens, it will be hard to help. I would not put much trust in the screen clipping code.

gotya,

so i need to figure out how to write to serial what is being sent to the oled, then see if the commands keep going when the screen has crashed, or stop... and if they stop that will be the bit of code that crashed the screen (hopefully)

The code you posted does not work at all on the Adafruit 128x64 SSD1306 OLED, because it has several errors.

The corrected code does work on an Arduino Uno, and I did not observe it to fail, so the clipping is working properly.

#include <Wire.h>//I2C stuff
#include <Adafruit_SSD1306.h>//oled oled stuff
#include <Adafruit_GFX.h>//Draw stuff on oled


Adafruit_SSD1306 oled(128, 64, &Wire, -1);//Declare oled type, size, connection and that it has no reset pin.

char message[] = "Oled roulette: Pressing the reset button may or may not crash the display";//Text to scroll
int x, minX;//Store position and length of text

void setup() {
  Serial.begin(115200);
  while (!Serial);

  oled.begin(SSD1306_SWITCHCAPVCC, 0x3D);//Start oled on I2C

  x = oled.width();//Sets to 128 pixels width
  minX = -18 * strlen(message);//18 = 6 pixels/character * text size 3

}

void loop() {

  oled.clearDisplay();//Clear buffer and screen
  oled.setTextColor(SSD1306_WHITE);//Use white pixels
  oled.setTextSize(3);//Set 3 times normal text size
  oled.setTextWrap(false);//Turn text wrapping off
  oled.setCursor(x, 16);//Position cursor
  oled.print(message);//Scrolling message
  oled.display();//Send the above to the display
  x = x - 3;//scroll speed, make more positive to slow down the scroll
  Serial.println(x);
  if (x < minX) x = oled.width();//If text length less than 128 pixels, use screen size as length to base scrolling maths on
}

Sorry about that, the I2C address OX3C seems to be needed for the arduino Due i'm using,
it does need to be 0X3D in the simulation, where i'm 'using' a virtual uno:

Wokwi simulation oled crashing

Not sure if the above link will work or not, things act a little different on the simulation.. could be because it's using an uno as it doesn't simulate Due boards,

But when i get it to crash the oled in the simulation, the screen fills with white bars, BUT the serial output runs faster than when it was updating the oled.

On the real Due, the screen just freezes, and the serial update time slows massively.

I ran your code on my Due (changing just the I2C address) and first time i pressed the reset button after it had uploaded, the oled crashed,
unplugged and re-plugged the usb cable to reset it, and had the serial monitor open this time... it took about 30 presses of the reset button before it crashed the oled this time (i must be doing a reset right at the wrong time, during a screen update or something?)

Below is a snippet of the serial monitor of the Due board... i added the text where i got successful resets, and where i crashed the oled,
did this a few more times, and it's totally random when i can crash the oled, i.e. it can be anywhere in the length of the text scroll.

16:11:38.726 -> 125 <<-- Successful reset
16:11:38.773 -> 122
16:11:38.773 -> 119
16:11:38.820 -> 116
16:11:38.867 -> 113
16:11:38.867 -> 110
16:11:38.914 -> 107
16:11:38.914 -> 104
16:11:38.961 -> 101
16:11:38.961 -> 98
16:11:39.007 -> 95
16:11:39.053 -> 92
16:11:39.053 -> 89
16:11:39.100 -> 86
16:11:39.567 -> 125 <<-- Successful Reset
16:11:39.567 -> 122
16:11:39.613 -> 119
16:11:39.613 -> 116
16:11:39.661 -> 113
16:11:39.707 -> 110
16:11:39.707 -> 107
16:11:39.753 -> 104
16:11:39.753 -> 101
16:11:39.800 -> 98
16:11:39.800 -> 95
16:11:39.847 -> 92
16:11:39.894 -> 89
16:11:40.363 -> 125 <<-- Successful reset
16:11:40.363 -> 122
16:11:40.410 -> 119
16:11:40.410 -> 116
16:11:40.455 -> 113
16:11:40.502 -> 110
16:11:40.502 -> 107
16:11:40.549 -> 104
16:11:40.549 -> 101
16:11:40.595 -> 98
16:11:40.595 -> 95
16:11:40.642 -> 92<<-- Crashed Oled on this reset
16:11:43.503 -> 125
16:11:45.379 -> 122
16:11:47.200 -> 119
16:11:49.063 -> 116
16:11:50.887 -> 113
16:11:52.758 -> 110
16:11:54.583 -> 107
16:11:56.416 -> 104
16:11:58.276 -> 101
16:12:00.143 -> 98

Sounds like a Due problem.

aaannnddd... that's it... it seems to be a Due specific problem (and the wokwi uno simulator)

I just dug an uno out, loaded the sketch onto it, transferred the oled over, and it resets differently to a Due,

almost as if the reset on an Uno is a hard reset... takes a second or 2 to perform the reset.
where as on the Due it's almost like it's doing a soft reset, takes milliseconds to reset,

i can see this on the oleds, on the Uno the oled pauses the scrolling, waits a second, then clears the display and the text comes up again from the beginning,

on the Due the reset pause is very brief, and the text will sometimes jump a bit, like the top half of the text moves over a few pixels, then the display clears and the scrolling is going from the start again... when it's not crashing the oled that is.

So it seems the problem is related to how the Due does its reset,
which is annoying as i need to use a Due as the controller will be simulating multiple joysticks, a keyboard and have a serial connection to send vendor specific data to mimic a raildriver controller.

Thank you very much for helping me with this,

I'll see if there's a Due specific part of the forum, and read up on any differences in how the Due resets, and maybe have to change the way i handle the 'train selection' to avoid doing a reset maybe.

Just discovered it may be an I2C thing causing the problem?.

Found that after a lock up, press reset again... oled still locked up, but unplug and re-plug the 2 pins for the I2C data, the display will start up again,
sometimes it will display garbled text, most times it just carries on normally,
if garbled text, another reset and it's back to normal.

Sounds like you should avoid using reset.

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