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:

Figure: The OLED SSD1306 demo in Proteus 8.5: NODE 2 (yellow-blue, Advance, 0x3D), its pop-up Display page with "TEP OLED 0x3D", the up time, "Hello, TEP" and the sine wave, and U2 answering SCAN and STATUS.

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

The 0.96 inch I2C OLED in numbers
FeatureValue
Display0.96", 128 x 64 pixels, one colour (white or blue) or two colours (yellow rows 0 - 15, blue below)
ControllerSSD1306 with 1 KB display RAM (GDDRAM)
BoardAbout 27 x 28 mm, four mounting holes
InterfaceI2C: SDA and SCL (A4 and A5 on an Arduino UNO), plus GND and VCC
Address0x3C (most modules) or 0x3D, set by a resistor on the back (marked 0x78 / 0x7A)
Supply3.3 V or 5 V on VCC (the module has its own regulator)
Panel voltageMade by the internal charge pump; the sketch must switch it on
I2C speedUp to 400 kHz; a full picture is 1024 bytes, about 37 ms at 400 kHz
Arduino librariesAdafruit SSD1306 + Adafruit GFX: Adafruit_SSD1306 display(128, 64, &Wire, -1)

Terms You Will See in This Article

OLED terms
TermMeaning
I2CA two-wire bus: SDA carries the data, SCL the clock; many devices share it, each with its own address
AddressThe 7-bit number a device answers to, written in hex (0x3C)
GDDRAMThe SSD1306's display memory: 128 columns x 8 pages, 1024 bytes
PageA band of 8 pixel rows; one byte of a page is 8 pixels on top of each other
Control byteThe first byte after the address: 0x00 = commands follow, 0x40 = picture data follows
Charge pumpThe circuit inside the SSD1306 that makes the high panel voltage from 3.3 V or 5 V
FrameOne 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:

The six devices (search "OLED12864" in Pick Devices; category Optoelectronics > Graphical LCDs)
DeviceName in Pick DevicesGlassExtras
OLED12864WHITETEPOLED SSD1306 128x64 V1.0 White - SimpleWhiteSTATUS panel on the sheet
OLED12864WHITEADVTEPOLED SSD1306 128x64 V1.0 White - AdvanceWhitePop-up window and tools
OLED12864BLUETEPOLED SSD1306 128x64 V1.0 Blue - SimpleBlueSTATUS panel on the sheet
OLED12864BLUEADVTEPOLED SSD1306 128x64 V1.0 Blue - AdvanceBluePop-up window and tools
OLED12864YELLOWBLUETEPOLED SSD1306 128x64 V1.0 Yellow-blue - SimpleYellow rows 0 - 15, blue belowSTATUS panel on the sheet
OLED12864YELLOWBLUEADVTEPOLED SSD1306 128x64 V1.0 Yellow-blue - AdvanceYellow rows 0 - 15, blue belowPop-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.LIB and our Arduino UNO library ArduinoV3TEP.LIB / ArduinoV3TEP.IDX.
  • Proteus Model Files: TEPOLED.DLL and TEPSERIALMON.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

  1. Close Proteus and extract the whole zip file to a normal folder, for example your Desktop.
  2. 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 PCs C:\ProgramData\Labcenter Electronics\Proteus 8 Professional\LIBRARY; use the folder that already holds Proteus's own .LIB files).
  3. Copy TEPOLED.DLL and TEPSERIALMON.DLL from Proteus Model Files into the MODELS folder.
  4. Delete TEPOLED12864.IDX from the LIBRARY folder if there is one; Proteus makes a new index.
  5. 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:

Figure: OLED1 (white, Simple) at 0x3C: "TEP OLED 0x3C", "Up 00:01:31", "F183", "Hello, TEP" and the sine wave; READY - 128x64, I2C 0x3C, DISPLAY ON; I2C 0x3C 400k, FRAME 36.8 ms.

Pinout

The 4-pin header, left to right
PinNameWhat it doesDemo connection
1GNDGroundGND (net label)
2VCCSupply, 3.3 V or 5 VVCC (net label, the 5 V rail)
3SCLI2C clockArduino A5, through the label SCL_1
4SDAI2C data (open drain, with the module's pull-up)Arduino A4, through the label SDA_1

The Board Up Close

Figure: The board of OLED1: the ADDRESS selector (0x3C), the four pins, the PWR LED and the I2C LED flashing during a frame, the glass, SIMPLE and PANEL: SHOWN.

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

Board indicators (the same on all six devices)
IndicatorWhat it showsOn the real module?
The pixelsThe picture in the controller's RAM, at the contrast's brightnessYes
PWR LED (red)Lit while the module has powerTEP addition
I2C LED (green)Flashes on every I2C transaction to this addressTEP addition
ADDRESS selector0x3C or 0x3D, clickable while the simulation runsTEP addition (a resistor on the back)
SIMPLE / ADVANCE badge, PANEL / OPEN PANELWhich device this is, and the panel buttonTEP addition

Component Properties

OLED properties (double-click the OLED > Edit Properties)
PropertyMeaningDefault
PANELSimple devices only: the STATUS panel at the start, OPEN or CLOSEDOPEN
ADDRESSThe I2C address, 0x3C or 0x3D; the selector on the board follows it0x3C

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:

Figure: Blue (LOGO: the TEP logo drawn with drawBitmap), white (SHAPES: rectangles, circles, a triangle and lines from Adafruit GFX) and yellow-blue (the dashboard: the header in the yellow rows).

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:

Figure: The selector on 0x3D: the up time stops at 00:04:39, the banner says WRONG ADDRESS and the I2C readout 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):

The OLED STATUS panel
PartWhat it shows
BannerREADY (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 PUMPWhether the display and the charge pump are on
CONTRASTThe contrast value, 0 to 255 (Adafruit's default is 207)
ADDRESSING, SCROLLThe addressing mode (HORIZONTAL, VERTICAL or PAGE) and the scrolling
VCCThe supply voltage
I2CThis OLED's address and the measured clock, or the address the sketch uses instead
FRAMEHow long one whole picture takes on the bus
WRITES, LOSTBytes the controller took, and unknown commands or bytes sent without power
BusThe 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:

Figure: OLED2 (yellow-blue, Advance) at 0x3D: the header in the yellow rows, "Hello, TEP" and the wave in blue; the I2C LED flashes during a frame.

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

Figure: The Display page: the glass dot by dot, CONTRAST 207, HORIZONTAL, UPRIGHT, 64 ROWS, ALTERNATIVE; panel refresh 87.6 Hz, the last frame 36.8 ms.
  • 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():

Figure: PIXEL GRID On: "TEP OLED" starts at x 0, y 0, the up time at y 8, "Hello, TEP" at y 18, and the wave's middle line is at y 52.

The Bus Log Page

Figure: The Bus log after SCROLL RIGHT: SCROLL SETUP RIGHT (26 00 00 00 ..), pages 0..7, every 5 frames, then SCROLL ON; below them, the last frame in runs of 31 bytes.

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?

Figure: Init check: every row green except "RAM before display on" (amber); the start-up as the sketch sent it, from DISPLAY OFF to DISPLAY ON.
Init check (OLED2)
CheckResult in the picture
First commandDISPLAY OFF (AE), as the datasheet starts
Charge pumpON (8D 14)
Multiplex ratio64 rows (A8 3F)
COM pinsALTERNATIVE (DA 12), right for 128x64
OrientationUPRIGHT (A1 + C8)
Addressing modeHORIZONTAL (Adafruit_SSD1306 sends whole frames this way)
Contrast207 (0xCF)
Display onYes (AF), 68.4 ms after power-on
RAM before display onAmber: not written, so the random power-on RAM shows until the first frame
VerdictInitialised 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?

Figure: I2C bus: 0x3D (SA0 high, the address resistor 0x7A); the sketch writes to 0x3D; up to 31 data bytes after the 0x40 control byte; SCL 400 kHz.

Good for: "a dark OLED - the wrong address, SDA / SCL swapped, the clock and Wire's 32-byte buffer." The tool shows:

I2C bus (OLED2)
RowResult in the picture
This OLED0x3D - SA0 high (the address resistor: 0x7A)
The sketch writes to0x3D - this OLED (14035 writes since power-on)
Measuring for189.351 s of simulation: 14036 transactions to 0x3D, 404431 bytes
Commands / data1149 command transactions (2300 bytes), 12886 data transactions (388096 bytes)
Data per transactionUp to 31 bytes after the 0x40 control byte (Arduino's Wire buffer is 32 bytes)
Address checks1 to this address, 1 to other addresses (a scanner / auto-detect)
SCL clock400 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?

Figure: Speed: 36.77 ms per frame, at best 27 frames per second; the demo sends 2 a second, so the bus is only 5.4 % busy.

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:

Speed (OLED2, the demo sketch with Adafruit SSD1306)
ResultValue
Time per frame36.77 ms on the bus (min 36.75 ms, max 36.85 ms)
Best possible27 frames per second if the sketch only sent frames
The sketch sends2.0 frames per second (one every 501 ms)
Bus busy5.4 % of the time
SCL clock400 kHz: about 22.5 us per byte (9 clocks + the gaps)
Panel refresh87.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?

Figure: Pixels: x 0, y 25 is page 3, column 0, bit 1; the byte holds 0xFF (the left stem of the "H"); Adafruit buffer[384]; display.drawPixel(0, 25, SSD1306_WHITE);

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.
Figure: The whole circuit running, without the pop-up windows: OLED1 shows "TEP OLED 0x3C", OLED2 "TEP OLED 0x3D", both "Up 00:00:10", "F21", "Hello, TEP" and the wave.

Wiring

Demo wiring (the same on both UNOs)
OLED pinArduino UNOWhy
GNDGNDGround
VCC5VSupply
SCLA5 (SCL)I2C clock
SDAA4 (SDA)I2C data
Serial Monitor RXD / TXD / GNDD1 / D0 / groundShows 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.

Figure: Node 1: the OLED's GND, VCC, SCL_1 and SDA_1 labels, and SDA_1 / SCL_1 at A4 and A5 of ARD1.

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

Commands of the demo sketch, upper or lower case (quick buttons in the full interface: U1 STATUS, TEXT Hi there!, BAR 75, LOGO; U2 STATUS, SHAPES, SCROLL RIGHT, DASH)
CommandWhat it does
HELPThe command list
STATUSThe address, the screen, the text, the lower part, contrast, inverse, scroll, display and frames
SCANEvery 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, DASHThe TEP logo, the GFX shapes, the dashboard again
INVERT ON / OFFInverse: the controller swaps lit and dark pixels, the RAM stays
CONTRAST <0-255>The brightness (Adafruit's default is 207)
DISPLAY ON / OFFThe panel on or off (the RAM is kept)
SCROLL LEFT / RIGHT / DIAG / STOPThe controller's own scrolling
CLEARAn empty screen (DASH brings the dashboard back)

How to Run the Demo

  1. 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.
  2. Watch the sine wave move and the frame count grow by two every second.
  3. On U1, type TEXT with your own text, BAR 37, INVERT ON and CONTRAST 20; on U2, SCAN, SHAPES and SCROLL DIAG.
  4. Click the ADDRESS selector of OLED1 and watch the panel; click it again.
  5. 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

Figure: U1 in the Simple interface: "OLED found at 0x3C", the HELP list and STATUS with the screen, text, contrast and frames.
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

Figure: U2 in the Simple interface: "OLED found at 0x3D", SCAN ("I2C devices: 0x3D"), 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 one display(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!