/*
This sketch can display scrolling marquees containing images on
an OLED screen.
As images can contain text, messages in any language can be shown.
This is especially useful for displaying non Latin characters.
Previous tasks:
1st: the text has to be converted into an image with the help of
a drawing editor (e.g. GIMP).
2nd: the image has to bee changed into a byte arrays. There
is a useful tool called image2cpp to accomplish it at
https://javl.github.io/image2cpp/
Written in 2024 by raphik
*/
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);
char message1[]= "This message is real text, but the two messages below are images instead. / ";
int minX1 = -6 * strlen(message1);
int minX2 = -247; // image width
int minY2 = 13; // image height
int gapX2 = 35 ; // space between image repetitions
int minX3 = -217; // image width
int minY3 = 11; // image height
int gapX3 = 50 ; // space between image repetitions
int colMarquee1 = display.width();
int colMarquee2 = display.width();
int colMarquee3 = minX3; // <-- (!) when scrolling to the right
uint32_t chronoMarquee1 = millis();
uint32_t chronoMarquee2 = millis();
uint32_t chronoMarquee3 = millis();
// this is an image containing a text
const unsigned char message2 [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x10, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x64, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x80, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x90, 0x24, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x04, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x88, 0x1c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x03, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x48, 0x06, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0xc0, 0xff, 0xf8, 0x7f, 0xff,
0xff, 0xfc, 0x7f, 0x0f, 0xff, 0xfc, 0x3f, 0xff, 0xc3, 0xff, 0xfc, 0x3f, 0x01, 0xff, 0xcc, 0xff,
0xff, 0xf0, 0x0f, 0xff, 0x87, 0xff, 0xf0, 0xff, 0xff, 0xc7, 0xe0, 0x24, 0x10, 0x04, 0x84, 0x04,
0x10, 0x22, 0x00, 0x82, 0x08, 0x12, 0x41, 0x00, 0x10, 0x48, 0x02, 0x00, 0x10, 0x92, 0x42, 0x40,
0x40, 0x02, 0x41, 0x02, 0x41, 0x00, 0x42, 0x41, 0x00, 0x40, 0x24, 0xf0, 0x3c, 0xa4, 0x7c, 0xf0,
0x22, 0x07, 0xe2, 0x78, 0x12, 0x4f, 0x00, 0x13, 0xc8, 0x1e, 0x00, 0xf4, 0x82, 0x52, 0x46, 0xc0,
0x02, 0x4f, 0x02, 0x4f, 0xc0, 0x46, 0x4f, 0x03, 0xc0, 0x64, 0x80, 0x44, 0x9f, 0x85, 0x10, 0x7e,
0x08, 0x92, 0x40, 0x12, 0x49, 0x83, 0xf2, 0x08, 0x10, 0x01, 0x03, 0x8e, 0x53, 0xc9, 0x40, 0x06,
0x48, 0x02, 0x51, 0x20, 0x39, 0x51, 0x02, 0x00, 0x84, 0xf0, 0x44, 0x90, 0x85, 0x10, 0x22, 0x08,
0x92, 0x38, 0x12, 0x50, 0x81, 0x13, 0xc8, 0x1e, 0x01, 0x02, 0x23, 0xcc, 0x51, 0x40, 0x08, 0x4f,
0x02, 0x51, 0x20, 0x45, 0x51, 0x03, 0xc0, 0x4c, 0x88, 0x3c, 0x88, 0x44, 0xf0, 0x02, 0x07, 0x92,
0x04, 0x0e, 0x48, 0x80, 0x12, 0x28, 0x11, 0x00, 0x99, 0x11, 0x44, 0x48, 0x40, 0x04, 0xc8, 0x81,
0x0f, 0x20, 0x46, 0x4f, 0x02, 0x20, 0x34, 0x98, 0x04, 0x84, 0x44, 0x10, 0x02, 0x00, 0xa2, 0xc8,
0x02, 0x48, 0x80, 0x12, 0x68, 0x13, 0x00, 0x70, 0x92, 0x42, 0x48, 0x44, 0x03, 0x49, 0x80, 0x81,
0x40, 0x46, 0x41, 0x02, 0x60, 0x04, 0x70, 0x04, 0x84, 0x24, 0x10, 0x02, 0x00, 0x82, 0x78, 0x02,
0x47, 0x00, 0x11, 0xc8, 0x0e, 0x40, 0x08, 0x4e, 0x42, 0x46, 0x44, 0x00, 0x47, 0x00, 0x41, 0x00,
0x38, 0x41, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00
};
// this is an image containing a text
const unsigned char message3 [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x08, 0x00, 0x00,
0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x20, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x00,
0x00, 0x00, 0x24, 0x00, 0x04, 0x08, 0x80, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x10,
0x08, 0x01, 0x20, 0x00, 0x00, 0x08, 0x40, 0x40, 0x00, 0x00, 0x81, 0x82, 0x01, 0x00, 0x24, 0x00,
0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00,
0x70, 0x08, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x24, 0x00, 0x0e, 0x08, 0x86, 0x00,
0x0e, 0x00, 0x00, 0x70, 0x00, 0x38, 0x04, 0x70, 0x38, 0xf9, 0x20, 0x00, 0x50, 0x08, 0x1c, 0x00,
0x00, 0x20, 0x80, 0x8e, 0x07, 0x1f, 0x24, 0x00, 0x0a, 0x08, 0x47, 0x00, 0x0b, 0x05, 0xc4, 0x90,
0x03, 0x2c, 0x1c, 0x51, 0x28, 0x19, 0x20, 0x06, 0x70, 0x09, 0x26, 0x40, 0x09, 0x24, 0x83, 0x8a,
0x25, 0x03, 0x24, 0x00, 0xce, 0x08, 0x45, 0x80, 0x09, 0x05, 0x47, 0x10, 0x43, 0xb4, 0x12, 0x71,
0x38, 0x29, 0x20, 0x87, 0x10, 0x09, 0xc4, 0x41, 0x0b, 0x64, 0x82, 0x4e, 0x27, 0x05, 0x24, 0x10,
0xe2, 0x08, 0x46, 0x80, 0x4f, 0x05, 0xff, 0xe0, 0x42, 0xfc, 0x0f, 0xff, 0xff, 0xcf, 0x20, 0x85,
0xe0, 0x4f, 0xfb, 0xc1, 0x0f, 0xff, 0x81, 0xff, 0xff, 0xf9, 0xe4, 0x10, 0xbc, 0x09, 0xff, 0x80,
0x00, 0x08, 0x40, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x01,
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x30, 0xc0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x30, 0x02, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
0x00, 0x00, 0x00, 0x00
};
void setup(){
Serial.begin(9600);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextColor(WHITE, BLACK);
display.setTextSize(1);
display.setCursor(0,0);
display.println("Image marquee test");
display.setTextWrap(false); // <-- this is really important!
}
void loop(){
uint32_t rightNow = millis();
// MARQUEE 1 - text scrolling leftwards
if (rightNow > chronoMarquee1 + 50) {
chronoMarquee1 = rightNow;
display.setTextSize(1);
display.setCursor(colMarquee1,18);
display.print(message1);
display.setCursor(colMarquee1-minX1,18);
display.print(message1);
colMarquee1 -=2;
if(colMarquee1 < minX1) colMarquee1 -= minX1;
}
// MARQUEE 2 - image scrolling leftwards
if (rightNow > chronoMarquee2 + 150) {
chronoMarquee2 = rightNow;
display.drawBitmap(colMarquee2, 32, message2, -minX2, minY2, WHITE, BLACK );
display.drawBitmap(colMarquee2-minX2+gapX2, 32, message2, -minX2, minY2, WHITE, BLACK );
colMarquee2 -=2;
if(colMarquee2 < minX2) colMarquee2 -= minX2-gapX2;
}
// MARQUEE 3 - image scrolling rightwards
if (rightNow > chronoMarquee3 + 50) {
chronoMarquee3 = rightNow;
display.drawBitmap(colMarquee3, 52, message3, -minX3, minY3, WHITE, BLACK );
display.drawBitmap(colMarquee3+minX3-gapX3, 52, message3, -minX3, minY3, WHITE, BLACK );
colMarquee3 +=1;
//if(colMarquee3 > display.width()) colMarquee3 -= -minX3+gapX3;
if(colMarquee3 > display.width()) colMarquee3 += minX3-gapX3;
}
display.display();
}