Skip to content

Add one pixel horizontal scroll commands for 0x2C and 0x2D #293

@greenonline

Description

@greenonline

There are two undocumented (yet hinted at) commands for one-pixel scroll, 0x2C and 0x2D (right and left, respectively).

From this answer to Scroll long text on OLED display:

The SSD1306 chip provides commands to enable both continuous scrolling and 1 pixel scroll. For our purpose of scrolling long text, the continuous scroll is not useful, as we want to scroll exactly one pixel. Therefore, we need to use the 1 pixel scroll command. The commands take the same parameters, except for the first opcode byte. For some reason in the widely circulated (and pretty old) datasheet online, the 1 pixel scroll command is missing, but it is there in HW.

#define CMD_CONTINUOUS_SCROLL_H_RIGHT		0x26
#define CMD_CONTINUOUS_SCROLL_H_LEFT		0x27
#define CMD_ONE_COLUMN_SCROLL_H_RIGHT		0x2C
#define CMD_ONE_COLUMN_SCROLL_H_LEFT		0x2D

Use the latter two.

It is easy enough to implement these undocumented commands, by adding two new member functions. One way is to duplicate and modify the two existing functions for continuous horizontal scroll, startscrollright() and startscrollleft(). See SSD1306 - Is there a way to scroll a certain number of pixels only?, for more details.

Implementing the missing commands

For example, duplicating and then renaming startscrollright() and startscrollleft() as startscrollrightone() and startscrollleftone():

Add to Adafruit_SSD1306.h the following #define lines:

#define SSD1306_RIGHT_HORIZONTAL_SCROLL_ONE 0x2C          ///< Init right scroll one pixel
#define SSD1306_LEFT_HORIZONTAL_SCROLL_ONE 0x2D           ///< Init left scroll one pixel

and the following two declarations:

  void startscrollrightone(uint8_t start, uint8_t stop);
  void startscrollleftone(uint8_t start, uint8_t stop);

Add to Adafruit_SSD1306.cpp the following two definitions

/*!
    @brief  Activate a 1 pixel right-handed scroll for all or part of the display.
    @param  start
            First row.
    @param  stop
            Last row.
    @return None (void).
*/
// To scroll the whole display, run: display.startscrollrightone(0x00, 0x0F)
void Adafruit_SSD1306::startscrollrightone(uint8_t start, uint8_t stop) {
  TRANSACTION_START
  static const uint8_t PROGMEM scrollList1a[] = {
      SSD1306_RIGHT_HORIZONTAL_SCROLL_ONE, 0X00};
  ssd1306_commandList(scrollList1a, sizeof(scrollList1a));
  ssd1306_command1(start);
  ssd1306_command1(0X00);
  ssd1306_command1(stop);
  static const uint8_t PROGMEM scrollList1b[] = {0X00, 0XFF,
                                                 SSD1306_ACTIVATE_SCROLL};
  ssd1306_commandList(scrollList1b, sizeof(scrollList1b));
  TRANSACTION_END
}

/*!
    @brief  Activate a 1 pixel left-handed scroll for all or part of the display.
    @param  start
            First row.
    @param  stop
            Last row.
    @return None (void).
*/
// To scroll the whole display, run: display.startscrollleftone(0x00, 0x0F)
void Adafruit_SSD1306::startscrollleftone(uint8_t start, uint8_t stop) {
  TRANSACTION_START
  static const uint8_t PROGMEM scrollList2a[] = {SSD1306_LEFT_HORIZONTAL_SCROLL_ONE,
                                                 0X00};
  ssd1306_commandList(scrollList2a, sizeof(scrollList2a));
  ssd1306_command1(start);
  ssd1306_command1(0X00);
  ssd1306_command1(stop);
  static const uint8_t PROGMEM scrollList2b[] = {0X00, 0XFF,
                                                 SSD1306_ACTIVATE_SCROLL};
  ssd1306_commandList(scrollList2b, sizeof(scrollList2b));
  TRANSACTION_END
}

Examples

Example#1 - Text scroll

Scrolls some text by 50 pixels to the right:

void testscrolltext_by50pixels(void) {
  display.clearDisplay();

  display.setTextSize(2);  // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10, 0);
  display.println(F("scroll"));
  display.display();  // Show initial text
  delay(100);

  int wait_time = 12;  // For the Xiao: 15 is the minimum (maybe 12... but definitely not 11)

  for (int i = 0; i < 50; i++) {
    display.startscrollrightone(0x00, 0x0F);
    delay(wait_time);
  }

  delay(2000);
}

Note: I found, on the Xiao at least, that a delay is required. Without any delay() I found that no scrolling occurred at all. If wait_time is less than 12 ms delay, then that causes some of the scrolls to fail, and the full 50 pixels are not scrolled. I found that the closer that the delay is to zero, the less distance, in pixels scrolled, the text/images actually move. The minimum length of the required delay probably depends upon the frequency of the µController, so you may need to experiment for your particular setup.

Example#2 - Scrolling landscape

Making use of the pages feature of the SSD1306 - in conjunction with varying single pixel scroll rates - here is a scrolling landscape:

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128  // OLED display width, in pixels
#define SCREEN_HEIGHT 64  // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO:       A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO:   2(SDA),  3(SCL), ...
#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
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define LOGO_HEIGHT 64
#define LOGO_WIDTH 128

// Generated from https://javl.github.io/image2cpp/
// 'StarWarsLandscape', 128x64px
const unsigned char epd_bitmap_StarWarsLandscape_WonB[] PROGMEM = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x01, 0xc1, 0x80, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x03, 0x00, 0x80, 0x00, 0x0f, 0x8e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
	0x00, 0x0e, 0x00, 0xc0, 0x00, 0x18, 0xf8, 0x30, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xc8, 0xf0, 0x00,
	0x00, 0x18, 0x00, 0x60, 0x00, 0x30, 0x60, 0x10, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x0d, 0x08, 0x00,
	0x00, 0x30, 0x00, 0x20, 0x00, 0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x07, 0x0c, 0x00,
	0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x10, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x04, 0x00,
	0x00, 0x40, 0x00, 0x40, 0x00, 0xc0, 0x00, 0x30, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x8c, 0x00,
	0x00, 0x20, 0x00, 0xc0, 0x01, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x80, 0x01, 0x80, 0x78, 0x00,
	0x00, 0x20, 0x20, 0x80, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x30, 0x00,
	0x00, 0x18, 0x31, 0x80, 0x01, 0x20, 0x00, 0x18, 0x00, 0x00, 0x00, 0xc0, 0x05, 0x00, 0x20, 0x00,
	0x00, 0x0c, 0x6b, 0x00, 0x01, 0xc0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x60, 0x01, 0x80, 0x60, 0x00,
	0x00, 0x03, 0x8e, 0x00, 0x00, 0x80, 0x10, 0x08, 0x00, 0x00, 0x00, 0x40, 0x03, 0xc1, 0xc0, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x18, 0x18, 0x00, 0x00, 0x00, 0x60, 0x0e, 0x7f, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x38, 0x70, 0x00, 0x00, 0x00, 0x3e, 0xf8, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xec, 0xc0, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
	0x00, 0x00, 0x03, 0x88, 0x00, 0x03, 0xff, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x18, 0x00,
	0x00, 0x00, 0x06, 0x08, 0x00, 0x06, 0x00, 0x80, 0x00, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x38, 0x00,
	0x80, 0x00, 0x0c, 0x0f, 0x00, 0x0c, 0x00, 0xc0, 0x00, 0x02, 0x1f, 0x80, 0x00, 0x01, 0xe8, 0x00,
	0xe0, 0x00, 0x78, 0x00, 0xe0, 0x18, 0x00, 0x40, 0x00, 0x06, 0x00, 0xc0, 0x00, 0x03, 0x0c, 0x01,
	0x20, 0x00, 0xc0, 0x00, 0x30, 0x30, 0x00, 0x40, 0x00, 0x04, 0x00, 0x40, 0x00, 0x0c, 0x07, 0xc3,
	0x30, 0x03, 0x00, 0x00, 0x0c, 0x60, 0x00, 0x20, 0x00, 0x0c, 0x00, 0x40, 0x00, 0x18, 0x00, 0x76,
	0x10, 0x02, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x20, 0x00, 0x18, 0x00, 0x7c, 0x00, 0x30, 0x00, 0x1c,
	0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x07, 0x80, 0x20, 0x00, 0x04,
	0x0f, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x60, 0x00, 0x00, 0xc0, 0x20, 0x00, 0x00,
	0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0xc0, 0x00, 0x00, 0x20, 0xc0, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x80, 0x00, 0x00, 0x1f, 0x40, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x18, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x18, 0x3c, 0x0e, 0x03, 0x0c, 0x00, 0x1c, 0x00, 0x00, 0x3f, 0x8e, 0x00, 0x07, 0xe0, 0x1e, 0x00,
	0x2e, 0x63, 0xb9, 0xce, 0x04, 0x67, 0xe6, 0xe0, 0x00, 0x60, 0xcf, 0x00, 0x6d, 0xf8, 0x73, 0x80,
	0xe3, 0xc0, 0xe0, 0x78, 0x07, 0xfc, 0x03, 0xbe, 0x01, 0xc0, 0x79, 0xfd, 0xf8, 0xcf, 0xc0, 0xff,
	0x41, 0x80, 0x40, 0x30, 0x00, 0x18, 0x03, 0x03, 0xff, 0x00, 0x19, 0x87, 0x00, 0x03, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

void setup() {

	Serial.begin(9600);

	// by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
	//display.begin(SSD1306_SWITCHCAPVCC);  // Waaay too basic
	// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
	if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
		Serial.println(F("SSD1306 allocation failed"));
		for (;;)
			;  // Don't proceed, loop forever
	}
	// init done

	// Clear the buffer.
	display.clearDisplay();

	// bitmap display
	display.drawBitmap(0, 0, epd_bitmap_StarWarsLandscape_WonB, 128, 64, 1);
	display.display();
}

void loop() {
    scroll_landscape();
}

void scroll_landscape(void) {

	int wait_time = 12;  // For the Xiao: 15 is the minimum (maybe 12... but definitely not 11)

	for (int j = 0; j < 2; j++) {
		for (int i = 0; i < 2; i++) {
			display.startscrollleftone(0x06, 0x07);
			delay(wait_time);
		}
		display.startscrollleftone(0x03, 0x05);
		delay(wait_time);
	}
	display.startscrollleftone(0x00, 0x02);
	delay(wait_time);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions