How to Create a Scrolling Text on a 0.95 Inch 96x64 OLED
To create scrolling text on a 0.95 inch 96x64 color oled display, you need to control the display’s framebuffer, manage pixel shifting, and handle timing with a microcontroller like an Arduino or ESP32. The 0.95 inch 96x64 OLED has a resolution of 96 pixels horizontally and 64 pixels vertically, which means you can display about 12 characters of a standard 5x7 font (like the Adafruit GFX library) at a time, but with scrolling, you can show longer messages. The key is to use a hardware SPI interface for speed—most color OLEDs like the SSD1331 driver run at 8 MHz to 24 MHz, giving you a frame refresh rate of about 60 Hz to 100 Hz. For smooth scrolling, you need to shift the display buffer left or right by one pixel per frame, which requires a buffer of 96 * 64 * 2 bytes (since each pixel is 16-bit color, that’s 12,288 bytes total). On an Arduino Uno with 2 KB of SRAM, you’ll run out of memory, so you need a microcontroller with at least 16 KB of SRAM, like an ESP32 or an STM32. The ESP32 has 520 KB of SRAM, which is plenty. The scrolling effect is achieved by using a circular buffer: you write the text into a larger buffer (say, 256 pixels wide), then shift the starting column index each frame, and send the visible 96-pixel window to the display. This consumes about 2.5 ms per frame at 24 MHz SPI, leaving plenty of time for other tasks. The 0.95 inch 96x64 color oled display supports full 16-bit color (65,536 colors), so you can use different colors for the text and background to improve readability. The display’s contrast ratio is about 10,000:1, and the viewing angle is 160 degrees, so scrolling text is visible even in bright light. The total power consumption is about 20 mA at 3.3V, which is low enough for battery-powered projects.
When implementing scrolling text, you have to decide between hardware scrolling (using the display’s built-in scrolling command) and software scrolling. The SSD1331 driver does not have a native scrolling command like some Sharp memory LCDs, so you must do software scrolling. This means you update the entire framebuffer each frame. For a 96x64 display with 16-bit color, each frame is 12,288 bytes. At 60 Hz, that’s 737,280 bytes per second, which is well within the SPI bandwidth of 24 MHz (3 MB/s). But the bottleneck is the microcontroller’s RAM and CPU. On an ESP32, you can use DMA (Direct Memory Access) to send the SPI data without blocking the CPU, allowing you to run other tasks like reading a sensor or updating a Wi-Fi connection. The scrolling speed is controlled by the delay between frames: a delay of 16 ms gives 60 Hz, but for slower scrolling, you can add a delay of 50 ms to 100 ms. The text speed is measured in pixels per second: if you shift by 1 pixel per frame at 60 Hz, the text moves at 60 pixels per second. On a 96-pixel wide display, a 100-character message (like “This is a very long scrolling text message on a 0.95 inch OLED display”) would take about 8 seconds to scroll across, assuming each character is 5 pixels wide plus 1 pixel spacing, so 6 pixels per character, giving 600 pixels total, plus 96 pixels for the display width, so 696 pixels / 60 pixels per second = 11.6 seconds. You can adjust the speed by changing the shift amount (e.g., 2 pixels per frame for faster scrolling).
The font size is critical. The standard 5x7 font is 5 pixels wide and 7 pixels tall, but you can use larger fonts like 8x8 or 10x14. However, on a 64-pixel tall display, a 10x14 font gives you only 4 lines of text (64 / 14 = 4.57, so 4 lines with 2 pixels of spacing). For scrolling text, you typically use a single line of large text that fills the height, like a 8x16 font (8 pixels wide, 16 pixels tall), which gives you 4 lines of scrolling text. But for a single line of scrolling text, you can use a 16x32 font (16 pixels wide, 32 pixels tall), which takes up half the display height, leaving room for static graphics. The number of characters visible at once depends on the font width: for a 5x7 font, you can fit 19 characters (96 / 5 = 19.2, but with 1 pixel spacing, it’s 96 / 6 = 16 characters). For a 8x16 font, you fit 12 characters (96 / 8 = 12). For a 16x32 font, you fit 6 characters. The scrolling buffer must be large enough to hold the entire message. For a 100-character message using a 5x7 font, the buffer width is 100 * 6 = 600 pixels, plus 96 pixels for the display, so 696 pixels. Each pixel is 2 bytes, so the buffer is 696 * 64 * 2 = 89,088 bytes, which is too large for an Arduino Uno but fits in an ESP32’s 520 KB SRAM. If you use a 16x32 font, the buffer is 100 * 17 (16 pixels plus 1 spacing) = 1,700 pixels, times 64 pixels tall (but actually 32 pixels tall for the font, so you only need 32 rows), so 1,700 * 32 * 2 = 108,800 bytes, still within ESP32 limits. To save memory, you can use a smaller font or a shorter message, or you can use a technique called “partial buffer update” where you only store the text as a bitmap and render it on the fly. But that increases CPU usage. A better approach is to use a circular buffer: you store the text as a character array, and each frame you calculate the pixel positions for the visible portion. This reduces memory to just the character array (e.g., 100 bytes) plus a small buffer for the current line (96 * 64 * 2 = 12,288 bytes). The trade-off is that you need to render the font each frame, which takes about 10 ms on an ESP32 at 240 MHz, but you can optimize by pre-rendering the font glyphs into a bitmap in flash memory.
The electrical interface is straightforward. The display uses SPI with 4 pins: SCK (clock), MOSI (data), DC (data/command), CS (chip select), and RST (reset). The typical wiring for an ESP32 is: SCK to GPIO 18, MOSI to GPIO 23, DC to GPIO 2, CS to GPIO 5, RST to GPIO 4, VCC to 3.3V, and GND to ground. The display’s logic voltage is 3.3V, but it can tolerate 5V on the control pins if you use a level shifter. The SPI frequency should be set to 8 MHz for reliable operation, but you can push it to 24 MHz if the wiring is short (less than 10 cm). The display’s initialization sequence is critical: you need to set the display to normal mode, set the contrast (usually 0x80 for 128 steps), and set the color depth to 16-bit. The initialization code typically takes about 50 ms. After that, you can use the Adafruit SSD1331 library or write your own commands. The library provides functions like setCursor(), print(), and scroll(), but the scroll function is not built-in, so you need to implement it yourself. The basic loop is: clear the display, draw the text at a specific x offset, then shift the offset by 1 pixel each frame. The drawing function uses the drawBitmap() or drawChar() functions. For smooth scrolling, you need to use double buffering: you draw to an off-screen buffer, then copy the buffer to the display. This avoids flickering. The buffer is 12,288 bytes, and copying it to the display takes about 2.5 ms at 24 MHz SPI. The total time per frame is about 10 ms (drawing) plus 2.5 ms (SPI transfer) plus 16 ms (delay for 60 Hz) = 28.5 ms, which is within the 16 ms target for 60 Hz. So you can achieve 60 Hz scrolling if you optimize the drawing code. For example, on an ESP32, you can use the esp_timer to create a periodic interrupt that updates the display every 16 ms. The scrolling speed is controlled by a variable: scroll_speed = 1 means 1 pixel per frame, scroll_speed = 2 means 2 pixels per frame, etc. The maximum speed is limited by the pixel clock: at 2 pixels per frame at 60 Hz, the text moves at 120 pixels per second, which is fast but still readable for short messages.
Color management is important for readability. The display supports 16-bit color in RGB565 format: 5 bits for red, 6 bits for green, 5 bits for blue. You can use any of the 65,536 colors. For scrolling text, a common choice is white text on a black background (0xFFFF on 0x0000), but you can also use yellow on blue (0xFFE0 on 0x001F) for high contrast. The contrast ratio is 10,000:1, so the text is sharp. The display’s brightness is about 100 cd/m², which is enough for indoor use. If you need outdoor visibility, you can increase the contrast by setting the display’s contrast register to 0xFF (maximum). But this increases power consumption to about 25 mA. The display’s refresh rate is set by the internal oscillator, which is about 60 Hz by default. You can change it by writing to the oscillator frequency register, but it’s not recommended because it can cause flickering. The scrolling text will appear smooth if the refresh rate is at least 30 Hz, but 60 Hz is ideal. The human eye perceives motion as smooth at 24 Hz, but for scrolling text, 60 Hz is better to avoid jitter. The display’s response time is about 10 ms, so there is no ghosting.
Practical implementation steps: First, wire the display to your microcontroller. Second, install the Adafruit SSD1331 library (or use the 0.95 inch 96x64 color oled display’s datasheet to write your own driver). Third, initialize the display with the correct sequence: send command 0xAE (display off), then 0xA0 (set remap), 0xB0 (set start line), 0xC8 (set COM scan direction), 0x81 (set contrast), 0x8F (set pre-charge), 0xA4 (set display mode), 0xA6 (set normal display), 0xAF (display on). Fourth, create a buffer of 96 * 64 * 2 bytes. Fifth, implement a function to draw a character at a given x, y position using a font bitmap. Sixth, in the main loop, increment a variable x_offset from 0 to (text_width + 96), and for each frame, draw the text at position 96 - x_offset, then send the buffer to the display. The text width is the number of characters times the font width (including spacing). For example, for a 50-character message using a 5x7 font, the text width is 50 * 6 = 300 pixels. So the scrolling will take (300 + 96) / 60 = 6.6 seconds at 60 Hz. You can loop the text by resetting the offset when it reaches the end. To make the text appear from the right side, you start with x_offset = 0, which means the text is off-screen to the right, and as x_offset increases, the text moves left. The text becomes fully visible when x_offset is greater than 96, and then it scrolls off the left side. The formula for the x position of the first character is: x = 96 - x_offset + (character_index * font_width). You only draw characters that have a visible x position between 0 and 95. This is efficient because you skip drawing off-screen characters. The drawing function uses the drawPixel() function, which sets a pixel in the buffer. For a 5x7 font, each character is 5 bytes wide (each byte represents 8 pixels, but you only use 7 rows), so you need to unpack the bitmap. The Adafruit GFX library provides a font table in flash memory, which is about 1 KB for 95 characters. You can also use a custom font like the “Tom Thumb” font (4x6) to fit more characters, but it’s less readable.
Data on performance: On an ESP32 at 240 MHz, drawing a single 5x7 character takes about 0.1 ms (using the Adafruit GFX library). For a 50-character message, drawing takes 5 ms. The SPI transfer takes 2.5 ms. So the total frame time is 7.5 ms, which is well within the 16 ms target for 60 Hz. You can even run at 120 Hz (8.3 ms per frame) if you reduce the delay. But the display’s internal refresh rate is 60 Hz, so sending frames faster than 60 Hz doesn’t improve smoothness. On an Arduino Uno, the same code would take about 50 ms per frame (due to slow SPI and CPU), so you can only achieve 20 Hz, which is still acceptable for scrolling text but may have slight jitter. The memory usage on an ESP32 is about 12 KB for the buffer, plus 1 KB for the font, plus 2 KB for the stack, totaling 15 KB, which is negligible compared to the 520 KB available. On an Arduino Uno, you only have 2 KB of SRAM, so you cannot use a full framebuffer. Instead, you must use a technique called “partial update” where you only update the pixels that change. This is complex because scrolling text changes every pixel each frame. A workaround is to use a smaller display resolution (like 96x64) but with a 1-bit color depth (monochrome), which reduces the buffer to 96 * 64 / 8 = 768 bytes, which fits in an Arduino Uno. But the color OLED requires 16-bit color, so you need a microcontroller with more RAM. The ESP32 is the most common choice for this project. The cost of an ESP32 is about $3, and the display is about $10, so the total is under $15.
Advanced techniques: You can add effects like “ping-pong” scrolling (text bounces left and right) or “vertical” scrolling (text moves up and down). For vertical scrolling, you shift the y offset instead of x. The buffer is the same size, but you need to handle the text height. For a 5x7 font, the text height is 7 pixels, so you can scroll multiple lines vertically. The display’s height is 64 pixels, so you can fit 9 lines of 5x7 text (64 / 7 = 9.14, with 1 pixel spacing, it’s 8 lines). For vertical scrolling, you can show a long list of messages. The implementation is similar: you have a buffer of 96 * (text_height * number_of_lines) pixels, and you shift the y offset. But the buffer size can become large. For 100 lines of 5x7 text, the buffer is 96 * 700 * 2 = 134,400 bytes, which still fits in an ESP32. You can also combine horizontal and vertical scrolling for a “marquee” effect. Another technique is to use the display’s hardware windowing feature: you can set a window (like a 96x64 rectangle) and only update that region. This reduces SPI traffic if you only update a portion of the screen. For scrolling text, you can update only the 96x64 region, but since the entire screen is scrolling, you still need to update the whole buffer. However, you can use the windowing feature to clear only the area where the text will be drawn, which saves time. The SSD1331 supports window commands: 0x15 (set column address) and 0x75 (set row address). You can set the window to the entire display (0 to 95 for columns, 0 to 63 for rows) and then send the pixel data. This is faster than sending individual pixels.
Common pitfalls: The display’s SPI pins are 3.3V logic, so if you use a 5V microcontroller like an Arduino Uno, you need a level shifter or you risk damaging the display. The display’s maximum SPI frequency is 24 MHz, but some clones may only work at 8 MHz. Always test with a lower frequency first. The initialization sequence must be exact; missing a command can cause the display to show garbage. The contrast setting is critical: if set too high, the display may draw too much current and overheat. The typical contrast value is 0x80 for 128 steps, but you can adjust it for your environment. The display’s viewing angle is 160 degrees, but the colors may shift at extreme angles. For scrolling text, this is not a problem because the text is usually viewed head-on. The display’s lifetime is about 50,000 hours (5.7 years of continuous use), which is fine for most projects. The display’s operating temperature is -40°C to 85°C, so it can be used outdoors. The display’s weight is about 2 grams, so