Hello friends, I hope you all are doing great. Today, I am going to share the new OLED SSD1306 Library for Proteus V1.0. The small 0.96" OLED display is everywhere in Arduino projects: 128 x 64 sharp, bright pixels, no backlight, and only four wires, GND, VCC, SCL and SDA. It shows text, numbers, graphs, icons and little animations, so it is the display to pick when a character LCD is not enough. With this OLED SSD1306 Library for Proteus, your sketch draws on an OLED that behaves like the real module, pixel by pixel.
This is our first OLED library, and it is our own model of the SSD1306 controller: its I2C interface, its 1 KB display memory, its commands, its charge pump and its scrolling. You get six devices: a white, a blue and a yellow-blue OLED, each as OLED SSD1306 Simple, with a live STATUS panel on the schematic, and OLED SSD1306 Advance, with a pop-up window, a bus log and four OLED test tools. The demo runs a small dashboard with the popular Adafruit SSD1306 and Adafruit GFX libraries on two Arduino UNOs, one OLED at 0x3C and one at 0x3D, with the Simple interface of our TEP Serial Monitor.
NOTICE: This library is very special to our team. An OLED is not a character LCD: every one of its 8,192 pixels is a bit in the controller's memory, and the controller refreshes the glass by itself. We modelled that memory, the way the module maps it to the glass, the charge pump that a real module needs and the scrolling the chip does on its own. It took our team a lot of hard work and many test runs. Your feedback is the fuel that keeps us going, so please tell us what you think in the comments below or in the OLED Library board of our forum. And if our free libraries help you, you can buy us a coffee. So, let's get started with the OLED SSD1306 Library for Proteus V1.0:
What is an OLED SSD1306 Display?
An OLED SSD1306 display is a small graphic display module: a 0.96" glass with 128 x 64 pixels and the SSD1306 controller chip behind it, on a blue board of about 27 x 28 mm with a 4-pin I2C header. OLED means organic LED: every pixel is a tiny LED that makes its own light. That is why an OLED needs no backlight, why black is really black, and why the picture stays sharp from any angle. The glass comes in one colour (white or blue) or in two colours: a yellow band on top and blue below.
Unlike a character LCD, the OLED has no character set of its own. The SSD1306 only knows pixels. Your Arduino library draws the letters, lines and pictures into a picture in memory and sends it to the display. On the Arduino, that is the job of the Adafruit SSD1306 library together with the Adafruit GFX library. If you have used a big graphic LCD before, read our Interfacing of Arduino with GLCD tutorial: the idea is the same, but the OLED needs only four wires.
The OLED SSD1306 at a Glance
| Feature | Value |
|---|---|
| Display | 0.96", 128 x 64 pixels, one colour (white or blue) or two colours (yellow rows 0 - 15, blue below) |
| Controller | SSD1306 with 1 KB display RAM (GDDRAM) |
| Board | About 27 x 28 mm, four mounting holes |
| Interface | I2C: SDA and SCL (A4 and A5 on an Arduino UNO), plus GND and VCC |
| Address | 0x3C (most modules) or 0x3D, set by a resistor on the back (marked 0x78 / 0x7A) |
| Supply | 3.3 V or 5 V on VCC (the module has its own regulator) |
| Panel voltage | Made by the internal charge pump; the sketch must switch it on |
| I2C speed | Up to 400 kHz; a full picture is 1024 bytes, about 37 ms at 400 kHz |
| Arduino libraries | Adafruit SSD1306 + Adafruit GFX: Adafruit_SSD1306 display(128, 64, &Wire, -1) |
Terms You Will See in This Article
| Term | Meaning |
|---|---|
| I2C | A two-wire bus: SDA carries the data, SCL the clock; many devices share it, each with its own address |
| Address | The 7-bit number a device answers to, written in hex (0x3C) |
| GDDRAM | The SSD1306's display memory: 128 columns x 8 pages, 1024 bytes |
| Page | A band of 8 pixel rows; one byte of a page is 8 pixels on top of each other |
| Control byte | The first byte after the address: 0x00 = commands follow, 0x40 = picture data follows |
| Charge pump | The circuit inside the SSD1306 that makes the high panel voltage from 3.3 V or 5 V |
| Frame | One whole picture of 1024 bytes, sent by display() in the Adafruit library |
How the SSD1306 Works
The SSD1306 keeps the picture in its GDDRAM: 128 columns and 8 pages. A page is a band of 8 pixel rows, and every byte in it stands for 8 pixels on top of each other, bit 0 at the top. So the pixel at x, y lives in page y / 8, column x, bit y % 8. The controller reads this memory about 88 times a second and lights the pixels; the Arduino only has to write when the picture changes.
Over I2C, every transaction starts with the address and then a control byte: 0x00 means "commands follow", 0x40 means "picture data follows". The Adafruit library keeps a copy of the whole picture, 1024 bytes, in the Arduino's RAM. All drawing functions (print, drawLine, drawCircle and so on) change only that copy. Then display() sets the column and page windows to the whole screen and sends all 1024 bytes, in pieces of 31 bytes, because Arduino's Wire library has a 32-byte buffer and the control byte takes one. At 400 kHz this takes about 37 ms.
One more thing makes OLED modules special: the charge pump. The organic pixels need a much higher voltage than 3.3 or 5 V, and the 4-pin module has no extra supply pin for it, so the SSD1306 makes it itself. The sketch must switch the pump on before it turns the display on (the command 8D 14). display.begin(SSD1306_SWITCHCAPVCC, 0x3C) does exactly that. Without it the panel stays black, even though the I2C bus works perfectly. Our model behaves the same way.
What's New in OLED SSD1306 Library for Proteus V1.0
Here is what the first version brings:
- Our own SSD1306 model in
TEPOLED.DLL: the I2C interface with the control byte, the 1 KB GDDRAM, the three addressing modes, contrast, inverse, the mirror and flip settings, the multiplex and COM pin settings, the charge pump and the controller's own scrolling. - A real 0.96" module on the schematic: the blue board drawn at twice the real size, so all 8,192 pixels stay readable, in white, blue or yellow-blue.
- An address selector 0x3C / 0x3D on the board that works while the simulation runs, with a red WRONG ADDRESS warning.
- Six devices in
TEPOLED12864.LIB: three glass colours, each Simple and Advance. - Four OLED test tools on the Advance: Init check, I2C bus, Speed and Pixels, plus a pixel grid with x / y numbers on the big glass.
- A two-UNO dashboard demo with Adafruit SSD1306 and GFX: big text, a moving sine wave, a bar, the TEP logo, shapes, and Serial commands such as TEXT, BAR, INVERT, CONTRAST and SCROLL.
- A compact package: about 1.38 MB, without the C++ source code.
OLED SSD1306 Simple vs OLED SSD1306 Advance
All six devices share one model and the same module. They differ in the glass colour and in where you see the extras:
| Device | Name in Pick Devices | Glass | Extras |
|---|---|---|---|
| OLED12864WHITETEP | OLED SSD1306 128x64 V1.0 White - Simple | White | STATUS panel on the sheet |
| OLED12864WHITEADVTEP | OLED SSD1306 128x64 V1.0 White - Advance | White | Pop-up window and tools |
| OLED12864BLUETEP | OLED SSD1306 128x64 V1.0 Blue - Simple | Blue | STATUS panel on the sheet |
| OLED12864BLUEADVTEP | OLED SSD1306 128x64 V1.0 Blue - Advance | Blue | Pop-up window and tools |
| OLED12864YELLOWBLUETEP | OLED SSD1306 128x64 V1.0 Yellow-blue - Simple | Yellow rows 0 - 15, blue below | STATUS panel on the sheet |
| OLED12864YELLOWBLUEADVTEP | OLED SSD1306 128x64 V1.0 Yellow-blue - Advance | Yellow rows 0 - 15, blue below | Pop-up window and tools |
Pick a Simple OLED when you want to see the controller's state and its I2C address right on the schematic. Pick an Advance OLED when the schematic is crowded, or when you want the bus log, the tools and a big copy of the glass. The address selector works on all six.
Download OLED SSD1306 Library for Proteus
Click the button below to download OLED-SSD1306-Library-for-Proteus-v1.0.zip (about 1.38 MB, without the C++ source code):
OLED SSD1306 Library for Proteus V1.0- README.txt: a detailed guide to the files, the wiring, the controller, the test tools and real hardware.
- Proteus Library Files:
TEPOLED12864.LIB(all six devices),TEPSERIALMON.LIBand our Arduino UNO libraryArduinoV3TEP.LIB/ArduinoV3TEP.IDX. - Proteus Model Files:
TEPOLED.DLLandTEPSERIALMON.DLL. - Proteus Simulation:
OLED-SSD1306-ArduinoUnoV3.pdsprj,OLED_SSD1306_Demo.hex(both UNOs run it) and copies of both DLLs. - Arduino Code:
OLED_SSD1306_Demo.ino, the demo sketch. It needs the "Adafruit SSD1306" library from the Library Manager, which offers Adafruit GFX and Adafruit BusIO too.
Want all three OLEDs? This OLED is also part of our OLED Library for Proteus V1.0: the 0.96" SSD1306, the 0.91" 128x32 and the 1.3" SH1106 in one library (14 parts, one DLL), with a demo in which one Arduino UNO drives all three.
Only text to show? Our LCD Library for Proteus V3.0 has the LCD 16x2, LCD 20x4, I2C LCD 16x2 and I2C LCD 20x4 in one library. No Proteus yet? Read How to Download and Install Proteus 7 and 8. New to libraries? Follow How to Install a New Library in Proteus.
How to Install OLED SSD1306 Library for Proteus
- Close Proteus and extract the whole zip file to a normal folder, for example your Desktop.
- Copy the four files from Proteus Library Files into the LIBRARY folder, usually
C:\Program Files (x86)\Labcenter Electronics\Proteus 8 Professional\LIBRARY(on some PCsC:\ProgramData\Labcenter Electronics\Proteus 8 Professional\LIBRARY; use the folder that already holds Proteus's own .LIB files). - Copy
TEPOLED.DLLandTEPSERIALMON.DLLfrom Proteus Model Files into the MODELS folder. - Delete
TEPOLED12864.IDXfrom the LIBRARY folder if there is one; Proteus makes a new index. - Start Proteus, press P and search for OLED12864. You get the six devices.
Tip: the demo project needs the .hex file and the two DLLs beside it, so keep the Proteus Simulation folder together. The library was tested in Proteus 8.5; Proteus 7 is not supported.
The OLED SSD1306 Board in Proteus
The part looks like the real module: the blue board with its four mounting holes, the black glass with the active area of 128 x 64 pixels, and the 4-pin header on top. A real 0.96" OLED is tiny, so we drew it at twice the real size: that way the pixels stay readable on the schematic without zooming in. The pins point up, in the order printed on most modules: GND, VCC, SCL, SDA. Here is OLED1, the white Simple OLED, running the demo's dashboard:
Pinout
| Pin | Name | What it does | Demo connection |
|---|---|---|---|
| 1 | GND | Ground | GND (net label) |
| 2 | VCC | Supply, 3.3 V or 5 V | VCC (net label, the 5 V rail) |
| 3 | SCL | I2C clock | Arduino A5, through the label SCL_1 |
| 4 | SDA | I2C data (open drain, with the module's pull-up) | Arduino A4, through the label SDA_1 |
The Board Up Close
Everything on the board is drawn by the model and changes while the simulation runs. The pixels glow in the glass colour, at the brightness the CONTRAST command sets. The PWR LED is lit while the module has power, and the I2C LED flashes green whenever the Arduino talks to this address. A real module has no LEDs at all, so these are our additions to help you see what happens.
LEDs and Indicators
| Indicator | What it shows | On the real module? |
|---|---|---|
| The pixels | The picture in the controller's RAM, at the contrast's brightness | Yes |
| PWR LED (red) | Lit while the module has power | TEP addition |
| I2C LED (green) | Flashes on every I2C transaction to this address | TEP addition |
| ADDRESS selector | 0x3C or 0x3D, clickable while the simulation runs | TEP addition (a resistor on the back) |
| SIMPLE / ADVANCE badge, PANEL / OPEN PANEL | Which device this is, and the panel button | TEP addition |
Component Properties
| Property | Meaning | Default |
|---|---|---|
| PANEL | Simple devices only: the STATUS panel at the start, OPEN or CLOSED | OPEN |
| ADDRESS | The I2C address, 0x3C or 0x3D; the selector on the board follows it | 0x3C |
Every run starts from these properties; a click on the selector changes the address for that run only. In the demo, OLED2 has ADDRESS = 0x3D.
Three Glass Colours
Real 0.96" OLEDs come in three common colours, and so do our devices. A white or a blue glass shows every pixel in one colour. The yellow-blue glass is a two-colour panel: rows 0 to 15 are yellow, rows 16 to 63 are blue, with a small gap between them. The colours are fixed in the glass, it is not an RGB display, so the sketch cannot choose them: put a header line in the top 16 rows, as our demo does. Here are all three in Proteus, running three different screens of the demo:
The Address Selector: 0x3C or 0x3D
Most 0.96" modules answer at 0x3C. On the back of the board, a small resistor chooses the address, and the print next to it says 0x78 or 0x7A: those are the same addresses written as 8-bit numbers (0x3C x 2 = 0x78, 0x3D x 2 = 0x7A). Moving the resistor gives you 0x3D, so two OLEDs can share one I2C bus.
On our board, the ADDRESS selector does this with a click, even while the simulation runs. We clicked 0x3D on OLED1 while its sketch kept writing to 0x3C. From that moment nothing reaches the OLED any more: the glass keeps its last picture, exactly as a real module would, the banner turns red and says WRONG ADDRESS - THE SKETCH WRITES TO 0x3C, THIS OLED IS 0x3D, and the I2C readout says 0x3D SKETCH 0x3C:
Click the selector again and the clock runs on. The simulation log gets a line for every click. On a real module, a wrong address simply looks like a dead display. In our model you see the reason at once, and the I2C bus tool of the Advance tells you both addresses.
OLED SSD1306 Simple: The STATUS Panel
Under the Simple OLED sits the OLED STATUS panel (see the pictures above):
| Part | What it shows |
|---|---|
| Banner | READY (128x64, the I2C address, DISPLAY ON), WAITING FOR I2C, WRONG ADDRESS, DISPLAY OFF, DARK - THE CHARGE PUMP IS OFF, 128x32 SETTINGS ON A 128x64 GLASS, MIRRORED or UPSIDE DOWN, or NO POWER |
| DISPLAY, CHARGE PUMP | Whether the display and the charge pump are on |
| CONTRAST | The contrast value, 0 to 255 (Adafruit's default is 207) |
| ADDRESSING, SCROLL | The addressing mode (HORIZONTAL, VERTICAL or PAGE) and the scrolling |
| VCC | The supply voltage |
| I2C | This OLED's address and the measured clock, or the address the sketch uses instead |
| FRAME | How long one whole picture takes on the bus |
| WRITES, LOST | Bytes the controller took, and unknown commands or bytes sent without power |
| Bus | The last 6 transfers, newest first |
The red X in the panel's header closes it, and PANEL on the board opens it again.
OLED SSD1306 Advance: The Pop-Up Window
The Advance OLED keeps only the module on the schematic. Here is OLED2, the yellow-blue Advance OLED at 0x3D:
At Run, the "TEP OLED SSD1306 Advance" pop-up opens with the look of our TEP Serial Monitor: move it by its header, resize or minimise it, and reopen it with OPEN PANEL. The header has the Display, Bus log and Tests tabs and the theme, Settings and Help icons.
The Display Page
- The glass: a big copy of the OLED, every pixel visible.
- Tiles: DISPLAY, CHARGE PUMP, CONTRAST, ADDRESSING, ORIENTATION (UPRIGHT, MIRRORED, UPSIDE DOWN or ROTATED 180), MULTIPLEX (64 ROWS), COM PINS and SCROLL.
- GDDRAM as stored: the controller's memory, page by page, as it really is, with the write pointer in blue. While the OLED scrolls, you can watch the memory itself move.
- Supply, frames and mapping: VCC, the panel's own refresh rate (87.6 Hz from the clock setting), the frames received and how long the last one took, the start line and offset, and the pointer.
Above the glass, GLASS: Live / Freeze holds the picture so you can study a fast animation, and PIXEL GRID: On (x / y) draws a line every 8 pixels with the x and y numbers around the glass. These are Adafruit GFX's coordinates, 0, 0 at the top left, so you can read off where to put your setCursor() or drawPixel():
The Bus Log Page
The Bus log lists what the controller took, newest first: one row per command, with its parameters and its meaning, or one row per run of picture data, with its size and where it landed. Read the picture from the bottom up: the last frame came in runs of 31 bytes, page by page (a page is 128 bytes, so it takes about four runs), and the last run is the single byte at page 7, column 127. Then the sketch sent SCROLL SETUP RIGHT and SCROLL ON, and from that moment the controller moves the picture by itself. The filters show only Commands, only Data or only Problems (unknown commands), and Pause and Clear help you catch one moment.
The Tests Page: Four OLED Test Tools
The tools read the bus as the model sees it during the running simulation. Each has a bold "Good for:" line and a verdict per row: green is fine, amber means check, red means wrong. All the results below come from OLED2 in Proteus 8.5.
1. Init Check: Is the Start-Up Right?
| Check | Result in the picture |
|---|---|
| First command | DISPLAY OFF (AE), as the datasheet starts |
| Charge pump | ON (8D 14) |
| Multiplex ratio | 64 rows (A8 3F) |
| COM pins | ALTERNATIVE (DA 12), right for 128x64 |
| Orientation | UPRIGHT (A1 + C8) |
| Addressing mode | HORIZONTAL (Adafruit_SSD1306 sends whole frames this way) |
| Contrast | 207 (0xCF) |
| Display on | Yes (AF), 68.4 ms after power-on |
| RAM before display on | Amber: not written, so the random power-on RAM shows until the first frame |
| Verdict | Initialised like the datasheet's flow for this 128x64 glass |
Good for: "an OLED that stays dark, mirrored, upside down or striped - is the sketch's begin() right?" Each row tells you what to fix: a red charge pump row means a black panel, a 32-row multiplex or sequential COM pins mean 128x32 settings on a 128x64 glass, and the orientation row catches a mirrored or upside-down picture. The amber row is honest, not a fault: Adafruit's begin() switches the display on before the sketch sends its first picture, so for a few milliseconds a real module shows its random power-on memory too.
2. I2C Bus: Is the Sketch Talking to This OLED?
Good for: "a dark OLED - the wrong address, SDA / SCL swapped, the clock and Wire's 32-byte buffer." The tool shows:
| Row | Result in the picture |
|---|---|
| This OLED | 0x3D - SA0 high (the address resistor: 0x7A) |
| The sketch writes to | 0x3D - this OLED (14035 writes since power-on) |
| Measuring for | 189.351 s of simulation: 14036 transactions to 0x3D, 404431 bytes |
| Commands / data | 1149 command transactions (2300 bytes), 12886 data transactions (388096 bytes) |
| Data per transaction | Up to 31 bytes after the 0x40 control byte (Arduino's Wire buffer is 32 bytes) |
| Address checks | 1 to this address, 1 to other addresses (a scanner / auto-detect) |
| SCL clock | 400 kHz (the SSD1306 is rated for 400 kHz) |
The two address checks are the sketch's search at the start: it tried 0x3C first (nobody answered on this bus) and then 0x3D. Below the rows, the last transactions are listed with their bytes: you can see the 0x40 control byte in front of every piece of picture data, and 0x00 in front of the commands. Move the selector of OLED2 and the "sketch writes to" row turns red.
3. Speed: How Fast Can the Picture Change?
Good for: "slow animations - one full frame is 1024 bytes; at 100 kHz it takes four times as long as at 400 kHz." The results:
| Result | Value |
|---|---|
| Time per frame | 36.77 ms on the bus (min 36.75 ms, max 36.85 ms) |
| Best possible | 27 frames per second if the sketch only sent frames |
| The sketch sends | 2.0 frames per second (one every 501 ms) |
| Bus busy | 5.4 % of the time |
| SCL clock | 400 kHz: about 22.5 us per byte (9 clocks + the gaps) |
| Panel refresh | 87.6 Hz: the controller redraws the glass by itself from its RAM |
So an OLED animation can reach about 27 frames per second over I2C at 400 kHz, minus the time your sketch needs to draw. At Wire's default 100 kHz it would be about four times slower. Note the difference between the two speeds: the glass is refreshed 87.6 times a second by the controller whatever the Arduino does, while a new picture arrives only when the sketch calls display().
4. Pixels: Where Does a Pixel Live in the RAM?
Good for: "drawing code and bitmaps - click a pixel: its page, column, bit and the byte the RAM holds." We clicked x 0, y 25, a pixel in the left stem of the big "H". The tool shows the rule from the start of this article at work: page 25 / 8 = 3, bit 25 % 8 = 1, and in Adafruit's buffer the byte is number 0 + 3 x 128 = 384. Below, the 16 bytes of page 3 are drawn as columns of 8 bits: the "H" is its two stems, columns 0 - 1 and 8 - 9 (0xFF), with the bar between them (0x03: rows 24 and 25). This is exactly how a bitmap for drawBitmap() or a font is built.
OLED SSD1306 with Arduino in Proteus
Open OLED-SSD1306-ArduinoUnoV3.pdsprj from the Proteus Simulation folder, with OLED_SSD1306_Demo.hex and both DLLs beside it. Its two Arduino UNOs from our Arduino Library for Proteus V3.0 run the same HEX file:
- NODE 1 - SIMPLE (on-sheet panel): UNO 1 (ARD1) with OLED1 (white, Simple, address 0x3C) and Serial Monitor U1.
- NODE 2 - ADVANCE (pop-up panel): UNO 2 (ARD2) with OLED2 (yellow-blue, Advance, address 0x3D) and Serial Monitor U2.
Wiring
| OLED pin | Arduino UNO | Why |
|---|---|---|
| GND | GND | Ground |
| VCC | 5V | Supply |
| SCL | A5 (SCL) | I2C clock |
| SDA | A4 (SDA) | I2C data |
| Serial Monitor RXD / TXD / GND | D1 / D0 / ground | Shows what the Arduino prints, sends what you type |
On an Arduino UNO, SDA is A4 and SCL is A5 (see our Introduction to Arduino UNO). In the demo, every pin goes through a short wire to a net label: in Proteus, terminals with the same name are connected. SDA_1 at OLED1 meets SDA_1 at A4 of UNO 1, and node 2 has its own SDA_2 and SCL_2, so the two buses stay apart. VCC and GND are Proteus's power rails.
The Arduino Code
The demo sketch, OLED_SSD1306_Demo V1.0, uses the Adafruit SSD1306 library (version 2.5.17) with the Adafruit GFX library (version 1.12.6) and Arduino's Wire library. It starts with the display object: 128 x 64 pixels on the Wire bus, and -1 because the 4-pin module has no reset pin:
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
Adafruit_SSD1306 display(128, 64, &Wire, -1); // width, height, the I2C bus, no reset pin
The sketch finds the address itself. An I2C address check is an empty write: if a device answers with ACK, Wire.endTransmission() returns 0 (our I2C LCD 16x2 Library for Proteus V1.0 article explains I2C addresses and the ACK in more detail):
bool answers(byte a) { // an address check: does a device ACK?
Wire.beginTransmission(a);
return Wire.endTransmission() == 0;
}
setup() tries 0x3C, then 0x3D, and starts the display with what it found. SSD1306_SWITCHCAPVCC is the important word here: it tells the library to switch the charge pump on. begin() returns false only when the Arduino has no room for the 1 KB picture buffer:
void setup() {
Serial.begin(9600);
Serial.println(F("OLED SSD1306 V1.0 demo - The Engineering Projects"));
Wire.begin();
if (answers(0x3C)) { address = 0x3C; found = true; }
else if (answers(0x3D)) { address = 0x3D; found = true; }
if (!display.begin(SSD1306_SWITCHCAPVCC, address)) {
Serial.println(F("SSD1306: not enough RAM for the 1 KB buffer"));
for (;;);
}
Everything the sketch draws goes into the buffer first. Only display() sends it to the OLED, so the demo wraps it and counts the frames, the "F" number on the glass:
void show() { display.display(); frames++; }
The header uses the small font (size 1, 6 x 8 pixels per character) in rows 0 to 15, the yellow zone of a yellow-blue OLED. The frame count is right-aligned: each character is 6 pixels wide, so the text starts at 128 minus 6 times its length:
void header() { // rows 0-15: the yellow zone of a yellow-blue OLED
unsigned long s = millis() / 1000;
display.setTextSize(1);
display.setCursor(0, 0);
display.print(F("TEP OLED"));
snprintf_P(buf, sizeof buf, PSTR("F%lu"), frames % 100000); // the frames sent, right-aligned
display.setCursor(128 - 6 * strlen(buf), 8);
display.print(buf);
The moving sine wave is one pixel per column. A table of 32 values in flash holds one period, and the phase moves with millis(), so the wave slides along every time the dashboard is redrawn:
int phase = (millis() / 125) % 32;
for (int x = 0; x < 128; x++) {
int y = 52 - (int8_t)pgm_read_byte(&wave[(x / 2 + phase) % 32]);
display.drawPixel(x, y, SSD1306_WHITE);
}
display.drawFastHLine(0, 52, 128, SSD1306_WHITE);
The SHAPES screen shows the Adafruit GFX basics, the same shapes you saw on the white glass above:
display.drawRect(0, 18, 30, 22, SSD1306_WHITE);
display.fillRect(4, 22, 22, 14, SSD1306_WHITE);
display.drawCircle(48, 34, 14, SSD1306_WHITE);
display.fillCircle(48, 34, 6, SSD1306_WHITE);
display.drawTriangle(70, 50, 84, 20, 98, 50, SSD1306_WHITE);
display.drawRoundRect(104, 18, 24, 32, 6, SSD1306_WHITE);
The LOGO screen draws our 40 x 32 logo from a byte array in flash with one call:
display.drawBitmap(4, 16, logo, 40, 32, SSD1306_WHITE);
Some commands go straight to the controller instead of the picture. CONTRAST sends the SSD1306's contrast command and its value:
display.ssd1306_command(SSD1306_SETCONTRAST);
display.ssd1306_command(contrast);
SCROLL uses the controller's own scrolling: one command, and the SSD1306 moves the picture over pages 0 to 7 by itself, with no data from the Arduino:
if (!strcasecmp_P(arg, PSTR("LEFT"))) { display.startscrollleft(0x00, 0x07); scroll = S_LEFT; }
else if (!strcasecmp_P(arg, PSTR("RIGHT"))) { display.startscrollright(0x00, 0x07); scroll = S_RIGHT; }
The loop reads the Serial Monitor and redraws the dashboard twice a second, without delay(). It does not redraw while the controller scrolls, because a new frame would put the picture back in place:
unsigned long now = millis();
if (screen == DASH && scroll == S_OFF && now - lastDraw >= 500) { // twice a second (not while it scrolls)
lastDraw = now;
drawDash();
}
The Adafruit library needs 1 KB of the UNO's 2 KB RAM for its buffer, so the demo keeps all its texts in flash with F() and PSTR(). To change the sketch, install the libraries from the Library Manager, export a new HEX file and load it into both UNOs; see How to get the HEX file from Arduino. New to Arduino? Start with our Arduino Tutorial for Beginners. Want something to show? Pair the OLED with our BME280 Sensor Library for Proteus V1.1 for a small weather station.
Serial Monitor Commands
| Command | What it does |
|---|---|
| HELP | The command list |
| STATUS | The address, the screen, the text, the lower part, contrast, inverse, scroll, display and frames |
| SCAN | Every I2C device on the bus (0x08 - 0x77) |
| TEXT <text> | The big line (10 characters; a longer text is cut) |
| SIZE <1-3> | Its text size |
| GRAPH / BAR <0-100> | The moving sine wave / a bar instead of the wave |
| LOGO, SHAPES, DASH | The TEP logo, the GFX shapes, the dashboard again |
| INVERT ON / OFF | Inverse: the controller swaps lit and dark pixels, the RAM stays |
| CONTRAST <0-255> | The brightness (Adafruit's default is 207) |
| DISPLAY ON / OFF | The panel on or off (the RAM is kept) |
| SCROLL LEFT / RIGHT / DIAG / STOP | The controller's own scrolling |
| CLEAR | An empty screen (DASH brings the dashboard back) |
How to Run the Demo
- Press Run. Both Serial Monitors (9600 baud) and OLED2's pop-up open; move them aside, as they open over the schematic. The OLEDs show the TEP logo for a second, then the dashboard.
- Watch the sine wave move and the frame count grow by two every second.
- On U1, type TEXT with your own text, BAR 37, INVERT ON and CONTRAST 20; on U2, SCAN, SHAPES and SCROLL DIAG.
- Click the ADDRESS selector of OLED1 and watch the panel; click it again.
- On OLED2's pop-up, turn on the pixel grid, look at the Bus log, and run the four tools.
OLED SSD1306 Proteus Simulation Results
The Serial Monitor in Its Simple Interface
Both monitors in this demo run in the Simple interface of our TEP Serial Monitor, as in every monitor screenshot here: only the log, the send box and one slim toolbar row stay visible.
- Switch to it: right-click the log and choose Simple interface, or press Ctrl+Shift+U.
- Back to the full view: click the corner button, press Esc or Ctrl+Shift+U, or right-click and choose Full interface.
- Your choice is remembered.
Type a command in the send box and press Enter. Read all about the monitor in TEP Serial Monitor Advance for Proteus.
U1: HELP and STATUS
OLED SSD1306 V1.0 demo - The Engineering Projects
OLED found at 0x3C
Type HELP for the commands
HELP
Commands:
TEXT <text> the big line (10 characters)
SIZE <1-3> its text size
GRAPH the moving sine wave
BAR <0-100> a bar instead of the wave
LOGO SHAPES DASH (the dashboard)
INVERT ON|OFF CONTRAST <0-255> DISPLAY ON|OFF
SCROLL LEFT|RIGHT|DIAG|STOP
CLEAR SCAN STATUS HELP
STATUS
OLED 128x64 at 0x3C, found
Screen: dashboard
Text: Hello, TEP (size 2)
Lower part: sine wave
Contrast 207, inverse off, scroll off, display on
Frames sent: 429
The second line is the address search at work: the sketch found the OLED at 0x3C by itself. 429 frames after about three and a half minutes is the two frames per second the Speed tool measured.
U2: SCAN, SHAPES and SCROLL RIGHT
OLED SSD1306 V1.0 demo - The Engineering Projects
OLED found at 0x3D
Type HELP for the commands
SCAN
I2C devices: 0x3D
SHAPES
OK - shapes
SCROLL RIGHT
OK - scroll right
SCAN finds one device, the OLED at 0x3D: each UNO has its own I2C bus in this demo. After SCROLL RIGHT the shapes slide to the right and come back on the left, endlessly, while the Arduino sends nothing at all; the Bus log above shows the two commands that started it.
Troubleshooting
- The glass stays black and the PWR LED is off: no power. Check VCC and GND (the panel says NO POWER with the voltage; the module needs 3.3 to 5 V).
- WAITING FOR I2C: nothing reached this address. Check SDA to A4 and SCL to A5 (swapped lines look the same: nothing works), and that the sketch runs.
- WRONG ADDRESS: the sketch writes to another address. Change the address in
display.begin(), or the ADDRESS property or the selector; the I2C bus tool shows both. - DARK - THE CHARGE PUMP IS OFF: the sketch turned the display on without 8D 14. Use
SSD1306_SWITCHCAPVCC, not SSD1306_EXTERNALVCC. - Random dots: the RAM before the first frame. Call
display.display()after drawing; nothing reaches the OLED before it. - Every second line, or a stretched picture: 128x32 settings on this 128x64 glass. Use
Adafruit_SSD1306 display(128, 64, ...). - Mirrored or upside down: the remap commands (A0 / A1, C0 / C8);
display.setRotation(2)turns the picture in software instead. - Slow or jerky animation: each
display()sends 1 KB, about a tenth of a second at 100 kHz. Check the Speed tool. - The part is not simulated, or no monitor window: TEPOLED.DLL or TEPSERIALMON.DLL is missing from MODELS and the project folder; click OPEN MONITOR on a closed monitor.
- The Advance pop-up does not appear: click OPEN PANEL on the board, or turn "Open the panel at Run" back on in Settings.
Things to Know Before Using a Real OLED SSD1306
The demo sketch uses only Wire, the Adafruit libraries and Serial, so it runs on a real UNO and 0.96" OLED too. Keep in mind:
- Check the pin order on YOUR module: most are GND, VCC, SCL, SDA, but some are VCC, GND, SCL, SDA. Swapped supply pins can kill the module.
- Find the address: run an I2C scanner (or the demo's SCAN). Most modules are 0x3C; the back says 0x78 (= 0x3C) or 0x7A (= 0x3D).
- Mind the RAM: the Adafruit library needs a 1 KB buffer, half of an UNO's RAM. Keep texts in flash with
F("...");begin()returns false when there is no room. - 1.3" look-alikes: many 1.3" OLEDs carry an SH1106 chip, not an SSD1306. With SSD1306 code they show a garbled or shifted picture and need another library (for example Adafruit SH110X or U8g2).
- Tell the library the size: a 128x32 module needs
display(128, 32, ...), a 128x64 onedisplay(128, 64, ...). - Supply and pull-ups: the module has its own regulator and SDA / SCL pull-ups, so it runs from 3.3 V and from 5 V. 400 kHz I2C is fine.
- Burn-in: OLED pixels age. A static picture shown for months can burn in, so turn the display off or dim it when nobody looks.
Limitations of the Simulation
- Not modelled: reading from the OLED (libraries never read it; a read is not acknowledged), the fade / blink and zoom extras of later datasheet revisions (logged as unknown and ignored), the SPI and parallel interfaces of other modules, an external VCC, the effect of the pre-charge and VCOMH settings on the picture (they are stored and shown), I2C timing violations, the current draw and pixel ageing.
- The board is drawn at twice the real size so the pixels stay readable; the LEDs and the address selector are TEP additions.
- The model is our own implementation, written from the SSD1306 datasheet. SSD1306 is a Solomon Systech part; this library is not made or endorsed by Solomon Systech, Adafruit or Labcenter.
- Licences: the demo sketch uses Adafruit SSD1306 2.5.17, Adafruit GFX 1.12.6 (BSD licence) and Arduino's Wire library. The HEX file contains them and the Arduino AVR core 1.8.6, which includes LGPL-licensed code; its source code is on GitHub. The README holds the Adafruit licence texts.
- Tested in Proteus 8.5 in October 2026 (the demo as shipped, all six devices, the dashboard, the Serial commands, scrolling, the address selector, the panel, the pop-up with Freeze and the pixel grid, the four tools and every screenshot here; about 0.62 x real time with the pop-up and both monitors open) and with automatic PC checks, 0 failed: 46 with the real demo sketch and the unmodified Adafruit libraries talking bit by bit to the model, where the glass equals Adafruit's own buffer after every frame, 56 on the SSD1306 controller, and the demo project read back from disk. Proteus 7 is not supported.
So, that was all about the OLED SSD1306 Library for Proteus V1.0. I hope the pixel-exact glass, the address selector and the test tools make the little OLED much easier to understand, so your display works the first time you wire a real one. If you use the OLED SSD1306 Library for Proteus in a project, please share your feedback in the comments or in our forum, and if you have any questions, ask in the comments and I will help you out. Till the next tutorial, take care and have fun!