Adventures with small and cheap OLED displays
David R. Brooks, © 2020


      Why is this an adventure? Because in contrast to my usual approach of buying only well-supported devices from reputable U.S. vendors, this project breaks that pattern. Why? Because I wanted a small and inexpensive monochrome organic LED (OLED) display – see HERE – to display real time data. I also use LCDs for this job, but they are relatively large and power-hungry, which makes them much less desirable for use in battery-powered projects.
      Adafruit sells a 128x64 pixel 0.96" (diagonal) display, but it costs $20. You can draw graphics on this display (and others, too) using Adafruit's very nice GFX graphics library, but that's not necessary for this simple text-based project. What do do?
      There are lots of off-shore (invariably Chinese) sources for these small OLED displays. If that kind of sourcing doesn't bother you politically and you don't mind dealing with delivery issues, this is an option. (The order for displays I used for this project was so late on delivery that Amazon assumed it was lost and issued a credit refund to my account, even though the order was allegedly shipped from a U.S. location. A similar order from another Chinese supplier placed in March had still not arrived as of the end of July, so CAVEAT  EMPTOR!)
      Here's a breadboard layout to test the display. I used a SparkFun RedBoard UNO compatible just because I had sitting on my desk. The display is an IZOKEE 0.96" I2C 12864 128x64 pixel OLED bought from KeeYees-US for $13 for three displays – a very sigificant price improvement over domestic equivalents! The displays already have breadboard-ready 4-pin headers soldered on and they come in a cute little plastic snap-lid case. Text displayed on this device is very bright and sharp white on a black background. Note that it's powered through the UNO's 3.3 V pin, but 5 V is also OK. Finally, frustrating experience has taught me that although these small OLEDs from various sources all LOOK the same, there are differences that must be accounted for in your code.
      SSD1306 displays are generic devices and there are many sources of libraries for using them with either SPI or I2C communication protocols. You can download the library I used from the Arduino IDE's library manager:



      This U8g2 library also has some graphics capabilities but I didn't need them for this simple project. I always use I2C interfaces when they're available because they're very simple to connect to Arduinos. To test this device I used a sketch available HERE. I stripped out everything that the code actually didn't need, like the commented-out lines for using an SPI interface and definitions for other OLED displays.
      In its OLED display documentation, adafruit.com says "OLED displays are made of hundreds of...OLEDs! That means each pixel is a little organic LED, and if it's kept on for over 1000 hours it'll start to dim. If you want to keep the display uniformly bright, please turn off the display (set the pixels off) when it isn't needed to keep them from dimming." Because of this warning I added a couple of lines of code that clear the display so it blinks on and off at one-second intervals just to test clearing the screen.
      It's interesting to note that you don't have to specify the I2C hardware address anywhere in the code. Either the address for these devices is always the same or the code automatically looks for the address. For more about I2C addresses, see the July 29 posting HERE. In fact, it's this very device that has been the subject of online complaints that the I2C address was incorrectly printed on the PC board, which is irrelevant if the software library doesn't need you to supply the address. When I ran a sketch to find the address, it was 0x3C and not what was printed on the PC board. Go figure...
// https://github.com/Seeed-Studio/Seeed_Learning_Space/tree/master/
// Grove%20-%20OLED%20Display%200.96''(SSD1315)V1.0
#include "U8g2lib.h"
#include "Wire.h"
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
void setup(void) {
  u8g2.begin();
}
void loop(void) {
  u8g2.clearBuffer();  
  //Reads right side up with pins on left. Otherwise...
  //u8g2.setFlipMode(1); //reads with pins on right.
  // clear the internal memory
  u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font
  u8g2.drawStr(10,10,"Hello World!"); // write to internal memory
  u8g2.sendBuffer();                  // transfer to the display
  delay(1000); 
  u8g2.clearDisplay();
  delay(1000);   
}
      When I compile this code, this warning message appears:

The problem lies not with the code size, but with the limited dynamic memory space available for local variables. I don't know why this occurs, but it may have to do with space required to store fonts. The small and rather blocky default font is perfectly readable on this display and I haven't bothered to try using whatever other fonts may already be available in the library. In any case, adding other code required to access data from sensors may cause problems. Let's see...
      Here's code to display data from a DHT22 T/RH sensor – it uses 36% of program storage space and leaves 469 bytes remaining for local variables (out of a total of 2048 bytes). I didn't bother to clear the display between sensor reads (DHTs take a couple of seconds to stabilize between reads), but this would be a good idea for a "permanently" running project.
/* U8g2_DHT22.ino,D. Brooks, August 2020
  https://github.com/Seeed-Studio/Seeed_Learning_Space/tree/master/
    Grove%20-%20OLED%20Display%200.96''(SSD1315)V1.0
  See U8g2.ino for OLED use.
*/
#include "U8g2lib.h"
#include "Wire.h"
#include "DHT.h"
#define DHTTYPE DHT22
#define DHTPIN 8
DHT dht(DHTPIN, DHTTYPE);
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
void setup(void) {
  u8g2.begin(); dht.begin();
}
void loop(void) {
  u8g2.clearBuffer();  
  //Reads right side up with pins on left. Otherwise...
  //u8g2.setFlipMode(1); //reads with pins on right.
  // clear the internal memory
  delay(3000);
  u8g2.setFont(u8g2_font_ncenB08_tr); 
  u8g2.drawStr(5,10,"T (C): "); 
  u8g2.setCursor(70,10); u8g2.print(dht.readTemperature(),1);
  u8g2.drawStr(5,25,"RH (%): ");
  u8g2.setCursor(70,25); u8g2.print(dht.readHumidity(),0);
  u8g2.sendBuffer();                  
}
Here's the breadboard layout. The display is actually sharper and easier to read than it appears in this image.


Code Update:
      The code examples given above work for the IZOKEE 0.96" OLED but not for the seemingly identical SeeedStudio device. Here's "Hello world" code that does work with the SeeedStudio 0.96" OLED on an UNO, as sent to me in response to a request to the SeeedStudio support team. The critical step is using the appropriate "constructor" statement – U8G2_SSD1306...

/* U8g2_seeedstudio.ino, August 2020
  Works with SeeedStudio 0.96" OLED. Code sent by SeeedStudio support team.
*/
#include "Arduino.h"
#include "U8g2lib.h"
#ifdef U8X8_HAVE_HW_SPI
#include "SPI.h"
#endif
#ifdef U8X8_HAVE_HW_I2C
#include "Wire.h"
#endif
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);
void setup(void) {
  u8g2.begin();
}
void loop(void) {
  u8g2.clearBuffer();                 // clear the internal memory
  u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font
  u8g2.drawStr(0,10,"Hello World!");   // write something to the internal memory
  u8g2.sendBuffer();                   // transfer internal memory to the display
  delay(1000);
}